mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-25 11:21:02 +00:00
Disalow to set arbitrary environments for plugins (#3909)
This commit is contained in:
parent
904d8da51a
commit
8aa3e5ec82
10 changed files with 225 additions and 16 deletions
|
@ -100,5 +100,5 @@ func toOpenApi3(input, output string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return os.WriteFile(output, data, 0644)
|
return os.WriteFile(output, data, 0o644)
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ func (l *Linter) lintContainers(config *WorkflowConfig, area string) error {
|
||||||
linterErr = multierr.Append(linterErr, err)
|
linterErr = multierr.Append(linterErr, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := l.lintCommands(config, container, area); err != nil {
|
if err := l.lintSettings(config, container, area); err != nil {
|
||||||
linterErr = multierr.Append(linterErr, err)
|
linterErr = multierr.Append(linterErr, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,16 +132,18 @@ func (l *Linter) lintImage(config *WorkflowConfig, c *types.Container, area stri
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Linter) lintCommands(config *WorkflowConfig, c *types.Container, field string) error {
|
func (l *Linter) lintSettings(config *WorkflowConfig, c *types.Container, field string) error {
|
||||||
if len(c.Commands) == 0 {
|
if len(c.Settings) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if len(c.Settings) != 0 {
|
if len(c.Commands) != 0 {
|
||||||
var keys []string
|
return newLinterError("Cannot configure both commands and settings", config.File, fmt.Sprintf("%s.%s", field, c.Name), false)
|
||||||
for key := range c.Settings {
|
}
|
||||||
keys = append(keys, key)
|
if len(c.Entrypoint) != 0 {
|
||||||
}
|
return newLinterError("Cannot configure both entrypoint and settings", config.File, fmt.Sprintf("%s.%s", field, c.Name), false)
|
||||||
return newLinterError(fmt.Sprintf("Cannot configure both commands and custom attributes %v", keys), config.File, fmt.Sprintf("%s.%s", field, c.Name), false)
|
}
|
||||||
|
if len(c.Environment) != 0 {
|
||||||
|
return newLinterError("Cannot configure both environment and settings", config.File, fmt.Sprintf("%s.%s", field, c.Name), false)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,6 +157,18 @@ func TestLintErrors(t *testing.T) {
|
||||||
from: "steps: { build: { image: golang, network_mode: 'container:name' } }",
|
from: "steps: { build: { image: golang, network_mode: 'container:name' } }",
|
||||||
want: "Insufficient privileges to use network_mode",
|
want: "Insufficient privileges to use network_mode",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
from: "steps: { build: { image: golang, settings: { test: 'true' }, commands: [ 'echo ja', 'echo nein' ] } }",
|
||||||
|
want: "Cannot configure both commands and settings",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "steps: { build: { image: golang, settings: { test: 'true' }, entrypoint: [ '/bin/fish' ] } }",
|
||||||
|
want: "Cannot configure both entrypoint and settings",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "steps: { build: { image: golang, settings: { test: 'true' }, environment: [ 'TEST=true' ] } }",
|
||||||
|
want: "Cannot configure both environment and settings",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testdata {
|
for _, test := range testdata {
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
steps:
|
||||||
|
publish:
|
||||||
|
image: plugins/docker
|
||||||
|
settings:
|
||||||
|
repo: foo/bar
|
||||||
|
tags: latest
|
||||||
|
environment:
|
||||||
|
CGO: 0
|
|
@ -0,0 +1,8 @@
|
||||||
|
steps:
|
||||||
|
publish:
|
||||||
|
image: plugins/docker
|
||||||
|
settings:
|
||||||
|
repo: foo/bar
|
||||||
|
tags: latest
|
||||||
|
commands:
|
||||||
|
- env
|
|
@ -18,7 +18,7 @@ steps:
|
||||||
image: alpine
|
image: alpine
|
||||||
entrypoint: ['some_entry', '--some-flag']
|
entrypoint: ['some_entry', '--some-flag']
|
||||||
|
|
||||||
singla-entrypoint:
|
single-entrypoint:
|
||||||
image: alpine
|
image: alpine
|
||||||
entrypoint: some_entry
|
entrypoint: some_entry
|
||||||
|
|
||||||
|
|
|
@ -304,10 +304,24 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"step": {
|
"step": {
|
||||||
|
"description": "A step of your workflow executes either arbitrary commands or uses a plugin. Read more: https://woodpecker-ci.org/docs/usage/workflow-syntax#steps",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/commands_step"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/entrypoint_step"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/plugin_step"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"commands_step": {
|
||||||
"description": "Every step of your pipeline executes arbitrary commands inside a specified docker container. Read more: https://woodpecker-ci.org/docs/usage/workflow-syntax#steps",
|
"description": "Every step of your pipeline executes arbitrary commands inside a specified docker container. Read more: https://woodpecker-ci.org/docs/usage/workflow-syntax#steps",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": ["image"],
|
"required": ["image", "commands"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
"description": "The name of the step. Can be used if using the array style steps list.",
|
"description": "The name of the step. Can be used if using the array style steps list.",
|
||||||
|
@ -334,9 +348,6 @@
|
||||||
"secrets": {
|
"secrets": {
|
||||||
"$ref": "#/definitions/step_secrets"
|
"$ref": "#/definitions/step_secrets"
|
||||||
},
|
},
|
||||||
"settings": {
|
|
||||||
"$ref": "#/definitions/step_settings"
|
|
||||||
},
|
|
||||||
"when": {
|
"when": {
|
||||||
"$ref": "#/definitions/step_when"
|
"$ref": "#/definitions/step_when"
|
||||||
},
|
},
|
||||||
|
@ -392,6 +403,160 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"entrypoint_step": {
|
||||||
|
"description": "Every step of your pipeline executes arbitrary commands inside a specified docker container. Read more: https://woodpecker-ci.org/docs/usage/workflow-syntax#steps",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["image", "entrypoint"],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "The name of the step. Can be used if using the array style steps list.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"$ref": "#/definitions/step_image"
|
||||||
|
},
|
||||||
|
"privileged": {
|
||||||
|
"$ref": "#/definitions/step_privileged"
|
||||||
|
},
|
||||||
|
"pull": {
|
||||||
|
"$ref": "#/definitions/step_pull"
|
||||||
|
},
|
||||||
|
"commands": {
|
||||||
|
"$ref": "#/definitions/step_commands"
|
||||||
|
},
|
||||||
|
"environment": {
|
||||||
|
"$ref": "#/definitions/step_environment"
|
||||||
|
},
|
||||||
|
"directory": {
|
||||||
|
"$ref": "#/definitions/step_directory"
|
||||||
|
},
|
||||||
|
"secrets": {
|
||||||
|
"$ref": "#/definitions/step_secrets"
|
||||||
|
},
|
||||||
|
"when": {
|
||||||
|
"$ref": "#/definitions/step_when"
|
||||||
|
},
|
||||||
|
"volumes": {
|
||||||
|
"$ref": "#/definitions/step_volumes"
|
||||||
|
},
|
||||||
|
"group": {
|
||||||
|
"description": "deprecated, use depends_on",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"depends_on": {
|
||||||
|
"description": "Execute a step after another step has finished.",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"minLength": 1,
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"detach": {
|
||||||
|
"description": "Detach a step to run in background until pipeline finishes. Read more: https://woodpecker-ci.org/docs/usage/services#detachment",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"failure": {
|
||||||
|
"description": "How to handle the failure of this step. Read more: https://woodpecker-ci.org/docs/usage/workflow-syntax#failure",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["fail", "ignore"],
|
||||||
|
"default": "fail"
|
||||||
|
},
|
||||||
|
"backend_options": {
|
||||||
|
"$ref": "#/definitions/step_backend_options"
|
||||||
|
},
|
||||||
|
"entrypoint": {
|
||||||
|
"description": "Defines container entrypoint.",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"minLength": 1,
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugin_step": {
|
||||||
|
"description": "Plugins let you execute predefined functions in a more secure context. Read more: https://woodpecker-ci.org/docs/usage/plugins/overview",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["image"],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "The name of the step. Can be used if using the array style steps list.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"$ref": "#/definitions/step_image"
|
||||||
|
},
|
||||||
|
"privileged": {
|
||||||
|
"$ref": "#/definitions/step_privileged"
|
||||||
|
},
|
||||||
|
"pull": {
|
||||||
|
"$ref": "#/definitions/step_pull"
|
||||||
|
},
|
||||||
|
"directory": {
|
||||||
|
"$ref": "#/definitions/step_directory"
|
||||||
|
},
|
||||||
|
"secrets": {
|
||||||
|
"$ref": "#/definitions/step_secrets"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"$ref": "#/definitions/step_settings"
|
||||||
|
},
|
||||||
|
"when": {
|
||||||
|
"$ref": "#/definitions/step_when"
|
||||||
|
},
|
||||||
|
"volumes": {
|
||||||
|
"$ref": "#/definitions/step_volumes"
|
||||||
|
},
|
||||||
|
"group": {
|
||||||
|
"description": "deprecated, use depends_on",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"depends_on": {
|
||||||
|
"description": "Execute a step after another step has finished.",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"minLength": 1,
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"detach": {
|
||||||
|
"description": "Detach a step to run in background until pipeline finishes. Read more: https://woodpecker-ci.org/docs/usage/services#detachment",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"failure": {
|
||||||
|
"description": "How to handle the failure of this step. Read more: https://woodpecker-ci.org/docs/usage/workflow-syntax#failure",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["fail", "ignore"],
|
||||||
|
"default": "fail"
|
||||||
|
},
|
||||||
|
"backend_options": {
|
||||||
|
"$ref": "#/definitions/step_backend_options"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"step_when": {
|
"step_when": {
|
||||||
"description": "Steps can be skipped based on conditions. Read more: https://woodpecker-ci.org/docs/usage/workflow-syntax#when---conditional-execution",
|
"description": "Steps can be skipped based on conditions. Read more: https://woodpecker-ci.org/docs/usage/workflow-syntax#when---conditional-execution",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
|
|
|
@ -116,6 +116,16 @@ func TestSchema(t *testing.T) {
|
||||||
testFile: ".woodpecker/test-custom-backend.yaml",
|
testFile: ".woodpecker/test-custom-backend.yaml",
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Broken Plugin by environment",
|
||||||
|
testFile: ".woodpecker/test-broken-plugin.yaml",
|
||||||
|
fail: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Broken Plugin by commands",
|
||||||
|
testFile: ".woodpecker/test-broken-plugin2.yaml",
|
||||||
|
fail: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range testTable {
|
for _, tt := range testTable {
|
||||||
|
|
|
@ -123,7 +123,9 @@ func (c *ContainerList) UnmarshalYAML(value *yaml.Node) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) IsPlugin() bool {
|
func (c *Container) IsPlugin() bool {
|
||||||
return len(c.Commands) == 0 && len(c.Entrypoint) == 0
|
return len(c.Commands) == 0 &&
|
||||||
|
len(c.Entrypoint) == 0 &&
|
||||||
|
len(c.Environment) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) IsTrustedCloneImage() bool {
|
func (c *Container) IsTrustedCloneImage() bool {
|
||||||
|
|
|
@ -131,6 +131,8 @@ func TestCopyLineByLineSizeLimit(t *testing.T) {
|
||||||
if _, err := w.Write([]byte("67\n89")); err != nil {
|
if _, err := w.Write([]byte("67\n89")); err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
// wait for writer to write
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
|
||||||
writes = testWriter.GetWrites()
|
writes = testWriter.GetWrites()
|
||||||
assert.Lenf(t, testWriter.GetWrites(), 2, "expected 2 writes, got: %v", writes)
|
assert.Lenf(t, testWriter.GetWrites(), 2, "expected 2 writes, got: %v", writes)
|
||||||
|
|
Loading…
Reference in a new issue