fix(go): distinguish dependency coverage from parse completeness

Explain unknown dependency coverage when embedded metadata has no main-module record. Preserve decoded dependencies and distinguish GOPATH-style omissions from observed module-mode empty lists.

Refs #4191, #4267

Co-authored-by: Y0UW1SH <29498139+Y0UW1SH@users.noreply.github.com>
This commit is contained in:
morlutoandY0UW1SH committed 2026-10-16 07:25:22 +08:00
1 parent 79e83cbd67
commit a05092726f
4 files changed
+115 -23

No files matched your search

+9 -2
View File
@@ -39,8 +39,8 @@ container format, architecture, word size and byte order. A recognized
- `module.dependencies`, with the same fields and their original order;
- `module.settings`, retaining ordered key/value entries and duplicate keys;
- `module.unparsed_lines`, `module.unparsed_line_bytes_base64` and
`module.complete`, so unfamiliar, malformed, conflicting or undecodable records
do not disappear behind a complete result.
`module.complete`, which says whether every present module-text line was
parsed. It does not establish that the dependency inventory is complete.
Missing checksums are `null`; empty fields and development/local replacement
versions remain unchanged. Build settings such as `GOOS`, `GOARCH` or `vcs.revision`
@@ -50,6 +50,13 @@ base64 while other decodable module records are still reported; the reader does
not insert replacement characters or reject the entire artifact for those
strings.
When no main-module (`mod`) record was decoded, `module.main` is `null` and
dependency coverage is unknown. Successfully decoded dependency records remain
visible. GOPATH-mode builds may contain only a package `path` and build settings;
their empty dependency array does not establish the absence of dependencies.
The result's limitations state this explicitly. A module-mode build with a main
module record and no dependency records has an observed empty dependency list.
## Supported boundary
The built-in TypeScript reader supports ELF, PE and thin Mach-O images with Go's
+32 -21
View File
@@ -17,6 +17,7 @@ import {
import {
goBinarySchema,
type GoBinary,
type GoModuleMetadata,
type InspectGoBinaryInput,
} from "../domain/go/goBinary.js";
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
@@ -138,6 +139,36 @@ const outputLimit = (path: string) =>
{ maximum_output_bytes: OUTPUT_BYTES },
);
/** Fixed reader boundaries plus unknowns left by the observed metadata. */
const limitationsFor = (
info: { go_version: string | null; module_text: string | null } | null,
module: GoModuleMetadata | null,
): string[] => [
"Embedded build metadata is observed file content, not proof of compiler identity, dependency authenticity or runtime behavior.",
"Only Go build information is inspected; function names, source mappings, type metadata and decompiled code are not recovered.",
"Resource guards bound stable artifact reads and cumulative header searches to 256 MiB each, aggregate embedded strings to 1 MiB, structural table decoding to 16 MiB and complete metadata payloads to 8 MiB; these are not operating-system memory or CPU limits.",
...(info !== null && (info.go_version === null || info.module_text === null)
? [
"Some embedded Go strings contain non-UTF8 bytes; undecodable text is null and exact source bytes are preserved as base64.",
]
: []),
...(info === null
? [
"No recognized Go build-info record was found in the inspected image; absent metadata does not establish that the binary is not Go.",
]
: []),
...(module !== null && module.main === null
? [
"Dependency coverage is unknown because no main-module record was decoded from the embedded module information, as in GOPATH-mode builds. Decoded dependency records remain visible; an empty list does not establish the absence of dependencies.",
]
: []),
...(module?.complete === false
? [
"Some embedded module-text records could not be parsed completely; their original lines are preserved and additional metadata remains unknown.",
]
: []),
];
/** Stable in-process metadata inspection without subprocesses, temporary files or Go installation. */
export class GoBinaryProvider implements GoBinaryPort {
readonly identity = GO_BINARY_PROVIDER_IDENTITY;
@@ -191,27 +222,7 @@ export class GoBinaryProvider implements GoBinaryPort {
bytes: snapshot.bytes.length,
},
build_info: info === null ? null : { ...info, module },
limitations: [
"Embedded build metadata is observed file content, not proof of compiler identity, dependency authenticity or runtime behavior.",
"Only Go build information is inspected; function names, source mappings, type metadata and decompiled code are not recovered.",
"Resource guards bound stable artifact reads and cumulative header searches to 256 MiB each, aggregate embedded strings to 1 MiB, structural table decoding to 16 MiB and complete metadata payloads to 8 MiB; these are not operating-system memory or CPU limits.",
...(info !== null &&
(info.go_version === null || info.module_text === null)
? [
"Some embedded Go strings contain non-UTF8 bytes; undecodable text is null and exact source bytes are preserved as base64.",
]
: []),
...(info === null
? [
"No recognized Go build-info record was found in the inspected image; absent metadata does not establish that the binary is not Go.",
]
: []),
...(module?.complete === false
? [
"Some embedded module-text records could not be parsed completely; their original lines are preserved and additional metadata remains unknown.",
]
: []),
],
limitations: limitationsFor(info, module),
};
if (Buffer.byteLength(JSON.stringify(report), "utf8") > OUTPUT_BYTES)
throw outputLimit(input.path);
+30
View File
@@ -284,3 +284,33 @@ cliTest(
);
},
);
cliTest(
"returns unknown dependency coverage for parse-complete GOPATH metadata in CLI Evidence",
async ({ cli }) => {
const moduleText =
"path\texample.com/tool/cmd/tool\n" +
"build\t-compiler=gc\n" +
"build\tGOOS=linux\n";
const root = await createTestTempDirectory("rea-go-gopath-cli-");
const path = join(root, "application.elf");
await writeFile(path, createGoBinaryFixture({ moduleText }).bytes);
const result = await cli.run({
arguments: ["inspect-go-binary", path, "--json"],
});
expect(result.exitCode, result.stderr).toBe(0);
const evidence = toolContract("inspect_go_binary").outputSchema.parse(
result.json,
);
expect(evidence.normalized_result.build_info?.module).toMatchObject({
path: "example.com/tool/cmd/tool",
main: null,
dependencies: [],
complete: true,
unparsed_lines: [],
});
expect(evidence.limitations.join(" ")).toContain(
"Dependency coverage is unknown",
);
},
);
@@ -82,3 +82,47 @@ it.skipIf(process.platform === "win32")(
if (!result.ok) expect(result.error).toBeInstanceOf(AnalysisInputError);
},
);
it("reports a parse-complete GOPATH module text as unknown dependency coverage", async () => {
const moduleText =
"path\texample.com/tool/cmd/tool\n" +
"build\t-compiler=gc\n" +
"build\tGOOS=linux\n";
const root = await createTestTempDirectory("rea-go-gopath-metadata-");
const path = join(root, "application.elf");
await writeFile(path, createGoBinaryFixture({ moduleText }).bytes);
const result = await new GoBinaryProvider().inspect({ path });
if (!result.ok) throw result.error;
expect(result.value.build_info?.module_text).toBe(moduleText);
expect(result.value.build_info?.module).toMatchObject({
path: "example.com/tool/cmd/tool",
main: null,
dependencies: [],
complete: true,
unparsed_lines: [],
});
expect(result.value.limitations.join(" ")).toContain(
"Dependency coverage is unknown",
);
});
it("keeps a module-mode build with no dependency records as an observed empty list", async () => {
const moduleText =
"path\texample.com/tool\n" +
"mod\texample.com/tool\t(devel)\t\n" +
"build\tGOOS=linux\n";
const root = await createTestTempDirectory("rea-go-empty-module-deps-");
const path = join(root, "application.elf");
await writeFile(path, createGoBinaryFixture({ moduleText }).bytes);
const result = await new GoBinaryProvider().inspect({ path });
if (!result.ok) throw result.error;
expect(result.value.build_info?.module_text).toBe(moduleText);
expect(result.value.build_info?.module).toMatchObject({
main: { path: "example.com/tool", version: "(devel)" },
dependencies: [],
complete: true,
});
expect(result.value.limitations.join(" ")).not.toContain(
"Dependency coverage is unknown",
);
});