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
+9 -1
View File
@@ -24,7 +24,7 @@ import { QueryBuilder } from '../db/queries';
import { GraphTraverser } from '../graph';
import { formatContextAsMarkdown, formatContextAsJson } from './formatter';
import { logDebug } from '../errors';
import { validatePathWithinRoot } from '../utils';
import { validatePathWithinRoot, isConfigLeafNode } from '../utils';
import { isTestFile, extractSearchTerms, scorePathRelevance, getStemVariants, isDistinctiveIdentifier } from '../search/query-utils';
import { LOW_CONFIDENCE_MARKER } from './markers';
@@ -1161,6 +1161,14 @@ export class ContextBuilder {
* Extract code from a node's source file
*/
private async extractNodeCode(node: Node): Promise<string | null> {
// SECURITY (#383): a config-leaf node's on-disk line is `key = <secret>`.
// Return the KEY only — never read the value off disk. This closes the
// includeCode / buildContext code-block path, mirroring the explore source
// renderer; an agent that genuinely needs a value can read the file itself.
if (isConfigLeafNode(node)) {
return node.signature || node.qualifiedName || node.name;
}
const filePath = validatePathWithinRoot(this.projectRoot, node.filePath);
if (!filePath || !fs.existsSync(filePath)) {