Repository navigation
fix: stop scenario metadata from reaching shells and host commands #155
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func TestValidateOVSToken(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| for _, value := range []string{"phenix", "br-0", "eth1.100", "mirror", "a_b", "abcdefghijklmno"} { | ||
| require.NoError(t, validateOVSToken("field", value), value) | ||
| } | ||
|
|
||
| for _, value := range []string{"", "--", "two words", "tab\there", "new\nline", "br0;reboot", "abcdefghijklmnop", "$(id)"} { | ||
| require.Error(t, validateOVSToken("field", value), value) | ||
| } | ||
| } | ||
|
|
||
| func TestExtractMetadataRejectsUnsafeTokens(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| _, err := extractMetadata(map[string]any{"version": "v1", "mirrorBridge": "phenix -- del-br phenix"}) | ||
| require.Error(t, err) | ||
|
|
||
| _, err = extractMetadata(map[string]any{"version": "v1", "mirrorVLAN": "mirror;id"}) | ||
| require.Error(t, err) | ||
|
|
||
| amd, err := extractMetadata(map[string]any{"version": "v1", "mirrorBridge": "phenix", "mirrorVLAN": "mirror"}) | ||
| require.NoError(t, err) | ||
| assert.Equal(t, "phenix", amd.MirrorBridge) | ||
|
|
||
| _, err = extractHostMetadata(map[string]any{"hilInterfaces": []any{"eth1", "eth2 eth3"}}) | ||
| require.Error(t, err) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -83,6 +83,12 @@ If exit non-zero and `abortOnError` is true, then the component exits as failed. | |
| If no validator is provided then it is assumed the test succeeded if the atomic | ||
| executor exits cleanly. | ||
|
|
||
| > [!IMPORTANT] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be enabled by default to avoid breaking existing environments, with the ability to lock things down more in environments where the security is needed (and there's less trust) |
||
| > Validators run as shell scripts on the phenix host, not in the VM, so they are | ||
| > off by default. Set `PHENIX_SCORCH_HOST_VALIDATORS=1` in the phenix | ||
| > environment to run them; otherwise each validator is skipped with a warning | ||
| > and the result is not validated. | ||
|
|
||
| `vms` is a list of settings per VM to execute the test on. | ||
|
|
||
| ## VM Settings | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,14 @@ | ||
| import os | ||
| import subprocess | ||
| import time | ||
| import uuid | ||
| from pathlib import Path | ||
|
|
||
| from box import Box | ||
|
|
||
| from phenix_apps.apps.scorch import ComponentBase | ||
| from phenix_apps.common import utils | ||
| from phenix_apps.common.logger import logger | ||
| from phenix_apps.common.settings import SCORCH_HOST_VALIDATORS | ||
|
|
||
| # This can be changed here to reflect your directory structure on hosts as a default. | ||
| # This is overwritten if a value is provided via goartPath | ||
|
|
@@ -102,34 +103,39 @@ def start(self): | |
| utils.mm_exec_wait(mm, hostname, cmd) | ||
| time.sleep(5) | ||
| logger.info(f"retrieving results: {out_file}") | ||
| results_file = os.path.join(self.base_dir, f"{hostname}.json") | ||
| results_file = str(Path(self.base_dir) / f"{hostname}.json") | ||
|
|
||
| try: | ||
| utils.mm_recv(mm, hostname, out_file, results_file) | ||
| logger.info(f"results_file path: {results_file}") | ||
| logger.info(f"results_file exists: {os.path.exists(results_file)}") | ||
| logger.info(f"results_file exists: {Path(results_file).exists()}") | ||
| except Exception as ex: | ||
| raise RuntimeError( | ||
| f"failed to get results file from {hostname}: {ex}" | ||
| ) from ex | ||
|
|
||
| validator = self.metadata.get("validator", None) | ||
| if validator: | ||
| if validator and not SCORCH_HOST_VALIDATORS: | ||
| logger.warning( | ||
| f"skipping host-side validator for {hostname}: set " | ||
| "PHENIX_SCORCH_HOST_VALIDATORS=1 to allow validators to run on the host" | ||
| ) | ||
| elif validator: | ||
| logger.info(f"validating results from {hostname}") | ||
|
|
||
| tempfile = f"/tmp/{uuid.uuid4()!s}.sh" | ||
| with open(tempfile, "w") as tf: | ||
| tempfile = Path(f"/tmp/{uuid.uuid4()!s}.sh") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't there a module in python standard library for secure temporary file creation?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agreed please switch to python tempfile |
||
| with tempfile.open("w") as tf: | ||
| tf.write(validator) | ||
|
|
||
| results = Box.from_json(filename=results_file) | ||
|
|
||
| proc = subprocess.run( | ||
| ["sh", tempfile, hostname], | ||
| ["sh", str(tempfile), hostname], | ||
| input=results.Executor.ExecutedCommand.results.encode(), | ||
| capture_output=True, | ||
| ) | ||
|
|
||
| os.remove(tempfile) | ||
| tempfile.unlink() | ||
|
|
||
| if proc.returncode != 0: | ||
| stderr = proc.stderr.decode() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,6 +38,12 @@ metadata: | |
| > validator script should be written to process STDIN. Anything the validator | ||
| > script writes to STDERR will be available to the user if the validation fails. | ||
|
|
||
| > [!IMPORTANT] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as the note on |
||
| > Validators run as shell scripts on the phenix host, not in the VM, so they are | ||
| > off by default. Set `PHENIX_SCORCH_HOST_VALIDATORS=1` in the phenix | ||
| > environment to run them; otherwise each validator is skipped with a warning | ||
| > and the result is not validated. | ||
|
|
||
| ## Types | ||
| - VM-specific command types | ||
| - `exec`: execute a command (`cc exec`) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Leaves /tmp"... where did it go? And did it take the kids with it?