feat(code-scanner): add the config subsystem (PRD 0005) - #54
Merged
Conversation
Implements PRD 0005, completing the four subsystems PRD.md scopes to
code-scanner.
This is the subsystem with the least CI overlap and, for small-team servers,
probably the highest yield. Dependabot, gitleaks and CodeQL each cover part of
the other three; configuration has no equivalent, because a docker-compose.yml
in a repository says what someone INTENDED while the file on the box — plus
what is actually served, plus the directory permissions — says what is TRUE.
Only an agent on the host sees the second.
Checks, all traceable to how servers actually get breached: sensitive files
inside a served directory (.env, .git, dumps, backups, compose files),
directory listing, wildcard CORS combined with credentials, debug flags in
production, world-writable deploy directories, world-readable credential
files, privileged containers, mounted Docker sockets, host networking,
CAP_SYS_ADMIN.
Two rules shape the output:
- Rank by REACHABILITY, not benchmark severity. An exposed medium outranks a
local critical, because reachability is what sets the deadline. A tool that
scores both the same makes the operator do the triage the tool should have
done.
- Every finding carries a fix INCLUDING the follow-through. Moving an exposed
.env is not remediation; the credentials must be rotated because it should
be assumed read. Operators routinely do the first half and stop, so the
text says both. A test asserts every finding has a remediation and a
consequence — a rule added without them is the start of the
compliance-checklist failure mode this PRD exists to avoid.
Web-root inference is the weakest link and is treated as such: when it cannot
determine what is served, the scan reports webRootUnknown and marks the result
incomplete rather than reporting clean. Silence there means "did not look",
not "nothing exposed" — the same honesty rule as PRD 0002 R6.
112 tests (22 new).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
| const trimmed = line.trim(); | ||
| if (trimmed.startsWith('#')) continue; | ||
|
|
||
| const nginx = /^root\s+([^;]+);/.exec(trimmed); |
| const nginx = /^root\s+([^;]+);/.exec(trimmed); | ||
| if (nginx?.[1]) roots.add(nginx[1].trim().replace(/^["']|["']$/g, '')); | ||
|
|
||
| const apache = /^DocumentRoot\s+(.+)$/i.exec(trimmed); |
| .filter((line) => !line.trim().startsWith('#')) | ||
| .join('\n'); | ||
|
|
||
| if (/autoindex\s+on|Options\s+[^\n]*\+Indexes/i.test(body)) { |
|
|
||
| // Individually defensible, together a vulnerability: a wildcard origin with | ||
| // credentials allows any site to make authenticated requests as the user. | ||
| const wildcardOrigin = /Access-Control-Allow-Origin[^\n]*\*/i.test(body); |
| // Individually defensible, together a vulnerability: a wildcard origin with | ||
| // credentials allows any site to make authenticated requests as the user. | ||
| const wildcardOrigin = /Access-Control-Allow-Origin[^\n]*\*/i.test(body); | ||
| const allowCredentials = /Access-Control-Allow-Credentials[^\n]*true/i.test(body); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements PRD 0005 — the
config/subsystem, completing the four subsystemsPRD.mdscopes tocode-scanner.deps/secrets/sast/config/Why this one has the highest yield
The other three each have a CI counterpart doing overlapping work — Dependabot, gitleaks, CodeQL. Configuration has none, because a
docker-compose.ymlin a repository says what someone intended. The file on the box, plus what is actually served, plus the directory permissions, say what is true. Only an agent on the host sees the second.And for the users this product targets, configuration is what actually gets them breached: a
.envserved by a web root, a reachable.git, debug mode left on, a mounted Docker socket. No exploit required — just a default nobody changed.Two rules shape the output
Rank by reachability, not benchmark severity. An exposed medium outranks a local critical, because reachability is what sets the deadline. A tool that scores both identically makes the operator do the triage the tool should have done.
Every finding carries a fix, including the follow-through. Moving an exposed
.envis not remediation — the credentials must be rotated, because it should be assumed read. Operators routinely do the first half and stop, so the text says both. A test asserts every finding has a remediation and a consequence; a rule added without them is the start of the compliance-checklist failure mode this PRD exists to avoid.Honest about the weak link
Every exposure check depends on knowing what the webserver serves, and real nginx configs use includes, variables and per-vhost roots. When inference fails, the scan sets
webRootUnknownand marks the whole result incomplete rather than reporting clean — silence there means "did not look", not "nothing exposed". Same honesty rule as PRD 0002 R6. The web root's provenance (configured/parsed/conventional) is reported with the finding, so a conventional guess isn't presented with the confidence of a parsed directive.Not a compliance tool
Stated plainly in the PRD and the docs: this is a short, defensible check set, not CIS/STIG. A clean result is not certification, and
compliance-reporterremains a separate marketplace module inPRD.md.Verification
112 tests (22 new), build clean.