Skip to content

Hard-fail on invalid ScopeKnownError config, warn on others - #356

Open
technicalpickles wants to merge 2 commits into
mainfrom
fix-known-error-silent-drop
Open

Hard-fail on invalid ScopeKnownError config, warn on others#356
technicalpickles wants to merge 2 commits into
mainfrom
fix-known-error-silent-drop

Conversation

@technicalpickles

Copy link
Copy Markdown
Member

Summary

  • ScopeKnownError conversion failures (bad regex capture name, broken fix template) now abort config load instead of vanishing silently. A check that isn't actually registered gave false confidence it was protecting you.
  • ScopeDoctorGroup and ScopeReportLocation conversion failures warn and continue instead, matching this file's existing warn-and-skip convention for other load errors (unreadable file, bad YAML, bad model shape).
  • Multiple bad ScopeKnownError files are all reported in one run instead of one at a time.

Test plan

Fixes #344

Config::new silently dropped any config entry that failed conversion
from raw YAML into its typed model, regardless of kind. A ScopeKnownError
with a reserved capture group name or a broken fix template just vanished
with no signal, so a typo could disable an error check without anyone
noticing.

ScopeDoctorGroup and ScopeReportLocation conversion failures now warn and
continue, matching the file's existing warn-and-skip convention for other
load errors (unreadable file, bad YAML, bad model shape). ScopeKnownError
failures are collected across the whole config tree and abort config load,
since a known-error rule that silently isn't registered gives false
confidence it's protecting you when it isn't.

Fixes #344
Note the new load-time validation behavior on each affected model's doc
page: ScopeKnownError failures abort load, ScopeDoctorGroup and
ScopeReportLocation failures warn and skip. Also clarifies why `scope
lint` doesn't check ScopeKnownError/ScopeDoctorGroup itself, since that
validation now happens earlier, at config load.
@dryrunsecurity

Copy link
Copy Markdown

DryRun Security

This pull request introduces a low-severity vulnerability in src/shared/config_load.rs where the write_raw_config_to_disk function creates temporary files in a predictable path, making it susceptible to symlink attacks. Although the issue is not blocking, it poses a risk of sensitive file overwriting or unintended data writes.

Insecure temporary file creation in src/shared/config_load.rs (drs_bb2f4f35)
Vulnerability Insecure temporary file creation
Description The write_raw_config_to_disk function writes to a predictable path in /tmp/scope/ using File::create. This is vulnerable to symlink attacks where an attacker could create a symlink at the expected path, causing the application to overwrite sensitive files or write data to an unintended location.

run_id: config_options.get_run_id(),
};
let mut known_error_failures: Vec<String> = Vec::new();
for raw_config in raw_config {
let file_path = raw_config.metadata().file_path();
let kind = raw_config.kind.clone();
match ParsedConfig::try_from(raw_config) {
Ok(value) => this.add_model(value),
Err(e) => {
let message =
format!("Unable to convert config from {} because {}", file_path, e);
if is_hard_fail_kind(&kind) {
known_error_failures.push(message);
} else {
warn!(target: "user", "{}", message);
}
}
}
}
if !known_error_failures.is_empty() {
return Err(anyhow!(known_error_failures.join("\n")));
}
Ok(this)
}
pub fn write_raw_config_to_disk(&self) -> Result<PathBuf> {


Comment to provide feedback on these findings.

Report false positive: @dryrunsecurity fp [FINDING ID] [FEEDBACK]
Report low-impact: @dryrunsecurity nit [FINDING ID] [FEEDBACK]

Example: @dryrunsecurity fp drs_90eda195 This code is not user-facing

All finding details can be found in the DryRun Security Dashboard.

technicalpickles added a commit that referenced this pull request Jul 28, 2026
…isk (#358)

## Summary
- `write_raw_config_to_disk` wrote the serialized raw config to
`/tmp/scope/config-{run_id}.json` via plain `File::create`, which
follows symlinks
- `run_id` is attacker/user-controllable via `--run-id` or
`SCOPE_RUN_ID`, making the exact path predictable ahead of time; on a
shared multi-user box or CI runner, an attacker could pre-plant a
symlink there to have scope overwrite an arbitrary file
- Replaced with `tempfile::Builder` to create a securely-generated,
unpredictable file under `/tmp/scope`, persisted via
`NamedTempFile::keep()` so it survives past this function (it's read by
a child process spawned right after in `exec_sub_command`)
- Moved `tempfile` from `[dev-dependencies]` to `[dependencies]` in
`Cargo.toml` since it's now needed at runtime

DryRun Security finding surfaced during review of #356 (that PR's own
diff is untouched here; this fixes a pre-existing issue in the
surrounding file that the security scanner flagged while scanning the
whole changed file).

## Test plan
- [x] `cargo build`
- [x] `cargo test` (full suite, 195+ tests pass)
- [x] `cargo fmt --check`
- [x] `cargo clippy --all-targets --all-features -- -D warnings`
- [x] Manually verified: ran `scope external` against the
`tests/test-cases/command-paths` fixture with an instrumented script;
confirmed `$SCOPE_CONFIG_JSON` points to a randomly-named file (not
`run_id`-derived), created with `0600` permissions, containing valid
config JSON readable by the child process

@rubberduck203 rubberduck203 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM but I have been wondering since we talked if it should just WARN to the user target. That way it wouldn't prevent everyone from running analyze over one bad config file.

I feel like we could maybe do a few different things.

Instead of hard failure we could just emit the warning

warn!(target: "user", "{}", message);

and then set up a monitor to send us an alert if this specific warning is matched.

OR

We could show the user a warning ad send an error to telemetry.

warn!(target: "user", "{}", message);
error!("{}", message);

I'm 99% certain that if we don't specify a target it only ends up in the traces and is not shown to the user, but we'd have to test that to be sure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Known-error/fix config errors are silently dropped at load time instead of failing loudly

2 participants