fix(audit): read forwarded -rpath and -rpath-link as linker paths

A cgo build with CGO_LDFLAGS=-Wl,-rpath,private-runtime kept the
relative directory, but the forwarded linker parser only knew -L, -T,
-I, --library-path, --dynamic-linker and --script, so go.build_paths
reported not_observed. Add -rpath and -rpath-link to the same parser,
in the attached, = and next-field forms. Loader-expanded values such as
$ORIGIN/../lib and @loader_path/Frameworks name no build-host directory
and stay unreported.
This commit is contained in:
Matt Van Horn committed 2026-11-03 12:23:16 -08:00
1 parent 279254b7e7
commit a44e0e13ff
2 files changed
+46 -3

No files matched your search

@@ -1465,6 +1465,40 @@ describe("ReleaseExposureService Go trimpath rewrites", () => {
});
});
describe("ReleaseExposureService Go forwarded linker search paths", () => {
it.each([
["-Wl,-rpath,private-runtime", "private-runtime"],
["-Wl,-rpath=private-runtime", "private-runtime"],
["-Wl,--rpath=private-runtime", "private-runtime"],
["-Xlinker -rpath -Xlinker private-runtime", "private-runtime"],
["-Wl,-rpath-link,private-runtime", "private-runtime"],
["-Wl,-rpath-link=private-runtime", "private-runtime"],
["-Wl,--rpath-link,private-runtime", "private-runtime"],
])(
"keeps the relative search path a compiler forwards in %s",
async (value, path) => {
const report = await goReport(goModule([{ key: "CGO_LDFLAGS", value }]));
const check = report.checks.find((item) => item.id === "go.build_paths");
expect(check?.status).toBe("exposed");
expect(check?.observation.samples).toEqual([path]);
},
);
it("does not read loader-expanded runtime search paths as build paths", async () => {
const report = await goReport(
goModule([
{
key: "CGO_LDFLAGS",
value:
"-Wl,-rpath,$ORIGIN/../lib -Wl,-rpath=${ORIGIN}/lib -Wl,-rpath,@loader_path/Frameworks -Xlinker -rpath -Xlinker @executable_path",
},
]),
);
const check = report.checks.find((item) => item.id === "go.build_paths");
expect(check?.status).toBe("not_observed");
});
});
describe("ReleaseExposureService Go relative flag paths", () => {
it("keeps relative paths given to compiler path flags, attached or not", async () => {
const report = await goReport(
@@ -842,10 +842,17 @@ const GO_COMPILER_PATH_FLAG =
* Linker options a C compiler forwards through `-Wl,a,b` or `-Xlinker a`. The
* linker reads its own letters: `-L`, `-T` and `-I` (the ELF interpreter) take
* a path, `-Bstatic` does not, and `-Ttext=0x400000` and its siblings set a
* section or segment address.
* section or segment address. `-rpath` and `-rpath-link` take a runtime or
* link-time library search directory.
*/
const LINKER_PATH_FLAG =
/^(?:-(?!T(?:bss|data|text|(?:ldata|rodata|text)-segment)(?:=|$))[ILT]|-{1,2}(?:dynamic-linker|library-path|script)(?==|$))=?(.*)$/su;
/^(?:-(?!T(?:bss|data|text|(?:ldata|rodata|text)-segment)(?:=|$))[ILT]|-{1,2}(?:dynamic-linker|library-path|rpath|rpath-link|script)(?==|$))=?(.*)$/su;
/**
* A runtime search path the loader expands, such as `$ORIGIN/../lib` or
* `@loader_path/Frameworks`, which names no directory on the build host.
*/
const LINKER_RUNTIME_TOKEN =
/^(?:\$(?:ORIGIN|LIB|PLATFORM)\b|\$\{(?:ORIGIN|LIB|PLATFORM)\}|@(?:executable_path|loader_path|rpath)(?:\/|$))/u;
/**
* The options `go tool link -h` documents as taking a directory, path or file,
* as `-L dir` or `-L=dir`, never attached. `-extld`, `-extar` and `-libgcc`
@@ -1015,7 +1022,9 @@ const compilerPaths = (value: string): string[] => {
return [
...flagPaths(flags),
...goRelativeFlagPaths(compiler, GO_COMPILER_PATH_FLAG),
...goRelativeFlagPaths(linker, LINKER_PATH_FLAG),
...goRelativeFlagPaths(linker, LINKER_PATH_FLAG).filter(
(path) => !LINKER_RUNTIME_TOKEN.test(path),
),
];
};