Skip to content

feat: improve file type detection; consolidate file content functionality - #1246

Merged
egibs merged 5 commits into
chainguard-dev:mainfrom
egibs:improve-file-type-determination
Dec 2, 2025
Merged

feat: improve file type detection; consolidate file content functionality#1246
egibs merged 5 commits into
chainguard-dev:mainfrom
egibs:improve-file-type-determination

Conversation

@egibs

@egibs egibs commented Dec 1, 2025

Copy link
Copy Markdown
Member

Follow-up for #1234

This PR improves the filetype detection by scanning the entirety of a given file instead of a limited subset (previously 512 bytes). There are cases when a file's magic bytes are located near the end of the file or at arbitrary locations within it which means a partial approach won't be adequate in all cases.

As opposed to the previous PR, this PR maintains the separate programkind.go and scan.go retrievals since we may not end up generating a file report once we determine a file's type (e.g., we may scan a file and subsequently filter it if the risk falls below the configured threshold).

If we do end up generating a report, we'll retrieve the file contents again.

The alternatives here are:

  • retrieve the contents and pass the results into File()
    • doing this would require retrieving file contents anywhere we determine a file's type
  • return the contents from File() and use the results where necessary
    • this was the approach in the previous PR and was relatively messy given that we only use the contents in one place outside of File()

Both would require updates in several places, so this is the best compromise between cleanliness and performance. I ran make refresh-sample-testdata on a loop for a while and each run took 28-32 seconds which is about what we'd expect.

I also added some more logic for NPM lockfiles, specifically:

  • package-lock.json
  • pnpm-lock.yaml
  • pnpm-workspace.yaml
  • yarn.lock

Previously, these files would be considered data files and would not be scanned unless --all was used.

egibs added 2 commits December 1, 2025 16:44
…lity

Signed-off-by: egibs <20933572+egibs@users.noreply.github.com>
Signed-off-by: egibs <20933572+egibs@users.noreply.github.com>
Comment thread pkg/action/scan.go
egibs added 3 commits December 2, 2025 08:29
Signed-off-by: egibs <20933572+egibs@users.noreply.github.com>
Signed-off-by: egibs <20933572+egibs@users.noreply.github.com>

@stevebeattie stevebeattie left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all LGTM, thanks!

Comment on lines +210 to +226
// typically, JSON and YAML files are data files only scanned via --all, but we want to support the NPM ecosystem
if strings.HasSuffix(path, "package.json") || strings.HasSuffix(path, "package-lock.json") {
return &FileType{
Ext: ext,
MIME: "application/json",
}
}

if strings.HasSuffix(path, "pnpm-lock.yaml") ||
strings.HasSuffix(path, "pnpm-workspace.yaml") ||
strings.HasSuffix(path, "yarn.lock") {
return &FileType{
Ext: ext,
MIME: "application/x-yaml",
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker on this PR, but would it be better to split this out into a separate function; this would (a) isolate future changes for the ecosystem, as they decide on different tools, (b) you could have unit tests for it, and (c) if we decide to add similar functionality for other language ecosystems, it will give us a similar pattern to follow.

Comment thread pkg/action/scan.go
Comment on lines +138 to +140
// create a buffer sized to the minimum of the file's size or the default ReadBuffer
// only do so if we actually need to retrieve the file's contents
buf := readPool.Get(min(size, file.ReadBuffer)) //nolint:nilaway // the buffer pool is created above

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed for this PR (since this is not an issue added by this PR), but is the thing the linter whinging about here about failing to catch the potential failure of the pool Get() call, say in a resource constrained environment?

@egibs egibs Dec 2, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That could be the case. The possibility of false positives when running nilaway is documented so it's likely not detecting that the pool is initialized previously (this is a common theme across the codebase). There could be cases where readPool may still be nil post-initialization but we always attempt this before ever running Get().

@egibs
egibs merged commit 9319847 into chainguard-dev:main Dec 2, 2025
14 checks passed
@egibs
egibs deleted the improve-file-type-determination branch December 2, 2025 22:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants