Skip to content

chore: security CI + lint/pre-commit config + NEXT_STEPS roadmap (+ exit-127 fix)#2

Merged
gesh75 merged 1 commit into
mainfrom
claude/next-steps-hardening
Jun 28, 2026
Merged

chore: security CI + lint/pre-commit config + NEXT_STEPS roadmap (+ exit-127 fix)#2
gesh75 merged 1 commit into
mainfrom
claude/next-steps-hardening

Conversation

@gesh75

@gesh75 gesh75 commented Jun 28, 2026

Copy link
Copy Markdown
Owner

Summary

Implements the immediate, fully-verifiable items from the EXA-researched hardening roadmap and lands the roadmap itself as aegis/docs/NEXT_STEPS.md. Larger architectural items are tracked as issues (linked below).

What's in this PR

P0 — Supply-chain & CI (the repo had no CI at all)

  • .github/workflows/ci.ymlpytest + Bandit (medium+) as blocking gates; pip-audit + detect-secrets informational. Every Action pinned to a full commit SHA, least-privilege permissions: contents: read (OpenSSF post-tj-actions guidance).
  • aegis/pyproject.toml — central Bandit / Ruff / pytest config (high-signal rule set: F,I,B,UP,S).
  • .pre-commit-config.yaml — shift-left mirror of the CI gates.

P2.8 — Correctness fix (deferred from the previous review)

  • The orchestrator no longer conflates "binary missing" with "tool ran and exited 127." ExecResult gains a tool_missing flag the sandbox sets only when a binary was never launched; DockerSandbox keeps its 127 heuristic, LocalSandbox flags synthesized-missing only. New tests in tests/test_sandbox.py.

Lint hygiene (to make the new gates pass on existing code, no behavior change)

  • Justified # nosec on the vetted urlopen (fixed scheme) and defusedxml call sites; # noqa for the lab default cred and best-effort error-body read.
  • ruff --fix import-sorting / pyupgrade cleanups across a few modules.

Documentation

  • aegis/docs/NEXT_STEPS.md — the full prioritized roadmap with best-practice citations (OWASP AI Agent Security, ROE Gate, Aikido, IntegSec, OpenSSF, tamper-evident-audit-log sources).

Tracked as follow-up issues (not in this PR)

  • P0.3 dependency hash-pinning + SBOM, then flip pip-audit to blocking
  • P1.4 out-of-band HMAC signer / key isolation
  • P1.5 external anchoring + WORM for the audit chain
  • P1.6 risk-tiered approval gate for --sandbox local / --arm
  • P1.7 network-layer egress control for the local path
  • P2.10 pre-flight "is this production?" checks

Testing

  • Full suite: 81 passed (was 79; +2 net new tests).
  • Bandit (medium+) and Ruff clean locally; CLI dry-run smoke OK.

🤖 Generated with Claude Code


Generated by Claude Code

Summary by Sourcery

Introduce security-focused CI, linting, and pre-commit configuration, add a hardening roadmap doc, and fix orchestrator handling of missing tools vs real exit-127 failures.

New Features:

  • Add a GitHub Actions CI pipeline running tests, Bandit, pip-audit, and detect-secrets for supply-chain and security checks.
  • Add centralized tool configuration via pyproject.toml and a pre-commit setup mirroring CI gates.
  • Add NEXT_STEPS.md documenting the prioritized security and operational hardening roadmap.

Bug Fixes:

  • Correctly distinguish between missing binaries and tools that actually exit with code 127, ensuring the orchestrator reports tool availability accurately.

Enhancements:

  • Tighten sandbox and orchestration result handling with a tool_missing flag propagated from local and docker sandboxes.
  • Apply Ruff-driven lint and type-hint cleanups, plus justified security rule exemptions for vetted urllib and defusedxml usage and lab default credentials.

CI:

  • Add a hardened CI workflow with pinned action SHAs, least-privilege permissions, and separate jobs for tests, SAST, dependency audit, and secret scanning.

Documentation:

  • Add a NEXT_STEPS roadmap outlining recommended security, audit, and operational improvements with external best-practice references.

Tests:

  • Extend sandbox tests to cover empty argv handling, real 127 exits, and missing-binary behavior, keeping test doubles in sync with ExecResult.

Chores:

  • Remove unused imports and minor code hygiene updates to satisfy new linting rules.

…-fix

Implements the EXA-researched hardening roadmap (aegis/docs/NEXT_STEPS.md).

CI / supply-chain (P0):
- .github/workflows/ci.yml — pytest + Bandit(medium+) blocking; pip-audit +
  detect-secrets informational. Every Action pinned to a full commit SHA,
  least-privilege permissions (OpenSSF post-tj-actions guidance).
- aegis/pyproject.toml — central Bandit/Ruff/pytest config (high-signal rule set).
- .pre-commit-config.yaml — shift-left mirror of the CI gates.

Correctness (P2.8): orchestrator no longer conflates "binary missing" with
"tool ran and exited 127". ExecResult gains a tool_missing flag the sandbox sets
only when a binary was never launched; DockerSandbox keeps its 127 heuristic,
LocalSandbox flags synthesized-missing only. Covered by new tests.

