You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
1. cwd leaks sensitive path information os.getcwd() is included in every cloud request payload. In CI environments this often contains repo names, org names, or secrets embedded in paths. This is exfiltrated to an external SaaS endpoint with no opt-out mechanism. Consider omitting or making it opt-in.
2. hostname included unconditionally platform.node() can reveal internal infrastructure naming conventions. Same concern as cwd — no redaction or opt-out.
3. NODE9_API_KEY check is inconsistent
In evaluate(), the guard is if os.environ.get("NODE9_API_KEY"): — truthy check. In _evaluate_cloud(), an empty string raises RuntimeError. But these are the same code path, so the empty string case in _evaluate_cloud is unreachable. The truthy check in evaluate() already gates it. This is dead code that creates a false sense of validation. The empty-string check should live in evaluate() before dispatch, or the logic should be consolidated.
4. HTTPS-only check is bypassable with https://evil.com
The check api_url.startswith("https://") does nothing meaningful for security. A developer could set NODE9_API_URL=https://attacker.com and all credentials + tool call data get forwarded there. There's no certificate pinning or domain allowlist. For a security library, this warrants at minimum a warning in docs, and ideally a NODE9_API_URL override should be locked to known domains unless an explicit "dev mode" flag is set.
5. ci_context value types are not validated
Keys are allowlisted, but values are passed through unconstrained. A github_head_ref containing a 9MB string or a nested object would still pass. Add per-value type/length validation.
6. _read_ci_context size cap has an off-by-one ambiguity
Reading _CI_CONTEXT_MAX_BYTES + 1 bytes to detect overflow is correct, but then json.loads runs on the full oversized buffer before the length check. The check if len(raw_bytes) > _CI_CONTEXT_MAX_BYTES happens after parsing — this is fine since read() happens before json.loads, but the order (read → length check → json.loads) would be clearer and avoids parsing attacker-controlled JSON unnecessarily.
Correctness & Edge Cases
7. Polling interval is fixed at 1 second with no backoff
For a 600-second default timeout, this is 600 HTTP requests. Under network issues the continue on URLError means all 600 fire regardless. Add exponential backoff with a cap (e.g., max 10s).
8. _CHECK_TIMEOUT = 5 used for initial POST to SaaS
5 seconds is very tight for a network call that may trigger async approval workflows. The initial POST timeout should be distinct from the daemon check timeout, or at least documented. A slow SaaS response will raise URLError and surface a confusing error.
9. int(os.environ.get("NODE9_CLOUD_TIMEOUT", "600")) can raise ValueError
If someone sets NODE9_CLOUD_TIMEOUT=abc, this crashes with an unhandled ValueError inside evaluate(). Wrap in a try/except with a fallback or clear error message.
10. status_url construction f"{api_url}/status/{request_id}" — api_url already has .rstrip("/") applied, and request_id is regex-validated, so this is fine. But if NODE9_API_URL is overridden to a non-standard base path, the status URL construction could be wrong. Minor, but worth noting.
API Design / Usability
11. Removed quickstart docstring from __init__.py
The removal of the usage example is a regression for developer experience. Inline examples in __init__.py are often the first thing developers see. Move them to a docstring on protect if you want to reduce __init__.py noise, but don't drop them entirely.
12. print() used for approval waiting message print(f"🛡️ Node9: waiting for approval...") is inappropriate for a library. Use logging.getLogger("node9").info(...). Library code should never write to stdout unconditionally — it breaks structured logging pipelines and CI log parsers.
Test Coverage Gaps
No tests for NODE9_CLOUD_TIMEOUT invalid value (ValueError path)
No tests for _read_ci_context with oversized file, non-dict JSON, or disallowed keys
No tests for polling loop: APPROVED, DENIED, timeout expiry, transient network errors
No tests verifying HTTPS enforcement and that HTTP URLs are rejected
No test for the unreachable empty-API-key branch in _evaluate_cloud
Workflow Change
The paths-ignore addition for ai-review.yml and scripts/ai-review.mjs is reasonable to prevent infinite loops. No issues.
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
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.
Auto-generated PR
Merge latest
devchanges intomainto trigger a release.