Skip to content

PM-4777: Add binary scanning for Java artifacts (JAR, WAR, EAR, Spring Boot) - #524

Open
dineshmistry wants to merge 1 commit into
cycodehq:mainfrom
dineshmistry:PM-4777-binary-composition-analysis
Open

PM-4777: Add binary scanning for Java artifacts (JAR, WAR, EAR, Spring Boot)#524
dineshmistry wants to merge 1 commit into
cycodehq:mainfrom
dineshmistry:PM-4777-binary-composition-analysis

Conversation

@dineshmistry

Copy link
Copy Markdown

What

Adds binary composition analysis to the CLI: point it at a built Java artifact and get the same SCA findings a source scan gives, plus an explicit account of what could not be identified.

cycode scan -t sca binary app.war
cycode scan -t sca binary dist/
cycode report sbom --format cyclonedx-1.4-json binary app.ear

Supported: JAR, WAR, EAR, Spring Boot fat JAR, nested archives to --max-depth (default 3).

How it works

  1. A hardened zip reader walks the archive in memory. Only nested archives and Maven metadata entries are read; class files and resources are never touched, and archive bytes are never uploaded or written to disk.
  2. Each library is identified by a ladder: META-INF/maven/**/pom.properties (exact) → optional Maven Central SHA-1 lookup with --maven-central (exact, opt-in, sends the hash only) → MANIFEST.MF attributes when they are shaped like real coordinates and carry a declared group (marked ambiguous, never gates the exit code) → otherwise unidentified, reported by path, digest and size.
  3. A CycloneDX 1.4 document is synthesised with one component per identified coordinate, cycode:evidence / cycode:confidence / cycode:path properties, and a dependency graph from containment overlaid with real edges from embedded pom.xml.
  4. That document, plus a fixed synthetic pom.xml (the engine routes a bare bom.json to nothing), goes through the existing zip_documentsapi/v4/scans/cli path. Nothing below scan_documents changes.

Product stance

  • No guessing. A coordinate comes from metadata the build wrote or from an exact hash match. A filename or a manifest banner never becomes a component. A wrong coordinate produces a wrong CVE list.
  • Unidentified is a first-class result. Listed in the console and under binary.unidentified in JSON, with a coverage line that is always printed and always true: 9 identified (2 low confidence) | 7 unidentified | 23 vulnerabilities.
  • Declared is not shipped. Embedded poms draw edges between components that were actually found; they do not add components. Source scanning remains the tool for the full dependency graph, and the README says so.

Options

Flag Default
--max-depth 3 Nested-archive recursion limit
--maven-central off Resolve unidentified archives by SHA-1 on search.maven.org (hash only). Refused with --offline
--offline off Embedded metadata only; acknowledges and silences the partial-coverage warning
--project-name inferred Platform identity when detached from a Git checkout; required for --monitor on a bare filename
--keep-bom off Write the synthesised document beside each artifact
--include-binaries off On scan path, also extract Java archives met during the walk

Security hardening in the reader

Zip-slip and absolute/drive/UNC paths, symlinks and non-regular entries, per-entry and total size limits, compression-ratio and entry-count bombs, truncated central directories, and duplicate entry names (Python's ZipFile.open(name) returns the last duplicate; every entry is read from its own ZipInfo so a vulnerable jar cannot hide behind a patched one with the same name). Embedded pom.xml is refused if it carries a DOCTYPE or ENTITY declaration, which closes entity expansion without adding defusedxml. Console output from entry names is stripped of rich markup, control characters and unbounded length.

Verification

  • 1,245 tests pass; ~94% coverage on the new modules; ruff check and ruff format --check clean; Python 3.9 floor respected; all HTTP in tests mocked with responses, including the Maven Central client.
  • Positive control before every corpus run: a WAR built from real Maven Central jars yields 23 findings including CVE-2021-44228.
  • Real vendor corpus (16 jars including SAP- and Oracle-licensed builds) scanned with and without lookup and cross-checked against Checkmarx, Trivy and Syft/Grype. On files physically present all four tools agree.

Not in this PR

  • Cycode-side digest index (Tier 2 proper): behind the DigestResolver seam; needs a backend endpoint that does not exist yet.
  • Shaded/relocated classes: no metadata survives; documented as a limitation.
  • .NET, npm, containers: the BinaryExtractor interface is ready; Java only ships now.
  • CycloneDX schema validation: would need jsonschema; a structural validator ships instead.
  • Pre-existing on main, not touched: report sbom prints "Report saved to" and writes no file (reproduces on report sbom path .).

Note on the ticket prefix

Branch and title use the product ticket PM-4777. If a CM- ticket should own this, the title and branch are a rename away.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CmShLt7zGmitrn35FBPMw9

…g Boot)