Lint hygiene to make the gates pass on existing code: justified # nosec on the
vetted urlopen (fixed scheme) and defusedxml call sites, # noqa for the lab
default cred and best-effort error-body read, and ruff --fix import/pyupgrade
cleanups. No behavior change.

Larger architectural items (out-of-band signer, WORM anchoring, approval gate,
network egress, dependency hash-pinning) are tracked as issues per NEXT_STEPS.

Full suite: 81 passed. Ruff + Bandit(medium+) clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PH7w1wBoUxFESq9gh6yjAQ
@sourcery-ai

sourcery-ai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a hardened security-focused CI pipeline and shift-left linting config, introduces a tool_missing flag to fix exit-code 127 handling in sandboxes and orchestrator, and updates documentation and minor code hygiene to satisfy new security lint rules.

Sequence diagram for updated sandbox exit-127 handling

sequenceDiagram
    participant Orchestrator as Orchestrator
    participant SandboxLocal as LocalSandbox
    participant SandboxDocker as DockerSandbox
    participant Subprocess as subprocess.Popen

    Orchestrator->>SandboxLocal: run(argv, timeout)
    alt [empty argv]
        SandboxLocal-->>Orchestrator: ExecResult(exit_code=127, tool_missing=True)
    else [argv[0] not on PATH]
        SandboxLocal-->>Orchestrator: ExecResult(exit_code=127, tool_missing=True)
    else [binary found]
        SandboxLocal->>Subprocess: Popen(argv, env=_safe_env)
        Subprocess-->>SandboxLocal: returncode, stdout, stderr
        SandboxLocal-->>Orchestrator: ExecResult(exit_code, tool_missing=False)
    end

    Orchestrator->>SandboxDocker: run(argv, timeout)
    SandboxDocker->>Subprocess: Popen(docker compose exec ...)
    Subprocess-->>SandboxDocker: returncode, stdout, stderr
    SandboxDocker->>SandboxDocker: [exit_code == 127 and not timed_out] set tool_missing=True
    SandboxDocker-->>Orchestrator: ExecResult(exit_code, tool_missing)

    Orchestrator->>Orchestrator: guard.record(tool.binary, exit_code, summary)
    alt [ex.tool_missing]
        Orchestrator->>Orchestrator: result.errors.append("tool unavailable in sandbox")
    else [not ex.tool_missing]
        Orchestrator->>Orchestrator: tool.parse(stdout, target)
    end
Loading

File-Level Changes

Change Details Files
Introduce security-focused CI pipeline with tests, SAST, dependency audit, and secret scanning, plus matching local pre-commit and centralized tool configuration.
  • Add GitHub Actions workflow that runs pytest, Bandit, pip-audit, and detect-secrets with pinned action SHAs and least-privilege permissions
  • Add pyproject.toml configuring Bandit to exclude tests, Ruff linting rules, and pytest defaults
  • Add pre-commit configuration mirroring CI gates with hooks for Ruff and Bandit on project files
.github/workflows/ci.yml
.pre-commit-config.yaml
aegis/pyproject.toml
Fix sandbox and orchestrator semantics so exit code 127 is only treated as a missing tool when the binary was never launched.
  • Extend ExecResult with a tool_missing flag and set it when subprocess.Popen raises FileNotFoundError
  • Update LocalSandbox to mark empty argv and non-existent host binaries as tool_missing while leaving real 127 exits unflagged
  • Keep DockerSandbox heuristic that maps non-timeout exit 127 to tool_missing
  • Change orchestrator error handling to rely on tool_missing instead of raw exit code 127 for "tool unavailable" messaging
  • Add new sandbox tests for empty argv, real 127 exits, and missing binaries; adjust integration test fake sandbox to include tool_missing
aegis/aegis/sandbox.py
aegis/aegis/orchestrator.py
aegis/tests/test_sandbox.py
aegis/tests/test_integration_agentic.py
Align existing code with new security lint rules via targeted nosec/noqa annotations, minor refactors, and cleanup of unused imports.
  • Annotate vetted urllib.request usage to suppress Bandit URL warnings with documented fixed schemes
  • Annotate defusedxml ET.fromstring usage and lab default credential fields to justify security rule exemptions
  • Adjust exception handling in web recon to mark best-effort error-body reads and maintain Bandit expectations
  • Tighten type hints and remove unused imports in configuration and reporting modules
aegis/aegis/ai_analyzer.py
aegis/aegis/recon/web.py
aegis/aegis/config.py
aegis/aegis/reporting.py
aegis/aegis/tools.py
aegis/aegis/web.py
Document the hardening roadmap and next operational security steps for the project.
  • Add NEXT_STEPS.md describing prioritized supply-chain, CI, audit-integrity, authorization, and operational hardening tasks with external best-practice citations
aegis/docs/NEXT_STEPS.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@gesh75
gesh75 marked this pull request as ready for review June 28, 2026 19:05
@gesh75
gesh75 merged commit 2303f05 into main Jun 28, 2026
5 checks passed

@sourcery-ai sourcery-ai 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.

