woodpecker/pipeline/frontend/yaml/linter/linter.go

177 lines
5.1 KiB
Go
Raw Normal View History

// Copyright 2023 Woodpecker Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2017-03-05 07:56:08 +00:00
package linter
import (
"fmt"
"go.uber.org/multierr"
2017-03-05 07:56:08 +00:00
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/linter/schema"
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/types"
2017-07-21 21:52:52 +00:00
)
2017-03-05 07:56:08 +00:00
// A Linter lints a pipeline configuration.
type Linter struct {
trusted bool
}
// New creates a new Linter with options.
func New(opts ...Option) *Linter {
linter := new(Linter)
for _, opt := range opts {
opt(linter)
}
return linter
}
// Lint lints the configuration.
func (l *Linter) Lint(rawConfig string, c *types.Workflow) error {
var linterErr error
if len(c.Steps.ContainerList) == 0 {
linterErr = multierr.Append(linterErr, newLinterError("Invalid or missing steps section", "steps", false))
}
if err := l.lintContainers(c.Clone.ContainerList); err != nil {
linterErr = multierr.Append(linterErr, err)
2017-07-21 21:52:52 +00:00
}
if err := l.lintContainers(c.Steps.ContainerList); err != nil {
linterErr = multierr.Append(linterErr, err)
2017-07-21 21:52:52 +00:00
}
if err := l.lintContainers(c.Services.ContainerList); err != nil {
linterErr = multierr.Append(linterErr, err)
2017-07-21 21:52:52 +00:00
}
if err := l.lintSchema(rawConfig); err != nil {
linterErr = multierr.Append(linterErr, err)
}
if err := l.lintDeprecations(c); err != nil {
linterErr = multierr.Append(linterErr, err)
}
if err := l.lintBadHabits(c); err != nil {
linterErr = multierr.Append(linterErr, err)
}
return linterErr
2017-07-21 21:52:52 +00:00
}
2017-03-05 07:56:08 +00:00
func (l *Linter) lintContainers(containers []*types.Container) error {
var linterErr error
2017-03-05 07:56:08 +00:00
for _, container := range containers {
if err := l.lintImage(container); err != nil {
linterErr = multierr.Append(linterErr, err)
2017-03-05 07:56:08 +00:00
}
if !l.trusted {
2017-03-05 07:56:08 +00:00
if err := l.lintTrusted(container); err != nil {
linterErr = multierr.Append(linterErr, err)
2017-03-05 07:56:08 +00:00
}
}
2017-07-21 21:52:52 +00:00
if err := l.lintCommands(container); err != nil {
linterErr = multierr.Append(linterErr, err)
2017-07-21 21:52:52 +00:00
}
2017-03-05 07:56:08 +00:00
}
return linterErr
2017-03-05 07:56:08 +00:00
}
func (l *Linter) lintImage(c *types.Container) error {
2017-03-05 07:56:08 +00:00
if len(c.Image) == 0 {
return newLinterError("Invalid or missing image", fmt.Sprintf("steps.%s", c.Name), false)
2017-03-05 07:56:08 +00:00
}
return nil
}
func (l *Linter) lintCommands(c *types.Container) error {
2017-07-21 21:52:52 +00:00
if len(c.Commands) == 0 {
return nil
}
if len(c.Settings) != 0 {
2017-07-21 21:52:52 +00:00
var keys []string
for key := range c.Settings {
2017-07-21 21:52:52 +00:00
keys = append(keys, key)
}
return newLinterError(fmt.Sprintf("Cannot configure both commands and custom attributes %v", keys), fmt.Sprintf("steps.%s", c.Name), false)
2017-07-21 21:52:52 +00:00
}
2017-03-05 07:56:08 +00:00
return nil
}
func (l *Linter) lintTrusted(c *types.Container) error {
yamlPath := fmt.Sprintf("steps.%s", c.Name)
2017-03-05 07:56:08 +00:00
if c.Privileged {
return newLinterError("Insufficient privileges to use privileged mode", yamlPath, false)
2017-03-05 07:56:08 +00:00
}
if c.ShmSize != 0 {
return newLinterError("Insufficient privileges to override shm_size", yamlPath, false)
2017-03-05 07:56:08 +00:00
}
if len(c.DNS) != 0 {
return newLinterError("Insufficient privileges to use custom dns", yamlPath, false)
2017-03-05 07:56:08 +00:00
}
if len(c.DNSSearch) != 0 {
return newLinterError("Insufficient privileges to use dns_search", yamlPath, false)
2017-03-05 07:56:08 +00:00
}
if len(c.Devices) != 0 {
return newLinterError("Insufficient privileges to use devices", yamlPath, false)
2017-03-05 07:56:08 +00:00
}
if len(c.ExtraHosts) != 0 {
return newLinterError("Insufficient privileges to use extra_hosts", yamlPath, false)
2017-03-05 07:56:08 +00:00
}
if len(c.NetworkMode) != 0 {
return newLinterError("Insufficient privileges to use network_mode", yamlPath, false)
2017-03-05 07:56:08 +00:00
}
2017-09-08 00:43:33 +00:00
if len(c.IpcMode) != 0 {
return newLinterError("Insufficient privileges to use ipc_mode", yamlPath, false)
2017-09-08 00:43:33 +00:00
}
2017-11-17 22:49:01 +00:00
if len(c.Sysctls) != 0 {
return newLinterError("Insufficient privileges to use sysctls", yamlPath, false)
2017-11-17 22:49:01 +00:00
}
2017-03-05 07:56:08 +00:00
if c.Networks.Networks != nil && len(c.Networks.Networks) != 0 {
return newLinterError("Insufficient privileges to use networks", yamlPath, false)
2017-03-05 07:56:08 +00:00
}
if c.Volumes.Volumes != nil && len(c.Volumes.Volumes) != 0 {
return newLinterError("Insufficient privileges to use volumes", yamlPath, false)
2017-03-05 07:56:08 +00:00
}
2017-09-08 00:43:33 +00:00
if len(c.Tmpfs) != 0 {
return newLinterError("Insufficient privileges to use tmpfs", yamlPath, false)
}
return nil
}
func (l *Linter) lintSchema(rawConfig string) error {
var linterErr error
schemaErrors, err := schema.LintString(rawConfig)
if err != nil {
for _, schemaError := range schemaErrors {
linterErr = multierr.Append(linterErr, newLinterError(
schemaError.Description(),
schemaError.Field(),
true, // TODO: let pipelines fail if the schema is invalid
))
}
2017-09-08 00:43:33 +00:00
}
return linterErr
}
func (l *Linter) lintDeprecations(_ *types.Workflow) error {
// TODO: add deprecation warnings
return nil
}
func (l *Linter) lintBadHabits(_ *types.Workflow) error {
// TODO: add bad habit warnings
2017-03-05 07:56:08 +00:00
return nil
}