Skip to content

Commit

Permalink
fix: handle Go binaries from the Go distributions
Browse files Browse the repository at this point in the history
Binaries that are built in the Go distribution (e.g. the Go compiler,
`gofmt`, `vet` and many more) do not have a `mod` directive like
"normal" Go binaries. This seems to be a special case for all binaries
from the Go source / standard library. So far, our parsing logic
couldn't handle the missing `mod` directive, which means that all these
binaries currently have the same project name `-compiler=gc`.

This commit fixes that behaviour by naming the binaries with a fixed
prefix `go-stdlib@` plus the path that we can extract from the `path`
directive in the build metadata. This results in names like
`go-stdlib@cmd/vet`.

Note that this is only relevant for the CLI output, a `snyk container
monitor` does not seem to use the project name from the CLI, but rather
the path.
  • Loading branch information
tommyknows committed Sep 28, 2022
1 parent abd3f57 commit c5f889d
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 13 deletions.
14 changes: 12 additions & 2 deletions lib/go-parser/go-binary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,18 @@ export function extractModuleInformation(
throw Error("binary contains empty module info");
}

const [, mainModuleLine, ...versionsLines] = mod.split("\n");
const [, name] = mainModuleLine.split("\t");
const [pathDirective, mainModuleLine, ...versionsLines] = mod.split("\n");
const lineSplit = mainModuleLine.split("\t");
let name = lineSplit[1];
if (lineSplit[0] !== "mod") {
// If the binary has no mod directive, it is a binary from the Go
// distribution, like the "go" command, "vet", "gofmt" or others. In that
// case, we use "go-distribution@" plus the path directive ("cmd/vet" for
// example) as the name. Using the "@" ensures that customers cannot create
// name-clashes with these as "@" is an invalid character in Go modules.
name = "go-distribution@" + pathDirective.split("\t")[1];
}

const modules: GoModule[] = [];
versionsLines.forEach((versionLine) => {
const [, name, ver] = versionLine.split("\t");
Expand Down
Binary file added test/fixtures/go-binaries/stdlib_pack
Binary file not shown.
36 changes: 25 additions & 11 deletions test/unit/go-binaries.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ function stdlib(
break;

case GoVersion.Go118:
case undefined:
lib.packages.set("unicode", ["casetables.go"]);
break;
}
Expand Down Expand Up @@ -239,7 +240,7 @@ function extractGoVersion(fileName: string): GoVersion {
return GoVersion.Go116;
case "go1.18":
return GoVersion.Go118;
case "latest":
default:
// if the test fails because we're using GoVersion.Go118 here, it means
// that the binary format has changed and we need to introduce a new
// version + check how to handle that.
Expand All @@ -248,22 +249,20 @@ function extractGoVersion(fileName: string): GoVersion {
}

describe("test from binaries", () => {
const files = readdirSync(path.join(__dirname, "../fixtures/go-binaries"), {
withFileTypes: true,
});
const files = readdirSync(path.join(__dirname, "../fixtures/go-binaries"));
for (const file of files) {
if (!file.isFile()) {
if (!file.match(/^go1\.[0-9]{1,2}\.[0-9]{1,2}_.*/)) {
continue;
}

describe(`handles file ${file.name}`, () => {
describe(`handles file ${file}`, () => {
const fileContent = readFileSync(
path.join(__dirname, "../fixtures/go-binaries/", file.name),
path.join(__dirname, "../fixtures/go-binaries/", file),
);
const goVersion = extractGoVersion(file.name);
const isTrimmed = file.name.includes("trimmed");
const isVendored = file.name.includes("vendored");
const isCGo = file.name.includes("cgo");
const goVersion = extractGoVersion(file);
const isTrimmed = file.includes("trimmed");
const isVendored = file.includes("vendored");
const isCGo = file.includes("cgo");

const testCase = new TestCase([
stdlib(goVersion, isCGo, isTrimmed),
Expand Down Expand Up @@ -393,3 +392,18 @@ describe("test from binaries", () => {
});
}
});

describe("test stdlib bin project name", () => {
it("has correct project name even if mod directive is missing", () => {
const goBin = new GoBinary(
elf.parse(
readFileSync(
path.join(__dirname, "../fixtures/go-binaries/stdlib_pack"),
),
),
);
expect(goBin.name).toBe("go-distribution@cmd/pack");
// binaries from the standard library usually don't have external deps.
expect(goBin.modules).toHaveLength(0);
});
});

0 comments on commit c5f889d

Please sign in to comment.