Hey - I've found 2 issues, and left some high level feedback:

  • In the secrets job, the detect-secrets scan aegis targets invocation assumes a targets directory exists; consider scoping the scan to actual repo paths (e.g. just aegis or using a glob) to avoid noisy failures or scanning non-existent paths.
  • The DockerSandbox heuristic sets tool_missing=True for every non-timeout 127 from docker exec, which means genuine in-container exit 127 is still surfaced as "tool unavailable"; if possible, consider tightening this by checking for known missing-binary patterns or adding an escape hatch for tools that legitimately use 127.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the `secrets` job, the `detect-secrets scan aegis targets` invocation assumes a `targets` directory exists; consider scoping the scan to actual repo paths (e.g. just `aegis` or using a glob) to avoid noisy failures or scanning non-existent paths.
- The DockerSandbox heuristic sets `tool_missing=True` for every non-timeout 127 from `docker exec`, which means genuine in-container exit 127 is still surfaced as "tool unavailable"; if possible, consider tightening this by checking for known missing-binary patterns or adding an escape hatch for tools that legitimately use 127.

## Individual Comments

### Comment 1
<location path=".github/workflows/ci.yml" line_range="77-78" />
<code_context>
+          python-version: "3.12"
+      - name: Install detect-secrets
+        run: pip install detect-secrets==1.5.0
+      - name: Scan working tree for secrets
+        run: detect-secrets scan aegis targets
</code_context>
<issue_to_address>
**🚨 suggestion (security):** The paths passed to `detect-secrets` depend on the workflow-level working directory and may not cover the whole repo as intended.

With `defaults.run.working-directory` set to `aegis`, `detect-secrets scan aegis targets` actually scans `aegis/aegis` and `aegis/targets` from the repo root, and skips top-level paths like `.github` or any non-`aegis` directories. If you want full-repo coverage, consider removing the workflow-level `working-directory` for this job or adjusting the command to scan from the repo root (e.g., `detect-secrets scan . ..` or explicitly listing root-level directories).
</issue_to_address>

### Comment 2
<location path="aegis/docs/NEXT_STEPS.md" line_range="26" />
<code_context>
+
+> *Best practice:* the 4-gate Python DevSecOps pattern (SAST + secret-scan + AST + dep-audit)
+> is the consensus baseline; CI is "the authoritative gate" because pre-commit can be skipped
+> with `--no-verify`. — `thunderstornX/secure-python-pipeline-template`,
+> `developmentseed/action-python-security-auditing`.
+
</code_context>
<issue_to_address>
**suggestion (typo):** Consider double-checking the spelling of `thunderstornX` in this reference.

The identifier `thunderstornX` appears to be misspelled (likely `thunderstormX` or similar). Since this references a GitHub repo/template, please confirm the exact name so readers can locate the correct resource.

Suggested implementation:

```
> *Best practice:* the 4-gate Python DevSecOps pattern (SAST + secret-scan + AST + dep-audit)
> is the consensus baseline; CI is "the authoritative gate" because pre-commit can be skipped
> with `--no-verify`. — `secure-python-pipeline-template`,
> `developmentseed/action-python-security-auditing`.

```

If the intention is to point to a specific GitHub repository owner, replace `secure-python-pipeline-template` with an explicit Markdown link once the exact `owner/repo` is confirmed, for example:
- \[`secure-python-pipeline-template`](https://github.com/OWNER/secure-python-pipeline-template)
This ensures readers can directly navigate to the referenced template without ambiguity.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread .github/workflows/ci.yml
Comment on lines +77 to +78
- name: Scan working tree for secrets
run: detect-secrets scan aegis targets

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 suggestion (security): The paths passed to detect-secrets depend on the workflow-level working directory and may not cover the whole repo as intended.

With defaults.run.working-directory set to aegis, detect-secrets scan aegis targets actually scans aegis/aegis and aegis/targets from the repo root, and skips top-level paths like .github or any non-aegis directories. If you want full-repo coverage, consider removing the workflow-level working-directory for this job or adjusting the command to scan from the repo root (e.g., detect-secrets scan . .. or explicitly listing root-level directories).

Comment thread aegis/docs/NEXT_STEPS.md

> *Best practice:* the 4-gate Python DevSecOps pattern (SAST + secret-scan + AST + dep-audit)
> is the consensus baseline; CI is "the authoritative gate" because pre-commit can be skipped
> with `--no-verify`. — `thunderstornX/secure-python-pipeline-template`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (typo): Consider double-checking the spelling of thunderstornX in this reference.

The identifier thunderstornX appears to be misspelled (likely thunderstormX or similar). Since this references a GitHub repo/template, please confirm the exact name so readers can locate the correct resource.

Suggested implementation:

> *Best practice:* the 4-gate Python DevSecOps pattern (SAST + secret-scan + AST + dep-audit)
> is the consensus baseline; CI is "the authoritative gate" because pre-commit can be skipped
> with `--no-verify`. — `secure-python-pipeline-template`,
> `developmentseed/action-python-security-auditing`.

If the intention is to point to a specific GitHub repository owner, replace secure-python-pipeline-template with an explicit Markdown link once the exact owner/repo is confirmed, for example:

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.

2 participants