Adds `cycode scan -t sca binary <path>` and `cycode report sbom binary <path>`.
The CLI opens a Java archive locally, identifies the open-source components
inside it from embedded Maven metadata (pom.properties, then optionally a
Maven Central SHA-1 lookup, then MANIFEST.MF), synthesises a CycloneDX 1.4
document, and feeds it to the existing SCA scan path. The archive itself is
never uploaded.

What ships:
- Hardened zip reader (zip-slip, symlinks, absolute paths, compression and
  entry-count bombs, duplicate entry names read by central-directory record).
- Identification ladder with an explicit `unidentified` result; no coordinate
  is ever guessed from a filename or an unshaped manifest value.
- Dependency graph from containment plus real edges from embedded pom.xml
  (parsed with a DOCTYPE/ENTITY guard, no defusedxml needed).
- `--max-depth`, `--offline`, `--maven-central` (opt-in, sends the hash only),
  `--project-name`, `--keep-bom`, `--include-binaries`.
- Coverage line and JSON fields so CI can gate on identification, not guess.
- `DigestResolver` seam so the Cycode backend digest index can replace the
  Maven Central implementation without caller changes.

No new runtime dependency. All HTTP in tests is mocked with `responses`.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

self._check_compression_ratio(entry, read_bytes)
self._budget.consume(len(chunk))

sha1.update(chunk)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cycode: SAST violation: 'Usage of weak hashing library (SHA-1)'.

Risk Score: 65 (MEDIUM)
Severity: Medium

Description

Using a weak hashing library like SHA-1 increases the risk of data breaches. SHA-1 in particular is vulnerable to collision attacks, where two different inputs can produce the same hash value, compromising data integrity and security.

Cycode Remediation Guideline

✅ Do


  • Do opt for stronger hashing algorithms such as SHA-256 to enhance security.
    hashlib.sha256('password').digest()

❌ Don't


  • Do not use SHA-1 for hashing. It is no longer considered secure due to its vulnerability to collision attacks.
    hashlib.sha1('password').digest() # unsafe

🎥 Learning materials (by Secure Code Warrior)


Tell us how you wish to proceed using one of the following commands:

Tag Short Description
#cycode_sast_false_positive <reason> Mark as false positive — applies to this violation only
#cycode_sast_ignore_here <reason> Ignore this violation — applies to this violation only
#cycode_ai_remediation Request remediation guidance using Cycode AI

⚠️ When commenting on Github, you may need to refresh the page to see the latest updates.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

#cycode_sast_ignore_here

Intentional, non-cryptographic use. SHA-1 is the digest Maven Central and every artifact index key on, so it is the only algorithm a coordinate lookup can use; the call passes usedforsecurity=False, and SHA-256 is computed alongside and emitted in the BOM for anyone who wants a strong digest. Nothing here relies on SHA-1 for integrity or security.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

👎 This user is not permitted to perform actions.

if not chunk:
break

digest.update(chunk)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cycode: SAST violation: 'Usage of weak hashing library (SHA-1)'.

Risk Score: 65 (MEDIUM)
Severity: Medium

Description

Using a weak hashing library like SHA-1 increases the risk of data breaches. SHA-1 in particular is vulnerable to collision attacks, where two different inputs can produce the same hash value, compromising data integrity and security.

Cycode Remediation Guideline

✅ Do


  • Do opt for stronger hashing algorithms such as SHA-256 to enhance security.
    hashlib.sha256('password').digest()

❌ Don't


  • Do not use SHA-1 for hashing. It is no longer considered secure due to its vulnerability to collision attacks.
    hashlib.sha1('password').digest() # unsafe

🎥 Learning materials (by Secure Code Warrior)


Tell us how you wish to proceed using one of the following commands:

Tag Short Description
#cycode_sast_false_positive <reason> Mark as false positive — applies to this violation only
#cycode_sast_ignore_here <reason> Ignore this violation — applies to this violation only
#cycode_ai_remediation Request remediation guidance using Cycode AI

⚠️ When commenting on Github, you may need to refresh the page to see the latest updates.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

#cycode_sast_ignore_here

Intentional, non-cryptographic use. SHA-1 is the digest Maven Central and every artifact index key on, so it is the only algorithm a coordinate lookup can use; the call passes usedforsecurity=False, and SHA-256 is computed alongside and emitted in the BOM for anyone who wants a strong digest. Nothing here relies on SHA-1 for integrity or security.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

👎 This user is not permitted to perform actions.

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.

1 participant