Skip to content

Add cryptographic algorithm scanning and CycloneDX 1.7 CBOM output#1687

Draft
CortezFrazierJr wants to merge 18 commits intomasterfrom
feature/crypto-scanning
Draft

Add cryptographic algorithm scanning and CycloneDX 1.7 CBOM output#1687
CortezFrazierJr wants to merge 18 commits intomasterfrom
feature/crypto-scanning

Conversation

@CortezFrazierJr
Copy link
Copy Markdown

Summary

Add CBOM (Cryptographic Bill of Materials) support to fossa-cli:

  • Cryptographic algorithm detection via regex + AST patterns across 15+ ecosystems
  • CycloneDX 1.7 CBOM output format
  • New fossa cbom command for standalone crypto scanning
  • Integration with existing fossa analyze workflow

Migration from fork-based PR

This PR replaces #1656 which was opened from a personal fork (CortezFrazierJr/fossa-cli). Tests couldn't run on the fork-based PR (per Zach's feedback). This PR is from a branch on the org repo so CI will run.

All 7 rounds of CodeRabbit review feedback from #1656 have been addressed in this branch. The commit history is preserved.

Previous PR: #1656

Test plan

  • CI tests run (the primary goal of this migration)
  • CodeRabbit re-reviews on this PR
  • Existing fossa-cli test suite passes
  • Manual verification of fossa cbom command

🤖 Generated with Claude Code

@CortezFrazierJr CortezFrazierJr requested a review from a team as a code owner April 9, 2026 23:30
@CortezFrazierJr CortezFrazierJr requested a review from nficca April 9, 2026 23:30
@CortezFrazierJr CortezFrazierJr marked this pull request as draft April 9, 2026 23:34
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 9, 2026

Walkthrough

