fix(audit): read relative -L paths and split flag arguments

The relative-path reader handled C compiler settings only, so a directory
kept through Go's own linker flag, such as -ldflags='-L private-sdk -s -w',
reported go.build_paths as not_observed.

A quoted flag and argument also reached it as one field. The separating
space stayed on the argument, which reported rooted paths twice and kept a
leading space on relative ones.

Give -ldflags its own pattern that matches only -L, so -B gobuildid and
-T 0x1000 stay ignored, and consume the separator in the flag patterns.
This commit is contained in:
Matt Van Horn committed 2026-11-02 03:31:38 -08:00
1 parent 5b93dabfd7
commit e136911605
2 files changed
+42 -5

No files matched your search

@@ -1357,6 +1357,31 @@ describe("ReleaseExposureService Go relative flag paths", () => {
const check = report.checks.find((item) => item.id === "go.build_paths");
expect(check?.status).toBe("not_observed");
});
it.each([
["-L private-sdk -s -w", "private-sdk"],
[`"-L private-sdk -s -w"`, "private-sdk"],
["-L=private-sdk -s -w", "private-sdk"],
["all=-L private-sdk -s -w", "private-sdk"],
])("keeps the relative linker directory in %s", async (value, path) => {
const report = await goReport(
goModule([{ key: "-ldflags", value: `-B gobuildid ${value}` }]),
);
const check = report.checks.find((item) => item.id === "go.build_paths");
expect(check?.status).toBe("exposed");
expect(check?.observation.samples).toEqual([path]);
});
it.each([
[`"-isystem /opt/my sdk/include"`, "/opt/my sdk/include"],
[`"-I private sdk/include"`, "private sdk/include"],
])("reports %s once, without the separating space", async (value, path) => {
const report = await goReport(goModule([{ key: "CGO_CFLAGS", value }]));
const check = report.checks.find((item) => item.id === "go.build_paths");
expect(check?.status).toBe("exposed");
expect(check?.observation.samples).toEqual([path]);
expect(check?.observation.counts.paths).toBe(1);
});
});
describe("ReleaseExposureService Go paths", () => {
@@ -798,7 +798,17 @@ const GO_FLAG_PATH =
*/
const GO_COMPILER_SETTING = /^(?:CGO_[A-Z]+FLAGS|-gccgoflags)$/u;
const GO_COMPILER_PATH_FLAG =
/^-(?:idirafter|imacros|include|iprefix|iquote|isysroot|isystem|[BFILT])=?(.*)$/su;
/^-(?:idirafter|imacros|include|iprefix|iquote|isysroot|isystem|[BFILT])=?\s*(.*)$/su;
/**
* `go tool link` takes a library directory as `-L dir` or `-L=dir`, never
* attached, and `go build` may prefix the flags with `pattern=`.
*/
const GO_LINKER_PATH_FLAG = /^(?:[^\s=-][^\s=]*=)?-{1,2}L(?:=|\s+|$)(.*)$/su;
const goPathFlag = (key: string): RegExp | null => {
if (GO_COMPILER_SETTING.test(key)) return GO_COMPILER_PATH_FLAG;
return key === "-ldflags" ? GO_LINKER_PATH_FLAG : null;
};
/** Flags in one value, with a quoted path kept whole up to the next flag. */
const goFlags = (value: string): string[] =>
@@ -817,9 +827,9 @@ const goFlagPaths = (value: string): string[] =>
* A path flag's relative argument, attached or in the next field. A rooted
* argument belongs to `goFlagPaths`, which keeps its `=` and `,` structure.
*/
const goCompilerFlagPaths = (value: string): string[] =>
const goRelativeFlagPaths = (value: string, pathFlag: RegExp): string[] =>
goFlags(value).flatMap((flag, index, flags) => {
const attached = GO_COMPILER_PATH_FLAG.exec(flag)?.[1]?.trimEnd();
const attached = pathFlag.exec(flag)?.[1]?.trimEnd();
if (attached === undefined) return [];
const next = flags[index + 1];
const argument =
@@ -849,8 +859,10 @@ const goPaths = (
for (const setting of module.settings) {
if (!GO_FLAG_SETTING.test(setting.key)) continue;
for (const path of goFlagPaths(setting.value)) add(path);
if (GO_COMPILER_SETTING.test(setting.key))
for (const path of goCompilerFlagPaths(setting.value)) paths.add(path);
const pathFlag = goPathFlag(setting.key);
if (pathFlag !== null)
for (const path of goRelativeFlagPaths(setting.value, pathFlag))
paths.add(path);
}
return [...paths];
};