Skip to content

feat(security): add vulnerability intelligence review - #77

Merged
elkaix merged 5 commits into
mainfrom
feat/security-intel-review
Jun 4, 2026
Merged

feat(security): add vulnerability intelligence review#77
elkaix merged 5 commits into
mainfrom
feat/security-intel-review

Conversation

@elkaix

@elkaix elkaix commented Jun 4, 2026

Copy link
Copy Markdown
Member

Summary

  • add a Python-native security_intel package for CVE/package vulnerability enrichment
  • add OSV dependency manifest scanning and stored dependency reports for pythinker security-scan
  • enrich security-review signals/prompts with CVE and dependency-change leads while keeping intel as supporting evidence, not proof
  • include dependency vulnerabilities in security-scan metrics/reports and document new commands

Verification

  • uv run --directory packages/pythinker-review ruff check src tests
  • uv run --directory packages/pythinker-review ruff format --check src tests
  • uv run --directory packages/pythinker-review pyright src tests/unit/test_security_intel.py tests/unit/test_security_scan_dependencies.py tests/unit/test_signals.py
  • make test-pythinker-review (168 passed, 3 skipped)

Notes

  • make check-pythinker-review passes ruff/format/pyright, then prints existing non-blocking ty diagnostics in unrelated files.
  • No new mandatory runtime dependencies were added.

Summary by CodeRabbit

  • New Features

    • Repo-wide dependency parsing and scans with OSV plus multi-source CVE enrichment (NVD, EPSS, CISA KEV, GitHub, vendor feeds); new CLI subcommands for dependency and intel lookups. Reports, markdown, and advisor context now surface dependency-vulnerability summaries, risk scores, and “vulnerability intelligence” leads; scanner detects CVEs and dependency-change leads.
  • Documentation

    • README and changelog updated with dependency-scan and CVE-enrichment examples.
  • Tests

    • New unit tests for parsing, report persistence, caching/client behavior, URL redaction, and risk scoring.

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a8068b09-04c8-4379-96fc-b39ce64cb741

📥 Commits

Reviewing files that changed from the base of the PR and between 8fc9041 and 4d77aec.

📒 Files selected for processing (4)
  • packages/pythinker-review/src/pythinker_review/cli/security_scan.py
  • packages/pythinker-review/src/pythinker_review/security_intel/client.py
  • packages/pythinker-review/src/pythinker_review/security_scan/dependencies.py
  • packages/pythinker-review/tests/unit/test_security_scan_dependencies.py

📝 Walkthrough

Walkthrough

Adds a security-intel subsystem, dependency manifest parsers and OSV scanning, CLI commands for deps/intel, signal emission for CVE/dependency leads, risk scoring and aggregation services, processor/reporting/prompt wiring, tests, and README/CHANGELOG updates.

Changes

Vulnerability Intelligence & Dependency Scanning

