fix(security): index config files by key only, never surface values (#383) (#722)

Spring `application.{properties,yml}` keys (and Shopify Liquid `{% schema %}`
blocks) were storing the config VALUE in the node docstring, and
`codegraph_explore`'s source section re-read the raw `key = value` line off
disk — so a secret committed to a config file (DB password, API key, JDBC URL
with embedded credentials) could be pushed into an agent's context via
explore/node output without the agent ever opening the file.

Config-leaf nodes (`kind: 'constant'` in a config language) now surface the KEY
only, via a shared `isConfigLeafNode` predicate applied at both surfacing
paths: the value is dropped from extraction, `getCode`/`includeCode` returns
the key instead of the file line, and explore excludes config leaves from
source rendering. The predicate can't match real code (real constants are
ts/java/go/…), so `@Value`/`@ConfigurationProperties` resolution and impact are
unaffected. Adds a regression test asserting a planted secret never appears in
`codegraph_explore` / `codegraph_node` output while the keys still resolve.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-06-08 00:10:10 -04:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 80db274e5f
commit 112e278b5c
7 changed files with 150 additions and 4 deletions
+19
View File
@@ -46,6 +46,25 @@ const SENSITIVE_PATHS = new Set([
'c:\\', 'c:\\windows', 'c:\\windows\\system32',
]);
/**
* Config "languages" whose nodes are pure key/value DATA lifted from a config
* file (e.g. Spring `application.{yml,properties}`), not source code.
*/
export const CONFIG_LEAF_LANGUAGES: ReadonlySet<string> = new Set(['yaml', 'properties']);
/**
* A config-leaf node is a single key lifted out of a pure config/data file —
* `kind: 'constant'` in a {@link CONFIG_LEAF_LANGUAGES} language. Its on-disk
* line is `key = <value>`, and that value is routinely a secret (DB password,
* API key, JDBC URL with embedded creds). CodeGraph must surface the KEY only
* and never read/return the value, or it pushes secrets into agent context
* unbidden — the value isn't needed for resolution, and an agent that genuinely
* needs it can read the file directly. (#383)
*/
export function isConfigLeafNode(node: { kind: string; language?: string }): boolean {
return node.kind === 'constant' && !!node.language && CONFIG_LEAF_LANGUAGES.has(node.language);
}
/**
* Validate that a resolved file path stays within the project root.
* Prevents path traversal attacks (e.g. node.filePath = "../../etc/passwd").