Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion docs/docs/target/container_image.md
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,14 @@ $ trivy image --podman-host /run/user/1000/podman/podman.sock YOUR_IMAGE
```

### Prevent scanning oversized container images
Use the `--max-image-size` flag to avoid scanning images that exceed a specified size. The size is specified in a human-readable format (e.g., `100MB`, `10GB`). If the compressed image size exceeds the specified threshold, an error is returned immediately. Otherwise, all layers are pulled, stored in a temporary folder, and their uncompressed size is verified before scanning. Temporary layers are always cleaned up, even after a successful scan.
Use the `--max-image-size` flag to avoid scanning images that exceed a specified size. The size is specified in a human-readable format (e.g., `100MB`, `10GB`).

An error is returned in the following cases:
- if the compressed image size exceeds the limit,
Copy link
Collaborator

Choose a reason for hiding this comment

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

For me, the most significant difference between markdown and mkdocs is that a newline is required before bullet points.

Suggested change
An error is returned in the following cases:
- if the compressed image size exceeds the limit,
An error is returned in the following cases:
- if the compressed image size exceeds the limit,

It's broken now.

CleanShot 2025-01-29 at 10 04 09

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed 92888b6

- if the total size of the layers exceeds the specified limit during their pulling,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
- if the total size of the layers exceeds the specified limit during their pulling,
- if the total size of the uncompressed layers exceeds the specified limit during their pulling,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done 92888b6

- if the uncompressed image size exceeds the limit after the layers are pulled.

The layers are pulled into a temporary folder during their pulling and are always cleaned up, even after a successful scan.

!!! warning "EXPERIMENTAL"
This feature might change without preserving backwards compatibility.
Expand Down
18 changes: 18 additions & 0 deletions pkg/fanal/artifact/image/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ func (a Artifact) checkImageSize(ctx context.Context, diffIDs []string) error {

imageSize, err := a.imageSize(ctx, diffIDs)
if err != nil {
var uerr *trivyTypes.UserError
if errors.As(err, &uerr) {
return uerr
}
return xerrors.Errorf("failed to calculate image size: %w", err)
}

Expand Down Expand Up @@ -281,6 +285,7 @@ func (a Artifact) compressedImageSize(diffIDs []string) (int64, error) {

func (a Artifact) imageSize(ctx context.Context, diffIDs []string) (int64, error) {
var imageSize int64
var completedLayers int

p := parallel.NewPipeline(a.artifactOption.Parallel, false, diffIDs,
func(_ context.Context, diffID string) (int64, error) {
Expand All @@ -292,6 +297,19 @@ func (a Artifact) imageSize(ctx context.Context, diffIDs []string) (int64, error
},
func(layerSize int64) error {
imageSize += layerSize
completedLayers += 1
// If the layer size exceeds the limit during loading, return an error immediately.
// Otherwise, if the image is fully loaded, check the size later.
if imageSize > a.artifactOption.ImageOption.MaxImageSize &&
completedLayers != len(diffIDs) {
return &trivyTypes.UserError{
Message: fmt.Sprintf(
"the accumulated size of uncompressed layers %s exceeds maximum allowed size %s",
units.HumanSizeWithPrecision(float64(imageSize), 3),
units.HumanSize(float64(a.artifactOption.ImageOption.MaxImageSize)),
),
}
}
return nil
},
)
Expand Down
14 changes: 11 additions & 3 deletions pkg/fanal/artifact/image/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2255,11 +2255,19 @@ func TestArtifact_Inspect(t *testing.T) {
},
{
name: "sad path, image size is larger than the maximum",
imagePath: "../../test/testdata/alpine-311.tar.gz",
imagePath: "../../test/testdata/image2.tar",
Copy link
Contributor

Choose a reason for hiding this comment

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

I seem to be missing something
Why are we using an existing image archive (e.g. vuln-image.tar.gz)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I created image2 as it is convenient for testing because it contains two 2mb layers.

Copy link
Contributor

Choose a reason for hiding this comment

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

But we only test 2 cases:

  • 1st layer size > MaxImageSize.
  • Image size > MaxImageSize.

Why do we need the 2nd layer?

We just don't want to add extra test files (there are too many of them anyway)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The layers are processed in parallel and if a small layer is processed first, we will not check the first case.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure if this makes sense for this test, but okay, let's keep this file.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Docker: Decimal prefixes (1000)
Docker Hub: Binary prefixes (1024)

Copy link
Collaborator

Choose a reason for hiding this comment

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

@nikpivkin It may be worth documenting. Otherwise, users who check the size on Docker Hub may complain about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

BTW I found an open issue docker/cli#4630

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Docker: Decimal prefixes (1000)
Docker Hub: Binary prefixes (1024)

Doesn't the Docker CLI display the actual binary size but uses decimal prefixes like Docker Hub?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added e5574ca

artifactOpt: artifact.Option{
ImageOption: types.ImageOptions{MaxImageSize: units.MB * 4.1},
Copy link
Contributor

Choose a reason for hiding this comment

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

It's weird, but my IDE complains 😄
изображение

Copy link
Contributor Author

Choose a reason for hiding this comment

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

GoLand? I think the static analyzer should not show the warning because 4.1*MB is an integer.
https://go.dev/play/p/M1aB0vKfVl1

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, i agree with you.
Just wanted to share 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Strange that the behavior is different from vscode, I think they use the same lsp.

},
wantErr: "uncompressed image size 4.2MB exceeds maximum allowed size 4.1MB",
},
{
name: "sad path, the first layer is larger than the threshold",
imagePath: "../../test/testdata/image2.tar",
artifactOpt: artifact.Option{
ImageOption: types.ImageOptions{MaxImageSize: units.MB * 4},
ImageOption: types.ImageOptions{MaxImageSize: units.MB * 1},
},
wantErr: "uncompressed image size 5.86MB exceeds maximum allowed size 4MB",
wantErr: "the accumulated size of uncompressed layers 2.1MB exceeds maximum allowed size 1MB",
},
}
for _, tt := range tests {
Expand Down
Binary file added pkg/fanal/test/testdata/image2.tar
Binary file not shown.
Loading