Layer / File(s) Summary
Models & public package surface
packages/pythinker-review/src/pythinker_review/security_intel/__init__.py, .../models.py
Adds Pydantic models, type aliases, and package exports for CVE/EPSS/KEV/exploit/vendor/package/risk/bundle contracts.
Validators, cache, HTTP client
.../security_intel/validators.py, .../cache.py, .../client.py
Adds CVE/package/url/ip/hash validators, URL redaction for logs, a JSON TTL cache with eviction/persistence, and IntelHttpClient that blocks redirects and enforces time/size limits.
Source adapters
.../security_intel/sources/{epss,nvd,kev,osv,github,vendor}.py
Cache-first adapters for EPSS, NVD, CISA KEV, OSV, GitHub PoC availability, and vendor advisories; normalize responses into models.
Risk scoring & service orchestration
.../security_intel/risk.py, .../service.py
Computes composite CVE RiskScore (CVSS/EPSS/KEV/PoC/recency) and provides lookup_cve_bundle/scan_packages/lookup_package orchestration.
Dependency parsing & scan reports
.../security_scan/dependencies.py
Parses requirements/package.json/pyproject/pom.xml into PackageRef list, deduplicates, runs OSV scans, and persists DependencyScanReport with read/write helpers.
CLI: deps & intel
.../cli/security_scan.py
Adds deps subcommands (list, scan, report) and intel subcommands (cve, package) to run lookups and scans and print JSON or summaries.
Signals & advisor leads
.../signals/models.py, .../signals/scanner.py, .../signals/advisor.py
Signal dataclass gains metadata; scanner emits CVE-reference and dependency-change signals; advisor assembles a “Vulnerability intelligence leads” block and adds slug notes.
Processor, reporting, prompts & docs
.../security_scan/processor.py, .../reporting.py, .../prompts/*, README.md, CHANGELOG.md
Processor augments project_info with dependency context; metrics and markdown include dependency vulnerability sections and errors; prompts updated to treat intel as leads; docs/changelog updated.
Tests
packages/pythinker-review/tests/unit/*
Unit tests added/updated for CVE normalization, URL redaction, redirect policy, cache roundtrip, risk scoring, manifest parsing, report roundtrip, signal detection, and advisor context.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Service as lookup_cve_bundle
  participant Cache as IntelCache
  participant Client as IntelHttpClient
  participant NVD
  participant EPSS
  participant KEV
  participant GitHub
  participant Vendor

  Caller->>Service: lookup_cve_bundle(cve_id,data_root,...)
  Service->>Cache: get(nvd:cve:{normalized})
  Service->>Client: request NVD (if miss)
  Client->>NVD: HTTP GET /cves/2.0?cveId=...
  Client-->>Service: NVD response
  Service->>Client: request EPSS
  Client->>EPSS: HTTP GET /epss?cve=...
  Client-->>Service: EPSS response
  Service->>Client: request KEV
  Client->>KEV: HTTP GET KEV feed
  Client-->>Service: KEV response
  Service->>Client: (opt) GitHub search
  Client->>GitHub: repos search q=CVE...
  Client-->>Service: GitHub response
  Service->>Client: (opt) vendor calls
  Client->>Vendor: vendor advisory endpoints
  Client-->>Service: vendor responses
  Service->>Service: score_cve(...) -> RiskScore
  Service-->>Caller: CVEIntelBundle (with models, source_errors, risk)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested labels

enhancement

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.07% which is insufficient. The required threshold is 70.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description includes a summary of changes, verification steps, and notes. However, it lacks the template structure with explicit sections for Related Issue and completed checklist items. Add explicit 'Related Issue' section with issue number link and complete the checklist section with marked items to match the repository template.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title follows conventional commits format with type 'feat', scope 'security', and clearly describes the main change of adding vulnerability intelligence review capabilities.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/security-intel-review

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

@codecov

codecov Bot commented Jun 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/pythinker-review/src/pythinker_review/cli/security_scan.py`:
- Around line 159-167: The intel_cve command currently lets exceptions from
asyncio.run(lookup_cve_bundle(...)) bubble up; wrap the asyncio.run call in a
try/except that catches expected failures (HTTP/client library errors, pydantic
ValidationError, and a general Exception fallback) and handle them by printing a
concise CLI error via typer.echo (include the error message) and exit with a
non-zero code (raise typer.Exit(1) or sys.exit(1)); apply the same pattern to
the other intel command(s) that call lookup_* functions (e.g., the intel command
block around lines 170-181) and keep use of _data_root and lookup_cve_bundle
unchanged.

In `@packages/pythinker-review/src/pythinker_review/security_intel/client.py`:
- Around line 120-126: The code currently does
int(resp.headers.get("content-length")) which can raise ValueError and bypass
the IntelClientError handling; update the block around content_length, the int
conversion and the subsequent check (referencing resp.headers.get,
content_length, self.max_response_bytes, IntelClientError, resp.read, and
IntelResponse) to defensively handle malformed headers: catch ValueError when
parsing Content-Length and either treat it as absent (skip the size check) or
explicitly raise an IntelClientError with a clear message like "invalid
Content-Length header" so callers always receive IntelClientError instead of a
raw ValueError.

In
`@packages/pythinker-review/src/pythinker_review/security_intel/sources/nvd.py`:
- Around line 61-64: search_cves currently assumes the upstream response is a
dict and calls data.get(...), which will raise if data is not a dict; after the
await client.get_json(...) call in search_cves validate that data is a dict
(e.g., isinstance(data, dict)) and fall back to an empty dict if not, then
extract vulnerabilities = data.get("vulnerabilities", []) only if
vulnerabilities is a list (otherwise treat as []), and finally build records via
CVERecord.model_validate(item.get("cve", {})) for each item in that list so the
function returns a safe empty list instead of raising on non-dict payloads.

In
`@packages/pythinker-review/src/pythinker_review/security_scan/dependencies.py`:
- Around line 250-253: The current _find_line function returns any substring
match which can point at comments or unrelated tokens; update _find_line to
perform a whole-word match and ignore commented lines: read the file via
_read_text(path), iterate lines as before, skip lines where stripped startswith
comment markers (e.g., '#' or '//'), and use a regex search like re.search(r'\b'
+ re.escape(needle) + r'\b', line) to ensure only true token matches are
returned; if no safe match is found return None. Ensure you import re and
preserve the existing signature and use of _read_text.
- Around line 18-21: The current regexes _REQUIREMENT_RE and
_BARE_REQUIREMENT_RE reject package names containing extras like
requests[socks]; update both to accept optional extras in square brackets
(comma-separated identifiers) as part of the name token. Change the name capture
from ([A-Za-z0-9_.\-]+) to something like
([A-Za-z0-9_.\-]+(?:\[[A-Za-z0-9_,]+\])?) in _REQUIREMENT_RE and similarly allow
optional \[...\] in _BARE_REQUIREMENT_RE so entries like requests[socks]==2.31.0
or requests[socks] are matched.

In `@packages/pythinker-review/src/pythinker_review/security_scan/processor.py`:
- Around line 148-152: The code currently recomputes project_info inside each
batch by calling _with_dependency_context(read_info(...)) — move that call into
process_project so project_info is computed once per run (before spawning
workers) and pass the precomputed project_info into the worker/batch-processing
functions instead of recomputing; update any function signatures that currently
call read_info/_with_dependency_context to accept the prebuilt project_info
(refer to process_project, _with_dependency_context, read_info, and the batch
worker entry points) to ensure identical prompt context across concurrent
batches.

In `@packages/pythinker-review/src/pythinker_review/security_scan/reporting.py`:
- Around line 136-138: The report currently appends every entry from
dependency_report.source_errors to lines, which can bloat outputs; update the
code that builds the dependency intel section (the block referencing
dependency_report.source_errors and the lines list) to show only the first N
errors (choose a sensible default like N=10), add a trailing line like "- and X
more errors" when there are additional items, and ensure empty source_errors
still skips the section; use the existing variables
(dependency_report.source_errors, lines) so the change is local and reversible.

In `@packages/pythinker-review/src/pythinker_review/signals/scanner.py`:
- Around line 26-29: The _DEP_LINE_RE currently only matches a few operators and
X.Y versions; update its pattern to also detect other common version operators
and formats so dependency lines aren't missed. Expand the regex in _DEP_LINE_RE
to include operators like >=, <=, !=, >, < in addition to ==, ^, ~, allow
semantic versions with three segments (X.Y.Z) and optional pre-release/build
parts, and also match JSON-style pinned entries (e.g., "pkg": "1.2.3") and bare
numeric pins; keep the word anchors
(dependencies|devDependencies|requires|requirement|package) and
case-insensitivity. Ensure the final pattern still compiles as re.compile and is
assigned to _DEP_LINE_RE.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8f8896ed-c13c-486f-a5c7-1ee7e8038b9b

📥 Commits

Reviewing files that changed from the base of the PR and between dadbbbf and ccdd84f.

📒 Files selected for processing (28)
  • CHANGELOG.md
  • packages/pythinker-review/README.md
  • packages/pythinker-review/src/pythinker_review/cli/security_scan.py
  • packages/pythinker-review/src/pythinker_review/reviewers/prompts/security_review.system.md
  • packages/pythinker-review/src/pythinker_review/security_intel/__init__.py
  • packages/pythinker-review/src/pythinker_review/security_intel/cache.py
  • packages/pythinker-review/src/pythinker_review/security_intel/client.py
  • packages/pythinker-review/src/pythinker_review/security_intel/models.py
  • packages/pythinker-review/src/pythinker_review/security_intel/risk.py
  • packages/pythinker-review/src/pythinker_review/security_intel/service.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/__init__.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/epss.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/github.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/kev.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/nvd.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/osv.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/vendor.py
  • packages/pythinker-review/src/pythinker_review/security_intel/validators.py
  • packages/pythinker-review/src/pythinker_review/security_scan/dependencies.py
  • packages/pythinker-review/src/pythinker_review/security_scan/processor.py
  • packages/pythinker-review/src/pythinker_review/security_scan/prompts/system.md
  • packages/pythinker-review/src/pythinker_review/security_scan/reporting.py
  • packages/pythinker-review/src/pythinker_review/signals/advisor.py
  • packages/pythinker-review/src/pythinker_review/signals/models.py
  • packages/pythinker-review/src/pythinker_review/signals/scanner.py
  • packages/pythinker-review/tests/unit/test_security_intel.py
  • packages/pythinker-review/tests/unit/test_security_scan_dependencies.py
  • packages/pythinker-review/tests/unit/test_signals.py

Comment thread packages/pythinker-review/src/pythinker_review/cli/security_scan.py
Comment thread packages/pythinker-review/src/pythinker_review/security_scan/dependencies.py Outdated
Comment thread packages/pythinker-review/src/pythinker_review/security_scan/processor.py Outdated
Comment thread packages/pythinker-review/src/pythinker_review/security_scan/reporting.py Outdated
Comment thread packages/pythinker-review/src/pythinker_review/signals/scanner.py

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/pythinker-review/tests/unit/test_security_intel.py`:
- Around line 36-46: The current test only exercises _NoRedirectHandler itself;
update test_intel_client_disables_implicit_redirects to instantiate
IntelHttpClient and assert the client's configured opener actually includes an
instance of _NoRedirectHandler (e.g., inspect the client's opener handlers or
registered handlers and assert any(isinstance(h, _NoRedirectHandler) for h in
opener.handlers)). This ensures the security handler is wired by IntelHttpClient
rather than just verifying the helper class alone.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0653a232-c1b8-40f7-86b0-0e4d01b5aa65

📥 Commits

Reviewing files that changed from the base of the PR and between ccdd84f and 1948a81.

📒 Files selected for processing (2)
  • packages/pythinker-review/src/pythinker_review/security_intel/client.py
  • packages/pythinker-review/tests/unit/test_security_intel.py

Comment thread packages/pythinker-review/tests/unit/test_security_intel.py

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/pythinker-review/README.md`:
- Line 59: Add a short inline comment next to the "pythinker-security-scan intel
cve CVE-2024-3094" command explaining what the command returns (e.g., that it
fetches intelligence/details related to the specified CVE such as affected
packages, severity, and references) to match the explanatory style used for the
"deps scan" comment; update the README line containing the command so the
comment is concise and consistent with the comment on the preceding "deps scan"
line.

In `@packages/pythinker-review/src/pythinker_review/security_intel/client.py`:
- Around line 110-119: The current call to urllib.request.urlopen(req,
timeout=self.timeout_s) only validates the initial URL via
validate_intel_url(url) and will follow 30x redirects without re-checking
allowlist; fix by creating a custom urllib.request.HTTPRedirectHandler that
overrides redirect_request to call validate_intel_url(newurl) (and raise an
HTTPError or return None to reject the redirect) and then use
urllib.request.build_opener(...your handler...) to open the Request (req)
instead of urlopen; update the code that constructs req and the open call
(referenced symbols: validate_intel_url, urllib.request.Request,
urllib.request.HTTPRedirectHandler, urllib.request.build_opener, req,
self.timeout_s) so every redirected Location is re-validated or redirects are
rejected.

In `@packages/pythinker-review/src/pythinker_review/security_intel/service.py`:
- Around line 57-77: When all enrichment inputs are None you must not synthesize
a LOW risk; before calling score_cve() in the block that builds the
CVEIntelBundle, check whether any scoring source succeeded (e.g. nvd_record,
epss_score (derived from epss_scores), kev_entry, or exploit are not None). If
none succeeded, set risk=None and skip calling score_cve(); otherwise call
score_cve(...) as currently done. Ensure the returned CVEIntelBundle uses that
risk (risk=None when no sources) and still includes source_errors,
vendor_result, and other fields unchanged.

In
`@packages/pythinker-review/src/pythinker_review/security_intel/sources/github.py`:
- Around line 55-63: check_exploit_availability currently assumes the GitHub
search response is a dict with "items" and unconditionally caches empty results;
change the flow so after calling client.get_json(GITHUB_REPO_SEARCH_URL, ...)
you explicitly validate the shape (isinstance(data, dict) and "items" in data
and isinstance(data["items"], list)); if the response is malformed, do not call
cache.set(..., TTL_EXPLOIT) and instead log the error/response (or raise) and
return confidence "NONE" without caching; only proceed to build refs/scores and
cache the result when the validated "items" list is present and processed.

In
`@packages/pythinker-review/src/pythinker_review/security_intel/sources/kev.py`:
- Around line 19-22: The current fetch_kev_catalog caches an empty/malformed
payload because it blindly uses data.get("vulnerabilities", []) before
validating the response shape; update fetch_kev_catalog to first verify that
data is a dict and that data["vulnerabilities"] exists and is a list (e.g.,
isinstance check) and only then build entries with KEVEntry.model_validate,
cache the serialized entries via cache.set("kev:catalog", ..., TTL_KEV) and
return them; if the shape check fails, raise an exception to avoid caching and
allow the caller to try the fallback source.

In
`@packages/pythinker-review/src/pythinker_review/security_intel/sources/nvd.py`:
- Around line 61-64: The current parsing after await client.get_json(NVD_BASE,
params=params, headers=_headers()) assumes shapes and will raise on malformed
responses; validate types before building CVERecord instances: ensure the
fetched value assigned to data is a dict, extract vulnerabilities =
data.get("vulnerabilities") and ensure it is a list, and for each element ensure
item is a dict before calling CVERecord.model_validate(item.get("cve", {})); if
any check fails raise a clear error (or return/raise to keep the fail-closed
behavior) referencing the variables/data and functions (client.get_json,
NVD_BASE, _headers, data, vulnerabilities, item, CVERecord) so readers can
locate the code.

In
`@packages/pythinker-review/src/pythinker_review/security_intel/sources/vendor.py`:
- Line 3: get_vendor_advisory currently awaits vendor lookups sequentially;
replace the tuple-literal awaits for _msrc/_redhat/_ubuntu with a single
asyncio.gather(...) call to run those coroutines concurrently. Also harden
per-item processing in the vendor-specific helpers: in _redhat, before using
release.get(...) inside the loop over affected_release, guard with
isinstance(release, dict) to skip non-dict items; apply the same pattern in
_msrc by checking each entry in value (e.g., if isinstance(item, dict)) before
accessing its keys/values so malformed entries do not raise during enrichment.
- Around line 62-71: The loop in _redhat() assumes data["affected_release"] is a
list of dicts and directly calls release.get(...), which can raise if the field
is malformed; guard by first retrieving affected_release =
data.get("affected_release") and checking isinstance(affected_release, list) (or
defaulting to []), then iterate over affected_release[:20] but skip or coerce
items that are not dicts (e.g., check isinstance(release, dict) before using
release.get) and use safe defaults for each field; update the block that builds
out.append(...) to only access release.get(...) when release is a dict so
malformed inputs won't raise TypeError/AttributeError.

In
`@packages/pythinker-review/src/pythinker_review/security_scan/dependencies.py`:
- Around line 34-49: parse_dependency_manifests currently only checks manifests
at the given root and thus misses manifests in subdirectories; update
parse_dependency_manifests to support recursive discovery (e.g., add optional
params like recurse: bool = False and include/exclude path globs or
allowlist/denylist) so it walks subdirs and calls the existing helpers
(_parse_requirements, _parse_package_json, _parse_pyproject, _parse_pom_xml) for
any discovered manifest files, then returns _dedupe(packages); alternatively, if
you prefer a smaller change, add a clear docstring to parse_dependency_manifests
calling out the root-only limitation and suggesting using a separate recursive
caller.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 749bb866-a71b-4653-be2e-2adefd0cb352

📥 Commits

Reviewing files that changed from the base of the PR and between dadbbbf and 43a8e2e.

📒 Files selected for processing (27)
  • packages/pythinker-review/README.md
  • packages/pythinker-review/src/pythinker_review/cli/security_scan.py
  • packages/pythinker-review/src/pythinker_review/reviewers/prompts/security_review.system.md
  • packages/pythinker-review/src/pythinker_review/security_intel/__init__.py
  • packages/pythinker-review/src/pythinker_review/security_intel/cache.py
  • packages/pythinker-review/src/pythinker_review/security_intel/client.py
  • packages/pythinker-review/src/pythinker_review/security_intel/models.py
  • packages/pythinker-review/src/pythinker_review/security_intel/risk.py
  • packages/pythinker-review/src/pythinker_review/security_intel/service.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/__init__.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/epss.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/github.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/kev.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/nvd.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/osv.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/vendor.py
  • packages/pythinker-review/src/pythinker_review/security_intel/validators.py
  • packages/pythinker-review/src/pythinker_review/security_scan/dependencies.py
  • packages/pythinker-review/src/pythinker_review/security_scan/processor.py
  • packages/pythinker-review/src/pythinker_review/security_scan/prompts/system.md
  • packages/pythinker-review/src/pythinker_review/security_scan/reporting.py
  • packages/pythinker-review/src/pythinker_review/signals/advisor.py
  • packages/pythinker-review/src/pythinker_review/signals/models.py
  • packages/pythinker-review/src/pythinker_review/signals/scanner.py
  • packages/pythinker-review/tests/unit/test_security_intel.py
  • packages/pythinker-review/tests/unit/test_security_scan_dependencies.py
  • packages/pythinker-review/tests/unit/test_signals.py

Comment thread packages/pythinker-review/README.md Outdated
Comment thread packages/pythinker-review/src/pythinker_review/security_intel/client.py Outdated
…eline

- Wrap asyncio.run() calls in intel CLI commands with try/except; print
  error to stderr and exit 1 on failure (cve + package sub-commands)
- Catch ValueError from malformed Content-Length header in IntelHttpClient
  and raise IntelClientError instead of leaking the raw exception
- Skip score_cve() and set risk=None in lookup_cve_bundle when all four
  sources (NVD, EPSS, KEV, exploit) return None, preventing a synthetic
  LOW risk from being reported when no data is available
- Validate NVD search_cves response is a dict with a list vulnerabilities
  field before building CVERecord instances; skip non-dict items
- Validate KEV catalog response shape before building entries; raise on
  malformed payload so the fallback URL is tried instead of caching empty
- Validate GitHub exploit response shape; return uncached NONE result
  when the response is not a dict with a list 'items' field; guard
  individual repo items with isinstance(repo, dict)
- Run _msrc/_redhat/_ubuntu concurrently via asyncio.gather in
  get_vendor_advisory; add isinstance(entry/release, dict) guards in
  both helpers to skip malformed list items
- Update _REQUIREMENT_RE and _BARE_REQUIREMENT_RE to accept package
  extras (e.g. requests[socks]==2.31.0)
- Update _find_line to skip comment lines and use whole-word regex match
  to avoid false matches on comments or unrelated tokens
- Compute project_info once before spawning batch workers in
  process_project to ensure identical prompt context across concurrent runs
- Cap source_errors at 10 in markdown report; append "and N more errors"
  when truncated
- Expand _DEP_LINE_RE to cover >=, <=, !=, ~=, >, < operators, three-part
  semver, and JSON-style pinned entries
- Extend redirect-handler test to also assert IntelHttpClient wires
  _NoRedirectHandler into its opener
- Add docstring to parse_dependency_manifests noting root-only limitation
- Add inline comment to README intel cve example

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/pythinker-review/src/pythinker_review/security_scan/dependencies.py`:
- Around line 19-21: The regex _REQUIREMENT_RE currently leaves extras (e.g.
"requests[socks]") in match.group(1) and _parse_requirements() forwards that
into PackageRef(name=...), causing validation/OSV lookups to skip the package;
update _parse_requirements() to strip extras when building PackageRef (e.g.
derive base_name = match.group(1).split("[",1)[0] or prefer using
_BARE_REQUIREMENT_RE to capture the plain name) and pass base_name into
PackageRef(name=base_name); add/extend unit test in
tests/unit/test_security_scan_dependencies.py asserting that
"requests[socks]==..." yields PackageRef.name == "requests" so OSV querying is
not skipped.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 74982212-c758-4fef-8742-749fdcfe574c

📥 Commits

Reviewing files that changed from the base of the PR and between 43a8e2e and 8fc9041.

📒 Files selected for processing (14)
  • CHANGELOG.md
  • packages/pythinker-review/README.md
  • packages/pythinker-review/src/pythinker_review/cli/security_scan.py
  • packages/pythinker-review/src/pythinker_review/security_intel/client.py
  • packages/pythinker-review/src/pythinker_review/security_intel/service.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/github.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/kev.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/nvd.py
  • packages/pythinker-review/src/pythinker_review/security_intel/sources/vendor.py
  • packages/pythinker-review/src/pythinker_review/security_scan/dependencies.py
  • packages/pythinker-review/src/pythinker_review/security_scan/processor.py
  • packages/pythinker-review/src/pythinker_review/security_scan/reporting.py
  • packages/pythinker-review/src/pythinker_review/signals/scanner.py
  • packages/pythinker-review/tests/unit/test_security_intel.py

…ement names

- Break long asyncio.run() call lines in intel_cve and intel_package to
  stay under the 100-char limit (E501)
- Add `from exc` to all three bare `raise` statements inside except
  blocks in cli/security_scan.py and client.py (B904)
- Strip package extras (e.g. requests[socks]) from the name in
  _parse_requirements before constructing PackageRef so OSV lookups
  receive a plain package name; bare requirements use the same stripping
- Add test asserting requests[socks]==2.31.0 and urllib3[secure] parse
  to PackageRef.name without brackets
@elkaix
elkaix merged commit e02ffc6 into main Jun 4, 2026
29 checks passed
@elkaix
elkaix deleted the feat/security-intel-review branch June 4, 2026 19:50
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