Skip to content

feat(profiles)!: require explicit config; stop auto-discovering .clawscan.yml#20

Open
jesse-merhi wants to merge 5 commits into
mainfrom
feat/config-trust-flip
Open

feat(profiles)!: require explicit config; stop auto-discovering .clawscan.yml#20
jesse-merhi wants to merge 5 commits into
mainfrom
feat/config-trust-flip

Conversation

@jesse-merhi

@jesse-merhi jesse-merhi commented Jul 21, 2026

Copy link
Copy Markdown
Member

New behavior

ClawScan never loads — or even looks for — .clawscan.yml files it was not explicitly told to trust. Operators name their config (--config <path>) or opt in to upward-walk discovery (--discover-config, which requires --profile); otherwise flags-only and built-in-profile runs do no config discovery I/O at all and print nothing about nearby config files. Run artifacts record where the effective config came from (configSource: a path, built-in, or flags-only).

  • A flags-only run in a directory containing a .clawscan.yml ignores it silently: no upward walk, no stderr notice, configSource: "flags-only".
  • --profile <name> where the profile lives in an undiscovered config fails with Unknown profile: <name> (available: clawhub, clawhub-aig) — no filesystem traversal to hint at nearby files.
  • --discover-config restores the old upward-walk behavior for a single run. It is mutually exclusive with --config and requires --profile (discovery without a profile would record the file as configSource while applying none of it).
  • Built-in profiles (--profile clawhub) record configSource: built-in even when --discover-config finds an unrelated project config.
  • clawscan profiles lists built-in profiles only, each marked Source: built-in.

Breaking change for anyone relying on silent repo-root .clawscan.yml pickup. Migration: pass --config <path> or --profile <name> --discover-config.

Why

Groundwork for user-defined (command-backed) scanners: config discovered from a scan target must never gain the power to execute commands an attacker chose by dropping a .clawscan.yml into a skill. If ClawScan deliberately does not trust a config, it also does not spend I/O finding it or mention it — a notice naming the file would itself be attacker-influenced output. Precedent: git, VS Code Workspace Trust, direnv, Trivy, and Grype all refuse discovered executable config; ESLint's implicit config trust is the cautionary tale.

CLI proof

Before — PR base silently auto-discovers .clawscan.yml and resolves the team-policy profile from it with no notice:

Base binary silently loading a discovered .clawscan.yml

What this shows: clawscan ./README.md --profile team-policy --json on the PR base finds the config via upward walk and runs the profile without any indication a file was trusted.

State: demo directory containing README.md and a .clawscan.yml defining team-policy; binary built from the PR base (main @ a37f49f); 114×31 terminal window capture.

Now — the same directory: the local config is ignored with no notice, and the run records configSource: "flags-only":

PR binary silently ignoring the local config

What this shows: ls -a shows .clawscan.yml sitting next to the target; clawscan ./README.md --scanner clawscan-static --json produces JSON with "configSource": "flags-only" and prints nothing to stderr — untrusted config is neither loaded nor mentioned.

State: same demo directory; binary built from this PR's head; 114×31 terminal window capture.

Now — asking for a profile that lives in the undiscovered config fails plainly, and --discover-config opts back in:

Unknown-profile error, then opt-in discovery resolving the profile

What this shows: clawscan ./README.md --profile team-policy fails with Unknown profile: team-policy (available: clawhub, clawhub-aig) — no traversal, no mention of the nearby file. The same command with --discover-config --json loads the config and runs with "profile": "team-policy".

State: same demo directory and PR-head binary; 114×31 terminal window capture.

How to verify

  1. In a directory with a .clawscan.yml defining a profile, run clawscan <file> --scanner clawscan-static --json 2>err.txt — expect normal JSON with configSource: "flags-only" and an empty err.txt.
  2. Run clawscan <file> --profile <that-profile> — expect Unknown profile: <that-profile> (available: clawhub, clawhub-aig) with no mention of the local file.
  3. Add --discover-config — expect the profile to resolve and the artifact's configSource to record the loaded path.
  4. Run clawscan <file> --profile clawhub — expect configSource: "built-in" in the artifact.
  5. Run clawscan profiles in that directory — expect built-ins only, Source: built-in.

Checks

  • Resolver tests cover: no discovery from cwd or parents (TestResolveArgsDefaultRunDoesNotAutoDiscover_*), --discover-config restoring the walk, its --profile requirement, mutual exclusion with --config, the plain unknown-profile error (TestResolveArgsUnknownProfileDoesNotDiscoverConfig), and configSource provenance (loaded path / built-in / flags-only).
  • CLI tests assert flags-only runs emit nothing to stderr about nearby configs.
  • go test -count=1 ./... — passes except two pre-existing macOS /var-symlink failures in target_test.go unrelated to this change.
  • go vet ./..., gofmt, make docs-site — clean.

…scan.yml

Auto-discovery of .clawscan.yml via upward directory walk is now opt-in
(--discover-config). Discovered-but-ignored configs produce a stderr
notice, and run artifacts record their config source. Groundwork for
config-defined scanners: discovered config must never gain the power to
execute commands chosen by a scan target.
… bundled skill

Review findings from ticket-1 code review: the migration hint was
skipped exactly when the requested profile lived in an unloaded
discovered config, and skills/clawscan-cli/SKILL.md still documented
automatic discovery.
Copilot AI review requested due to automatic review settings July 21, 2026 04:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

An embedded profile run recorded configSource: flags-only (or an
unrelated discovered config path). selected.source is authoritative:
built-in profiles record built-in provenance regardless of what other
config was loaded alongside them.
Discovery without a profile recorded the discovered file as the run's
configSource while applying none of its settings (sandbox mode, image,
env). Reject the combination with guidance instead of silently claiming
provenance.
Flags-only runs no longer walk parent directories looking for
.clawscan.yml, and the discovered-but-not-loaded stderr notice is gone:
if we deliberately do not trust a config, we do not spend I/O finding it
or mention it. Discovery lives solely in discoverConfig, reached only
via --discover-config. clawscan profiles now lists built-ins only for
the same reason. Help and docs explain the security rationale: a config
can define scanners that run arbitrary commands, so ClawScan never loads
config it was not explicitly told to trust.
@clawsweeper

clawsweeper Bot commented Jul 22, 2026

Copy link
Copy Markdown

ClawSweeper status: review started.

I am starting a fresh review of this pull request: feat(profiles)!: require explicit config; stop auto-discovering .clawscan.yml This is item 1/1 in the current shard. Shard 0/1.

This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking.

Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted.

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.

2 participants