diff --git a/src/application/releaseExposure/ReleaseExposureService.test.ts b/src/application/releaseExposure/ReleaseExposureService.test.ts index 0b9d426c43..92009a44d2 100644 --- a/src/application/releaseExposure/ReleaseExposureService.test.ts +++ b/src/application/releaseExposure/ReleaseExposureService.test.ts @@ -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( diff --git a/src/application/releaseExposure/ReleaseExposureService.ts b/src/application/releaseExposure/ReleaseExposureService.ts index da8478f058..b95f9542a9 100644 --- a/src/application/releaseExposure/ReleaseExposureService.ts +++ b/src/application/releaseExposure/ReleaseExposureService.ts @@ -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), + ), ]; };