Add corgea scan --include-image to scan fully built container images - #153
Open
Ibrahimrahhal wants to merge 5 commits into
Open
Add corgea scan --include-image to scan fully built container images#153Ibrahimrahhal wants to merge 5 commits into
corgea scan --include-image to scan fully built container images#153Ibrahimrahhal wants to merge 5 commits into
Conversation
corgea scan --include-image <image:tag> exports each image with docker (or podman) into corgea-image-scanning-<name>-<tag>.tar, bundles the archives with the uploaded project zip, and lets the backend scan the fully baked images instead of rediscovering base images from Dockerfiles and Compose files. Co-authored-by: ibrahim <ibrahim@corgea.com>
Ibrahimrahhal
marked this pull request as ready for review
August 12, 2026 06:41
There was a problem hiding this comment.
Automated review risk: 4/5.
High risk: image staging can expose or leak large archives, and image-only scans can incorrectly fail. Tests also break on Windows.
Critical or high-priority changes must be addressed.
Automatic approval was not submitted: automated review found critical or high-priority findings.
juangaitanv
reviewed
Aug 12, 2026
Archive names were compared case-sensitively, so myapp:V1 and myapp:v1 — distinct images, since tags allow uppercase — exported over each other on a case-insensitive filesystem and both scanned as the same image. Also gates the two tests that execute the POSIX stub engine to Unix. Windows can't run that script through Command::new, so cargo test failed there; CI never caught it because the Windows jobs only build wheels. Co-authored-by: ibrahim <ibrahim@corgea.com>
- Stage in an owner-only directory with an unguessable name. create_dir_all under a 0022 umask left the project zip and any exported images readable by other local users, and a fixed /tmp/corgea parent can be pre-created, or pointed elsewhere by a symlink, by another user first. tempfile creates directories with default permissions, so owner-only is requested explicitly. - Write image archives with ZIP64 headers: without large_file the zip writer aborts an entry once it passes 4 GiB, which exported images reach routinely. - Exclude corgea-image-scanning-*.tar found in the repository, so a committed copy cannot put the backend into image-scanning mode on scans that never asked for it. - Treat an exported image as sufficient input. A target that matches nothing — a clean tree under --only-uncommitted — now warns and scans the image instead of exporting it and then failing on the empty target. - Delete staging on every exit after the export, not only on upload failure. Co-authored-by: ibrahim <ibrahim@corgea.com>
Co-authored-by: ibrahim <ibrahim@corgea.com>
Conflict in the blast staging block: main (#150) dropped the get_repo_info call that sat after it, replacing it with the repo_before/repo_after pair that feeds reconcile_repo_info_for_upload. Kept this branch's create_private_temp_dir and main's new repo-state handling, and removed the now-stale repo_info binding.
| .map(|archive| (archive.path.clone(), archive.archive_name.clone())) | ||
| .collect(); | ||
|
|
||
| let stop_signal = Arc::new(Mutex::new(false)); |
There was a problem hiding this comment.
🧹 Quality - The code repeatedly performs spinner stop, join, terminal reset, and cleanup logic across multiple error branches after printing the "Packaging your project..." spinner. This duplicated code risks inconsistency and harder maintenance. View in Corgea ↗
More Details
🎟️Issue Explanation: The code repeatedly performs spinner stop, join, terminal reset, and cleanup logic across multiple error branches after printing the "Packaging your project..." spinner. This duplicated code risks inconsistency and harder maintenance.
- Duplication in spinner lifecycle management across error paths can cause inconsistent terminal states, e.g., missed resets in "stop spinner + join + reset terminal + cleanup" steps.
- Makes code harder to maintain and update since any change requires modifying multiple similar blocks dealing with spinner handling post validation in "packaging_thread".
- Increases cognitive load for the team when reading or debugging the packaging process as spinner logic is scattered rather than encapsulated in a clear RAII pattern or helper function.
We could not generate a fix for this.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Container scanning today only sees the base images referenced by the source tree (Dockerfile
FROM, Composeimage:). This adds a way to scan the image a build actually produces.docker build -t myapp:1.2.3 . corgea scan --include-image myapp:1.2.3 --include-image ghcr.io/acme/api:latestFor each
--include-imagevalue the CLI exports the image withdocker save(orpodman save) intocorgea-image-scanning-<name>-<tag>.tar, stages it in an owner-only temp directory, and adds it to the project zip that gets uploaded. Fusion greps the extracted bundle forcorgea-image-scanning-*.tar; when it finds any it scans those archives and skips base-image discovery (companion PR infusion).Behavior details
--metadata. Rejected forsemgrep/snyk.-are hard errors, so nothing user-supplied can turn into a flag for the container CLI.<engine> image inspectdecides; on a miss the CLI runs<engine> pullwith inherited stdio so the pull progress is visible, and a failure names the image and points at building/pulling and registry login.docker, thenpodman, else an actionable error.CORGEA_CONTAINER_ENGINEoverrides it (also how the tests inject a stub CLI).repository[:tag][@digest], treats a colon in the registry host as a port (registry:5000/team/app), defaults an untagged reference tolatest, sanitizes to[A-Za-z0-9._-], caps the file name at 200 chars, and suffixes any name two references collapse onto — compared case-insensitively, since tags may carry uppercase and a case-insensitive filesystem would otherwise exportmyapp:V1overmyapp:v1. The name is also what Fusion labels findings with, so it stays readable:myapp:1.2.3becomesmyapp-1.2.3.--only-uncommitted, say — the scan warns and covers just the image instead of failing after the export.corgea-image-scanning-*.tarcommitted to the repository is excluded from the bundle, so it can't put the backend into image-scanning mode on scans that never asked for it.create_zip_from_targettakes anextra_files: &[(PathBuf, String)]list of(staged path, zip entry name)pairs written to the zip root, withlarge_file(true)so entries past 4 GiB get ZIP64 headers. Exclude globs deliberately don't apply to them — these files are there because the user named them on the command line.Review follow-ups
All findings from the automated reviews and @juangaitanv are addressed:
large_file(true)on the extras'FileOptions;#[ignore]d test writes a sparse 4 GiB filecreate_private_temp_dir: owner-only mode requested explicitly, random namedelete_directoryon the zero-target, resolve-error and zip-error exitsunique_archive_name#[cfg(unix)]on both tests and the stub helperTwo notes on the staging fix.
tempfiledocuments that temporary directories are created with default permissions (only files are private), soBuilder::tempdir()alone would still be world-readable — the mode is passed explicitly, and a unit test asserts0700. And the suggestedTempDirRAII guard can't work as-is:blast::runends the process throughstd::process::exitin ~30 places, which skips destructors, so cleanup stays explicit and the path is taken withkeep().tempfilemoved from3.12to3.20forTempDir::keep; the lockfile already resolved to 3.23, so no lock change.Type of Change
Testing Method & Results
./harness checkis green (685 tests). Coverage:src/images.rs: archive naming (tag, digest, registry port, untagged, length cap), reference validation/de-duplication, export against a stub container CLI, collision suffixing including case-only, and a failing-CLI error.src/utils/generic.rs: extras land at the zip root under the requested entry name; staging directory is0700; and an#[ignore]d ZIP64 test (cargo test --bin corgea -- --ignored four_gib, ~60s). I confirmed that test fails with "Large file option has not been set" before the fix and passes after.tests/cli_scan_include_image.rs(e2e, real binary + stubbed API + stubbed container CLI): both archives bundled into the uploaded zip and named in the output; nothing bundled without the flag; an unavailable image exits 1 without uploading; a bad reference exits 1;--only-uncommittedon a committed, clean tree still uploads the archive; a checked-in archive is not bundled while source files still are.Manual checks: ran the built binary against a stub upload server with a stub engine standing in for
docker save. The uploaded zip containedmain.py,Dockerfile, and bothcorgea-image-scanning-*.tarentries, and feeding the extracted bundle to Fusion's container step scanned both archives without touching skopeo or the Dockerfile'salpine:3.19. Separately confirmed that a resolve error after an export now leaves nocorgea-scan-*directory behind.Dependency
fusionPR to be deployed for the archives to be scanned. Until then the archives are uploaded and ignored.container_scanscan config); the CLI can't turn it on.