Added a new Rust binary crate extlib/cryptoscan implementing multi-ecosystem crypto algorithm detection, FIPS classification, and CycloneDX CBOM generation. Introduced Haskell integration modules and types to run the embedded scanner, export CBOMs, and render a FIPS compliance report; added CLI options --x-crypto-scan, --crypto-cbom-output, and --crypto-fips-report. Documentation pages describe the feature and experimental reference. Test fixtures for multiple languages/ecosystems were added. Build and packaging were updated (Makefile, Cargo workspace, Cabal, embedded-binary wiring) to include the cryptoscan binary and related modules.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description covers summary, test plan, and references, but lacks several required template sections including Overview, Acceptance criteria, detailed Testing plan with concrete steps, Risks, Metrics, and Checklist items are not marked, reducing completeness. Add the missing template sections: provide explicit Overview explaining intent, Acceptance criteria for user impact, detailed Testing plan with concrete steps, Risks assessment, Metrics guidance, and verify/update all Checklist items.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly describes the primary change: adding cryptographic algorithm scanning and CycloneDX 1.7 CBOM output, which aligns with the main objectives documented in the PR description.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 19

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/features/crypto-scanning.md`:
- Around line 53-65: The docs claim "No source code content is sent to FOSSA"
but the finding model still includes matched_text / cryptoFindingMatchedText;
update the upload flow to strip/redact that field before emission or update the
docs to remove the absolute privacy claim. Specifically, in the code path that
serializes/uploads findings (the function that assembles the FOSSA payload / the
upload/sendToFossa/exportFindings routine), remove or replace
cryptoFindingMatchedText/matched_text with a redacted placeholder (or omit the
key entirely) prior to sending, and add a unit/integration test to assert the
payload lacks matched_text; alternatively, if you choose docs change, edit the
crypto-scanning.md text to state that matched snippets may be included unless
explicitly redacted.

In `@docs/references/subcommands/analyze.md`:
- Around line 182-183: Update the wording that currently reads "Crypto Scanning
detects cryptographic algorithm usage across 10 language ecosystems" to reflect
the implemented scope—either change it to "15+ language ecosystems" or a neutral
phrase like "multiple language ecosystems" so the docs match the PR scope;
ensure the same change is applied to the repeated instance later in the doc (the
other occurrence of the "10 ecosystems" sentence).

In `@extlib/cryptoscan/src/crypto_algorithm.rs`:
- Around line 35-46: Primitive::KeyWrap currently serializes to "key-wrap" which
doesn't match the Haskell decoder; update the Rust enum serialization for
Primitive::KeyWrap (in crypto_algorithm.rs) to the exact token expected by the
Haskell decoder in src/App/Fossa/CryptoScan/Types.hs (e.g., change the serde
rename from "key-wrap" to the Haskell-accepted string), ensuring the serde
attribute is applied to the KeyWrap variant so both sides are in lockstep; run a
quick round-trip test or fossa analyze to verify decoding succeeds.

In `@extlib/cryptoscan/src/cyclonedx.rs`:
- Around line 161-188: The current dedup logic using seen_locations,
seen_methods, and seen_ecosystems produces nondeterministic order; instead
collect unique values for each of those (e.g., build Vecs of file_path,
detection_method.as_str(), and ecosystem from the loop over group or use a
BTreeSet), sort the collections deterministically, then iterate the sorted lists
to push BomProperty entries (name: "fossa:detected-in",
"fossa:detection-method", "fossa:ecosystem") into properties; update references
to the variables seen_locations/seen_methods/seen_ecosystems (or replace them
with properly named collections) and remove HashSet from the imports.

In `@extlib/cryptoscan/src/fips.rs`:
- Around line 192-197: The DSA-specific branch that returns
FipsStatus::Deprecated currently checks lower.contains("dsa") before detecting
the SLH-DSA case, causing "slh-dsa" to be misclassified; move the SLH-DSA check
so it runs before the generic DSA rule (and apply the same reorder for the
similar case at lines 237-239) so the SLH-DSA-approved branch is matched first;
locate the conditional that uses lower.contains("dsa") and the SLH-DSA condition
and swap their order so SLH-DSA is evaluated prior to the generic DSA check.
- Around line 29-31: The FipsRemediation struct in fips.rs is annotated with
#[allow(dead_code)], which silences a real compiler warning; remove that
attribute and either delete FipsRemediation if it's not needed or make it
used/exported (e.g., remove the allow, add pub if intended public, and wire it
into the public model or create a unit test that constructs/uses
FipsRemediation) so the compiler no longer reports it as dead code; ensure any
serialization derives (Serialize, Deserialize) remain correct when you
reintroduce usage.

In `@extlib/cryptoscan/src/main.rs`:
- Around line 69-78: The code currently forwards a possibly invalid --path into
scanner::scan_project which masks WalkDir errors and yields an empty successful
result; fix by validating the scan root in main before calling
scanner::scan_project: check that cli.path exists and is a directory (e.g., use
std::path::Path::exists()/is_dir() or std::fs::metadata) and if the check fails,
print a clear error (eprintln! or process_logger.error) and exit with a non-zero
code; keep the existing call sites (scanner::detect_ecosystems and
scanner::scan_project) but do not call scanner::scan_project when the path is
invalid so the program fails fast with a visible error instead of producing
empty findings.

In `@extlib/cryptoscan/src/patterns.rs`:
- Around line 7-15: The CryptoPattern currently only has file_extensions which
causes manifest rules to overmatch; add a new field to the CryptoPattern struct
(e.g., file_names: Vec<&'static str> or file_globs: Vec<&'static str>) to allow
exact filename or glob constraints (examples: "package.json", "Cargo.toml",
"*.csproj"), populate those for manifest rules, and update
scanner::pattern_applies to first check the incoming file's basename (and run a
glob match if you chose globs) against the new file_names/file_globs list before
falling back to file_extensions; keep the existing extension-based behavior for
non-manifest rules and for extensionless files ensure pattern_applies still
handles them appropriately.
- Around line 64-68: The current pat(...) rules (e.g., the patterns calling pat
with DetectionMethod::ImportStatement and Confidence::High/Medium) treat
namespace imports as concrete algorithm usages (AES, SHA-256, HKDF); update
these patterns to capture the actual imported symbol (use capturing groups in
the regex, e.g., capture (\w+) or the specific group already present) and then
normalize the captured symbol to a concrete algorithm before emitting an
algorithm-level finding, otherwise emit a library-level finding or downgrade
confidence; specifically, modify the pat invocations that match
"cryptography.hazmat.primitives.ciphers", "hashes", and "kdf" so they either (1)
require and use the captured symbol to map to AES/SHA-256/HKDF when present, or
(2) when only the namespace is imported (no concrete symbol captured) produce a
generic/ library-level finding or lower the Confidence from High to Medium/Low
instead of attributing AES/SHA-256/HKDF directly.

In `@extlib/cryptoscan/src/scanner.rs`:
- Around line 322-325: The match arms in scanner.rs that map generic algorithm
labels ("RSA", "ECDSA", "ECDSA-P256", "ECDSA-P384", "ECDSA-P521", and the
similar arms at the other noted locations) currently populate concrete fields
(parameter_set, curve, and derived security level) for generic detections;
instead, when normalize_detected_algorithm did not extract explicit parameters,
leave those fields empty/None and avoid inventing defaults. Update the tuple
values returned by the affected arms (the tuples constructed with
Primitive::Pke/Primitive::Signature and the following elements) so parameter_set
and curve are None and the security level is None/empty for truly generic
matches rather than inserting "2048", "nist/P-256", Some(128), etc., and do the
same for the other similar arms referenced in the comment.
- Around line 389-406: The deduplication currently returns
HashMap::into_values() which yields non-deterministic iteration order; in
deduplicate_findings collect best.into_values() into a Vec<CryptoFinding> and
sort it deterministically (e.g., by the same composite key used for dedupe:
algorithm.name, file_path, line_number, and if needed tie-break by confidence or
algorithm.name) before returning; update the function to build that sorted Vec
so repeated runs produce stable output.
- Around line 21-25: The auto-detection currently limits WalkDir to three levels
via .max_depth(3), causing detect_ecosystems to miss nested services in
monorepos; remove the .max_depth(3) call from the WalkDir::new(project_path)
chain used by detect_ecosystems (and any related scan_project sampling) so the
directory walk covers the whole repo, keeping the existing .filter_entry(|e|
!should_skip_path(e.path())) and .filter_map(|e| e.ok()) and ensuring
pattern_applies can evaluate files at any depth.

In `@src/App/Fossa/Analyze.hs`:
- Around line 423-430: The CBOM write is performed outside the diagnostic
boundary so failures (e.g., missing parent directory or permission error) can
abort a successful scan; modify the block handling Success _ (Just bytes) so
that before calling BL.writeFile cbomPath bytes you either ensure the parent
directory exists (createDirectoryIfMissing True on the parent of cbomPath)
and/or perform the write inside Diag.errorBoundaryIO/diagToDebug to catch and
log IOExceptions (using Diag.context "crypto-cbom-output" and process the error
via Diag.flushLogs or logError) referencing maybeCbomBytes,
analyzeCryptoScanCBOM, cbomPath and BL.writeFile to locate the code to change.

In `@src/App/Fossa/Analyze/ScanSummary.hs`:
- Line 231: Remove the match guard that checks `| not (null findings) =` in the
crypto rendering code and replace it with a case expression on `findings` so
empty crypto scans are rendered explicitly (or render an empty placeholder)
rather than being silently omitted; keep `srcUnitToScanCount crypto` as-is so
counts remain consistent with displayed items, e.g. locate the branch that
renders `crypto` (it currently uses the guard on `findings`) and change it to
`case findings of [] -> ...; _ -> <existing rendering>` so empty findings are
not hidden.

In `@src/App/Fossa/CryptoScan/FipsReport.hs`:
- Around line 217-223: The mapping for 3DES is missing because the code only
checks for "3des-encrypt" but the scanner emits the canonical name "3DES";
update the conditional in FipsReport.hs (the branch that currently checks
["rc4", "rc2", "blowfish", "des", "3des-encrypt"]) to also include the canonical
"3DES" (and optionally "3des") so that matches return "AES-256" (same
remediation as the existing 3des-encrypt case).

In `@src/App/Fossa/CryptoScan/Types.hs`:
- Around line 13-24: Replace the unqualified imports with explicit, qualified
imports: import Data.Aeson as Aeson (FromJSON(parseJSON), ToJSON(toJSON),
Value(String), object, withObject, withText, (.:), (.:?), (.=)) and import
Data.Text as Text (Text); then update usages in this module to use the Aeson.
and Text. prefixes for the referenced symbols (e.g.,
Aeson.FromJSON/Aeson.parseJSON, Aeson.object, Aeson.(.:), and Text.Text) so the
module follows the repository guideline of explicit, fully-qualified Haskell
imports.
- Around line 188-240: The CryptoPrimitive type and its JSON instances are
missing the key-wrap variant; add a new constructor PrimitiveKeyWrap to the
CryptoPrimitive data declaration and update the FromJSON parseJSON handler to
map the text "key-wrap" -> Pure PrimitiveKeyWrap and the ToJSON toJSON function
to emit String "key-wrap" for PrimitiveKeyWrap so JSON decoding/encoding
recognizes that primitive (update the data type, the parseJSON case list, and
the toJSON match arm accordingly).

In `@test-fixtures/csharp-service/Service.csproj`:
- Line 9: The test fixture declares the Argon2 package as
Isopoh.Cryptography.Argon2 which the C# dependency manifest scanner does not
recognize; change the <PackageReference> in Service.csproj to
Konscious.Security.Cryptography (replacing Isopoh.Cryptography.Argon2) so the
detector emits the intended Argon2 signal, or alternatively update the C#
manifest scanner’s dependency pattern list to include
"Isopoh.Cryptography.Argon2" (whichever you prefer) — target the
PackageReference element in Service.csproj or the detector's dependency pattern
configuration/class to apply the change.

In `@test-fixtures/ruby-web-app/app.rb`:
- Around line 8-40: The cipher name literals passed to OpenSSL::Cipher.new in
the encryption helpers cause low-confidence scanner matches because they use
lowercase or noncanonical names; update the string literals in the methods (the
OpenSSL::Cipher.new(...) calls inside the AES-GCM helper, encrypt_aes_cbc,
encrypt_chacha, and encrypt_3des) to the scanner's expected canonical uppercase
names (e.g. use "AES-256-GCM" for the AES-GCM helper, "AES-128-CBC" in
encrypt_aes_cbc, "ChaCha20" in encrypt_chacha, and "DES-EDE3-CBC" in
encrypt_3des) so the high-confidence Ruby patterns match.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0d61159a-88c6-4ce8-9968-87c2959ad545

📥 Commits

Reviewing files that changed from the base of the PR and between 423737e and 0b01d05.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • extlib/cryptoscan/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (43)
  • Cargo.toml
  • Makefile
  • docs/features/crypto-scanning.md
  • docs/references/experimental/crypto-scanning/README.md
  • docs/references/subcommands/analyze.md
  • extlib/cryptoscan/Cargo.toml
  • extlib/cryptoscan/src/crypto_algorithm.rs
  • extlib/cryptoscan/src/cyclonedx.rs
  • extlib/cryptoscan/src/fips.rs
  • extlib/cryptoscan/src/main.rs
  • extlib/cryptoscan/src/patterns.rs
  • extlib/cryptoscan/src/scanner.rs
  • extlib/cryptoscan/tests/integration_test.rs
  • spectrometer.cabal
  • src/App/Fossa/Analyze.hs
  • src/App/Fossa/Analyze/ScanSummary.hs
  • src/App/Fossa/Analyze/Types.hs
  • src/App/Fossa/Config/Analyze.hs
  • src/App/Fossa/CryptoScan/Analyze.hs
  • src/App/Fossa/CryptoScan/FipsReport.hs
  • src/App/Fossa/CryptoScan/Types.hs
  • src/App/Fossa/EmbeddedBinary.hs
  • test-fixtures/csharp-service/CryptoService.cs
  • test-fixtures/csharp-service/Service.csproj
  • test-fixtures/elixir-phoenix-app/crypto.ex
  • test-fixtures/elixir-phoenix-app/mix.exs
  • test-fixtures/go-api-server/go.mod
  • test-fixtures/go-api-server/main.go
  • test-fixtures/java-microservice/pom.xml
  • test-fixtures/java-microservice/src/main/java/com/example/CryptoService.java
  • test-fixtures/node-auth-service/auth.js
  • test-fixtures/node-auth-service/package.json
  • test-fixtures/php-api/composer.json
  • test-fixtures/php-api/crypto.php
  • test-fixtures/python-web-app/app.py
  • test-fixtures/python-web-app/requirements.txt
  • test-fixtures/ruby-web-app/Gemfile
  • test-fixtures/ruby-web-app/app.rb
  • test-fixtures/rust-crypto-tool/Cargo.toml
  • test-fixtures/rust-crypto-tool/src/main.rs
  • test-fixtures/swift-ios-app/CryptoManager.swift
  • test-fixtures/swift-ios-app/Podfile
  • test/App/Fossa/CryptoScan/FipsReportSpec.hs

Comment thread docs/features/crypto-scanning.md Outdated
Comment thread docs/references/subcommands/analyze.md Outdated
Comment thread extlib/cryptoscan/src/crypto_algorithm.rs
Comment thread extlib/cryptoscan/src/cyclonedx.rs
Comment thread extlib/cryptoscan/src/fips.rs Outdated
Comment thread src/App/Fossa/CryptoScan/FipsReport.hs Outdated
Comment thread src/App/Fossa/CryptoScan/Types.hs
Comment thread src/App/Fossa/CryptoScan/Types.hs
Comment thread test-fixtures/csharp-service/Service.csproj Outdated
Comment thread test-fixtures/ruby-web-app/app.rb Outdated
CortezFrazierJr and others added 10 commits April 9, 2026 20:07
Introduce a new experimental feature (--x-crypto-scan) that detects
cryptographic algorithm usage across 10 ecosystems (Python, Java, Go,
Node, Rust, Ruby, C#, PHP, Swift, Elixir) and produces CycloneDX 1.7
CBOM output for FIPS compliance assessment.

Key features:
- Rust-based crypto detection engine (extlib/cryptoscan) with pattern
  matching for imports, API calls, dependency manifests, and config files
- Auto-detection of ecosystems present in the project
- FIPS 140-3 compliance classification (approved/deprecated/not-approved)
- CycloneDX 1.7 CBOM file output (--crypto-cbom-output)
- Detailed FIPS compliance report (--crypto-fips-report) with remediation
  recommendations and key-size warnings
- Crypto scan results displayed in the analysis scan summary
- 56 passing integration tests covering all 10 ecosystems

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…aggregation, parse error handling, and doc wording

- scanner.rs: Add normalize_detected_algorithm() to use matched text for
  refining generic pattern names into specific algorithm variants (e.g.,
  AES -> AES-256-GCM based on matched code context)
- cyclonedx.rs: Aggregate detection contexts (detected-in, detection-method,
  ecosystem) from all findings per algorithm instead of only keeping the first
- Analyze.hs: Use fatalText instead of warn+Nothing for JSON parse failures
  so scanner output-contract regressions surface as hard errors
- crypto-scanning.md: Fix wording that implied non-ECB AES modes are deprecated

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ng, add doc entries

- FipsReport.hs: Convert all match guards to if/then/else per style guide
  (compliancePercentage, coloredPercentage, suggestAlternative, catWarnings,
  rsaWarning). Remove unused matchesAny function.
- scanner.rs: Use filter_entry() to prune skipped directories during WalkDir
  traversal instead of filtering after entry. Replace brittle slash-based
  path matching with file_name() segment comparison for cross-platform
  correctness.
- analyze.md: Add --crypto-cbom-output and --crypto-fips-report to the
  experimental flags table for discoverability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, regression test, clock safety

- cyclonedx.rs: Sort unique_algos for deterministic BOM output across runs.
  Use unwrap_or_default() for SystemTime to avoid panic on clock anomalies.
- FipsReport.hs: Replace quadratic nubBy with Map-based deduplicateFindings
  for O(n log n) deduplication. Reuse single dedupe pass in both
  computeFipsStats and renderFipsReport.
- FipsReportSpec.hs: Add regression test verifying RSA-1024 and RSA-2048
  are counted as separate algorithm variants (different parameter sets).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…em detection, variable shadowing

- Don't guess AES key sizes from mode-only detections (AES-GCM → None, not 256)
- Add AES-192 variants for GCM and CBC modes
- Add RSA-<bits> dynamic matching in resolve_algorithm fallthrough
- Rewrite detect_ecosystems with filter_entry pruning and source extension detection
- Fix foldl→foldr in categorizeFindings for proper ordering
- Fix variable shadowing in keySizeWarning (name→algoName, name/paramSet→n/ps)
- Change debug logging to show finding count instead of full payload
- Separate FIPS status from algorithmProperties in CBOM docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lder, smoke tests

- Declare rust-version = "1.70" in Cargo.toml for is_some_and compatibility
- Add Primitive::as_str() to avoid serde round-trip for string conversion
- Reuse bom_ref computation instead of computing twice per loop iteration
- Use BTreeSet for combined dedup+sort in CycloneDX dependency generation
- Replace defensive month=12 fallback with debug_assert
- Fix double deduplication in renderFipsReport (dedupe once, pass to stats)
- Extract mkCryptoScanCommand to reduce command builder duplication
- Remove redundant double deserialization in integration test
- Add renderFipsReport smoke tests for empty and mixed results

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Pin dependency versions to match millhone for workspace consistency
- Add DetectionMethod::as_str() for parity with Primitive::as_str()
- Replace serde round-trip for DetectionMethod in cyclonedx.rs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ed libs, algorithmFamily validation

- Add KeyWrap variant to Primitive enum per CycloneDX 1.7 schema
- Scope library identity by (ecosystem, library_name) to prevent cross-ecosystem collisions
- Validate algorithmFamily against CycloneDX 1.7 registry, emit None for unknown values

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ithmFamily

- Replace HashMap with BTreeMap for algo_findings and library_algorithms
  to ensure deterministic component/dependency ordering in CBOM output
- Replace custom KNOWN_FAMILIES with official CycloneDX 1.7 algorithmFamily
  enum (78 canonical entries) and return canonical case via .find()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve hlint warnings: use isJust, foldl', guards, eta-reduce, toString.
Run fourmolu, cabal-fmt, rustfmt on all changed files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@CortezFrazierJr CortezFrazierJr force-pushed the feature/crypto-scanning branch from 0b01d05 to 9110190 Compare April 10, 2026 00:18
CortezFrazierJr and others added 7 commits April 9, 2026 20:26
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ordering, key-wrap sync, docs accuracy

- Replace HashSet with BTreeSet in cyclonedx.rs for deterministic property order
- Move SLH-DSA/ML-DSA checks before generic DSA rule to prevent misclassification
- Remove dead FipsRemediation struct from fips.rs
- Add scan root validation in main.rs
- Remove depth-3 cap from ecosystem auto-detection
- Use BTreeMap for deterministic deduplicated findings output
- Stop inventing key sizes for generic algorithm detections
- Add PrimitiveKeyWrap to Haskell CryptoPrimitive with key-wrap mapping
- Qualify Data.Aeson imports in Types.hs per repo style guide
- Guard CBOM file write with fatalOnIOException
- Add 3DES to FipsReport remediation mapping
- Fix ecosystem count wording in docs (10 -> multiple)
- Soften privacy claim in crypto-scanning docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…confidence, test fixtures

- Add file_names field to CryptoPattern for dependency-manifest filename scoping
- Lower confidence for generic Python namespace import patterns (High -> Medium)
- Replace match guard with case expression in ScanSummary crypto rendering
- Fix C# test fixture: use Konscious.Security.Cryptography for Argon2
- Fix Ruby test fixture: use canonical uppercase cipher names

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
App.Fossa.Config.Crypto and App.Fossa.Crypto were listed but never created.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Master added cmdEnvVars to Command type; set to mempty for crypto scan.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
logInfo expects Doc AnsiStyle, not Text — remove unnecessary renderIt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Master's AnalyzeConfig gained xCryptoScan, cryptoCbomOutput, cryptoFipsReport.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@CortezFrazierJr
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 10, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@CortezFrazierJr CortezFrazierJr marked this pull request as ready for review April 10, 2026 03:34
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

♻️ Duplicate comments (1)
src/App/Fossa/CryptoScan/Types.hs (1)

13-23: ⚠️ Potential issue | 🟠 Major

Qualify all Haskell imports (and corresponding usages) consistently.

This module still uses unqualified Data.Aeson/Data.Text imports (FromJSON, ToJSON, Text, (.:), (.=), etc.), which conflicts with the repo’s Haskell import rule.

Suggested direction
-import Data.Aeson (
-  FromJSON (parseJSON),
-  ToJSON (toJSON),
-  Value (String),
-  (.:),
-  (.:?),
-  (.=),
- )
-import Data.Aeson qualified as Aeson
-import Data.Text (Text)
+import Data.Aeson qualified as Aeson
+import Data.Text qualified as Text
-instance FromJSON CryptoFinding where
+instance Aeson.FromJSON CryptoFinding where
   parseJSON = Aeson.withObject "CryptoFinding" $ \o ->
     CryptoFinding
-      <$> o .: "algorithm"
+      <$> o Aeson..: "algorithm"

As per coding guidelines, "Use explicit imports in Haskell, qualified with full names".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/App/Fossa/CryptoScan/Types.hs` around lines 13 - 23, This module uses
unqualified imports from Data.Aeson and Data.Text (e.g. FromJSON, ToJSON,
Value(String), (.:), (.:?), (.=), Text) which violates the repo rule to use
explicit qualified imports; change the imports to import Data.Aeson qualified as
Aeson and Data.Text qualified as Text (or similar consistent qualifiers) and
update all usages in this file to use those qualifiers (e.g.
Aeson.FromJSON/Aeson.parseJSON, Aeson.ToJSON/Aeson.toJSON,
Aeson.Value(Aeson.String), Aeson.(.:)/Aeson.(.:?)/Aeson.(.=), and Text.Text) so
imports are explicit and consistent with the coding guidelines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/references/experimental/crypto-scanning/README.md`:
- Line 13: Update the sentence "Ten ecosystems are supported: Python, Java, Go,
Rust, Node.js, Ruby, C#/.NET, PHP, Swift, and Elixir." to reflect the PR's claim
of "15+ ecosystems"—either change the numeric wording to "15+ ecosystems are
supported" or enumerate the full set of 15+ ecosystems to match the PR; ensure
the sentence and any adjacent summary text consistently state "15+" instead of
"ten" so user-facing docs align with the PR.

In `@extlib/cryptoscan/src/cyclonedx.rs`:
- Around line 397-408: The month calculation currently initializes month = 0 and
relies on the loop invariant plus a debug_assert; make it defensive by computing
month using an iterator and unwrapping to a safe default instead of leaving
month possibly zero. Replace the manual for-loop over months_days with a call
like months_days.iter().position(|&md| days < md).map(|i| i +
1).unwrap_or(months_days.len()) (or equivalent) to set month, and keep the final
debug_assert or replace it with a runtime panic/explicit error if you prefer;
update references to the month variable accordingly (symbols: months_days,
month, debug_assert).

In `@extlib/cryptoscan/src/fips.rs`:
- Around line 159-177: The current logic in the RSA/ECDSA/ECDH/DH branches
returns FipsStatus::Approved for bare algorithm name matches even when no
parameters (key size, curve, domain params) were extracted; update the branches
around the `lower.contains("rsa")` check and the `lower.contains("ecdsa")` /
ECDH / DH checks so that approval is only returned when the required parameters
were actually parsed: for RSA, only return FipsStatus::Approved when `key_size`
is Some(bits) and bits >= 2048, otherwise return NotApproved (or a manual-review
status) if key_size is None or <2048; for ECDSA/ECDH/DH do not return Approved
unconditionally — require explicit curve/domain parameters to be parsed before
returning Approved and otherwise return NotApproved/manual-review. Keep
references to the existing symbols (`lower`, `key_size`, `FipsStatus`) and
adjust the matching branches accordingly.

In `@src/App/Fossa/CryptoScan/FipsReport.hs`:
- Around line 269-300: The SHA-related branches in keySizeWarning (inside
catWarnings) use substring matching on algoName so composite names like
"HMAC-SHA1" trigger hash deprecation warnings; update catWarnings to only emit
SHA warnings for standalone hash primitives—either by checking the algorithm
type/primitive from cryptoFindingAlgorithm (if available) or by matching
exact/bounded names (e.g., exact "sha-1"/"sha1" or regex word-boundary matches)
and explicitly skip names prefixed with "hmac-" or other MAC/combination
prefixes; keep rsaWarning behavior the same and use cryptoAlgorithmName and
cryptoAlgorithmParameterSet to locate and gate the change.

In `@test-fixtures/java-microservice/pom.xml`:
- Around line 8-12: The pom dependency for org.bouncycastle:bcprov-jdk18on
currently pins version 1.78 which is vulnerable (CVE-2025-8916 and other fixes
missing); update the <version> for the dependency with artifactId bcprov-jdk18on
(groupId org.bouncycastle) to a fixed release (preferably 1.79, or at minimum
1.78.1 if you must remain on the 1.78 line), then run a build to verify no
compatibility issues and update any dependency management or BOM entries if they
override this version.

In
`@test-fixtures/java-microservice/src/main/java/com/example/CryptoService.java`:
- Around line 14-16: Update the class Javadoc for CryptoService to explicitly
state this is test-fixture-only and not for production use; add a short guidance
sentence mentioning that the class intentionally contains insecure/deprecated
crypto patterns for detection/testing and must not be reused in production, so
reviewers/readers of the CryptoService class (and its methods) are warned
against copy/paste.
- Around line 99-103: The PBEKeySpec in deriveKeyPbkdf2 holds sensitive password
data and must be cleared; modify deriveKeyPbkdf2 to create the PBEKeySpec,
perform SecretKeyFactory.getInstance(...).generateSecret(spec).getEncoded()
inside a try block, and in a finally block call spec.clearPassword() (and null
out spec if desired) so the password chars are wiped even on exceptions;
reference PBEKeySpec spec, SecretKeyFactory factory, and deriveKeyPbkdf2 when
applying the change.

In `@test-fixtures/php-api/crypto.php`:
- Around line 3-4: Remove the unused phpseclib3 imports (use
phpseclib3\Crypt\AES and use phpseclib3\Crypt\RSA) from crypto.php or, if they
are intentionally present for import-pattern detection, replace them with a
short clarifying comment above the import block explaining they are deliberately
unused; update the file so the actual implementations only reference openssl_*
and sodium_* functions to avoid scanners falsely reporting phpseclib usage.

In `@test-fixtures/rust-crypto-tool/src/main.rs`:
- Around line 56-64: The build fails because feature-gated APIs are used in
generate_ed25519_key (SigningKey::generate requires ed25519-dalek's rand_core)
and x25519_key_exchange (EphemeralSecret::random requires x25519-dalek's
getrandom); update your Cargo.toml dependency declarations to enable these
features by setting ed25519-dalek to include the "rand_core" feature and
x25519-dalek to include the "getrandom" feature so those methods are available
at compile time.

In `@test-fixtures/swift-ios-app/CryptoManager.swift`:
- Around line 81-95: The deriveKeyPbkdf2 function currently force-unwraps
baseAddress in derivedKey.withUnsafeMutableBytes and salt.withUnsafeBytes and
ignores CCKeyDerivationPBKDF's return code; update deriveKeyPbkdf2 to safely
unwrap the pointers (use guard let on derivedBytes.baseAddress and
saltBytes.baseAddress within derivedKey.withUnsafeMutableBytes /
salt.withUnsafeBytes closures) and capture the Int32 status returned by
CCKeyDerivationPBKDF; if status is not kCCSuccess, handle it deterministically
(e.g., throw an error or call fatalError with a descriptive message) so failures
are surfaced instead of silently ignored. Ensure you reference deriveKeyPbkdf2,
CCKeyDerivationPBKDF, derivedKey.withUnsafeMutableBytes and salt.withUnsafeBytes
when making the changes.

---

Duplicate comments:
In `@src/App/Fossa/CryptoScan/Types.hs`:
- Around line 13-23: This module uses unqualified imports from Data.Aeson and
Data.Text (e.g. FromJSON, ToJSON, Value(String), (.:), (.:?), (.=), Text) which
violates the repo rule to use explicit qualified imports; change the imports to
import Data.Aeson qualified as Aeson and Data.Text qualified as Text (or similar
consistent qualifiers) and update all usages in this file to use those
qualifiers (e.g. Aeson.FromJSON/Aeson.parseJSON, Aeson.ToJSON/Aeson.toJSON,
Aeson.Value(Aeson.String), Aeson.(.:)/Aeson.(.:?)/Aeson.(.=), and Text.Text) so
imports are explicit and consistent with the coding guidelines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 07eb1abc-ea85-4f13-a07d-ecace7c67940

📥 Commits

Reviewing files that changed from the base of the PR and between 0b01d05 and 2d2490b.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • extlib/cryptoscan/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (44)
  • Cargo.toml
  • Makefile
  • docs/features/crypto-scanning.md
  • docs/references/experimental/crypto-scanning/README.md
  • docs/references/subcommands/analyze.md
  • extlib/cryptoscan/Cargo.toml
  • extlib/cryptoscan/src/crypto_algorithm.rs
  • extlib/cryptoscan/src/cyclonedx.rs
  • extlib/cryptoscan/src/fips.rs
  • extlib/cryptoscan/src/main.rs
  • extlib/cryptoscan/src/patterns.rs
  • extlib/cryptoscan/src/scanner.rs
  • extlib/cryptoscan/tests/integration_test.rs
  • spectrometer.cabal
  • src/App/Fossa/Analyze.hs
  • src/App/Fossa/Analyze/ScanSummary.hs
  • src/App/Fossa/Analyze/Types.hs
  • src/App/Fossa/Config/Analyze.hs
  • src/App/Fossa/CryptoScan/Analyze.hs
  • src/App/Fossa/CryptoScan/FipsReport.hs
  • src/App/Fossa/CryptoScan/Types.hs
  • src/App/Fossa/EmbeddedBinary.hs
  • test-fixtures/csharp-service/CryptoService.cs
  • test-fixtures/csharp-service/Service.csproj
  • test-fixtures/elixir-phoenix-app/crypto.ex
  • test-fixtures/elixir-phoenix-app/mix.exs
  • test-fixtures/go-api-server/go.mod
  • test-fixtures/go-api-server/main.go
  • test-fixtures/java-microservice/pom.xml
  • test-fixtures/java-microservice/src/main/java/com/example/CryptoService.java
  • test-fixtures/node-auth-service/auth.js
  • test-fixtures/node-auth-service/package.json
  • test-fixtures/php-api/composer.json
  • test-fixtures/php-api/crypto.php
  • test-fixtures/python-web-app/app.py
  • test-fixtures/python-web-app/requirements.txt
  • test-fixtures/ruby-web-app/Gemfile
  • test-fixtures/ruby-web-app/app.rb
  • test-fixtures/rust-crypto-tool/Cargo.toml
  • test-fixtures/rust-crypto-tool/src/main.rs
  • test-fixtures/swift-ios-app/CryptoManager.swift
  • test-fixtures/swift-ios-app/Podfile
  • test/App/Fossa/CryptoScan/FipsReportSpec.hs
  • test/Test/Fixtures.hs

Comment thread docs/references/experimental/crypto-scanning/README.md Outdated
Comment thread extlib/cryptoscan/src/cyclonedx.rs
Comment thread extlib/cryptoscan/src/fips.rs Outdated
Comment thread src/App/Fossa/CryptoScan/FipsReport.hs
Comment thread test-fixtures/java-microservice/pom.xml
Comment thread test-fixtures/php-api/crypto.php
Comment thread test-fixtures/rust-crypto-tool/src/main.rs
Comment thread test-fixtures/swift-ios-app/CryptoManager.swift
… warnings, fixture fixes

- Parameterless RSA/ECDSA/ECDH/DH now return Deprecated with manual-review note
- Gate SHA-1/SHA-224 deprecation warnings on hash primitives to avoid HMAC-SHA1
- Fix x25519-dalek v2 API (EphemeralSecret::random_from_rng)
- Add test fixture safety comments (Java, PHP, Swift)
- Add clearPassword() call for PBEKeySpec in Java fixture
- Reconcile ecosystem count in experimental docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@nficca nficca removed their request for review April 15, 2026 15:24
@zlav zlav marked this pull request as draft April 22, 2026 16:01
@zlav zlav removed their request for review April 22, 2026 16:02
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