Skip to content

feat: fix confirmed findings during scans with native agents - #1365

Open
yoni-at-strix wants to merge 39 commits into
mainfrom
devin/1790308365-verified-fix-preparation
Open

yoni-at-strix wants to merge 39 commits into
mainfrom
devin/1790308365-verified-fix-preparation

Conversation

@yoni-at-strix

@yoni-at-strix yoni-at-strix commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Make fix preparation a deterministic scan-runtime responsibility: persisted source-backed findings enqueue exactly once per (finding_id, candidate_digest), dispatch safely from worker threads onto the owning event loop, replay after restart, and reschedule revised candidates.

Before an artifact becomes READY, a separate reviewer adversarially checks the reported security invariant, bypasses, behavior compatibility, and dependency/toolchain constraints. Readiness is bound to the unchanged final workspace digest, so reviewer mutations or approvals for a stale snapshot fail closed.

For open-source source scans, automatic fixes are enabled by default and can be disabled with --no-auto-fix. After assessment and Fix reconciliation finish, each current approved artifact becomes an idempotent local branch based on the exact scanned commit. Strix does not change the active checkout or push the branch. Final CLI output and run.json list prepared branches and any publication errors.

Validation: 103 affected tests passed; Ruff, mypy, Bandit, formatting, and pre-commit hooks passed. Repository-wide checks still encounter existing unrelated Ruff and Pyright baseline failures.

Part of STR-738

Link to Devin session: https://app.devin.ai/sessions/34237f615e7747eeb6473128f0ab86e3
Open in Devin Desktop: https://app.devin.ai/desktop/session/34237f615e7747eeb6473128f0ab86e3?variant=devin
Requested by: @yoni-at-strix

@yoni-at-strix

Copy link
Copy Markdown
Contributor Author

@greptile

@yoni-at-strix

Copy link
Copy Markdown
Contributor Author

@strix

@strix-security

strix-security Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Strix Security Review

All previously reported security findings have been resolved.

2 resolved findings
Review summary

Reviewed the new verified fix-preparation subsystem (strix/fix/contracts.py, locations.py, prepare.py, workspace.py, runtime.py, scan.py), the agent/runtime plumbing it introduces (strix/runtime/agent_session.py, core/execution.py, core/runner.py, core/hooks.py, tools/finish, tools/reporting, tools/processes, session_manager, docker_client, backends), the fix CLI entry points, report/state/SARIF changes, and secret-file handling.

The change is defensively constructed: all subprocess and git invocations use argv lists (no shell interpolation); source and checkpoint paths are containment-checked with resolve()+is_relative_to() and reject traversal; tar/zip archives are unpacked via getmember/extractfile rather than extractall; network isolation is enforced at the Docker layer via network_mode="none" with port removal; and secret outputs use an atomic 0600 write path. The two previously reported issues — unenforced network_allowed on the command sandbox and symlink-following path traversal — are resolved in the current code. New prompt surfaces for the Fix agent and independent verifier fence repository/finding/tool data as untrusted with randomized boundaries.

No confirmed vulnerabilities were identified in the changed code.

Updated for 9edb2ae.


Reviewed by Strix
Re-run review · Configure security review settings

@greptile-apps

greptile-apps Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[High risk] Adds automated fix generation and verification workflow to the scanning system.

The PR appears safe to merge based on the changes since the previous review.

What we checked:

  • PR reviews start fix jobs: The runner checks for pr_review before it creates the fix manager, and the new test covers that choice on both new and resumed scans.

Summary

Strix now prepares fixes for confirmed source findings during scans, then checks each patch with a separate reviewer before making it available. Open-source source scans can create local branches from the scanned commit, and a new strix fix command runs the same preparation flow on its own.

  • Saved findings can start native Fix agents in separate worktrees, including after a scan resumes.
  • Independent review must approve the unchanged final patch before Strix marks it ready.
  • CLI, report, and TUI updates show fix progress and prepared branch results.

Reviews (15) · Last reviewed commit: "fix: disable automatic fix agents for PR..."

Comment thread strix/report/sarif.py
Comment thread strix/fix/prepare.py Outdated
Comment thread strix/tools/reporting/tool.py Outdated
Comment thread strix/fix/prepare.py
Comment thread strix/fix/prepare.py Outdated
Comment thread strix/fix/prepare.py Outdated
Comment thread strix/fix/prepare.py Outdated
@greptile-apps

greptile-apps Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Comments Outside Diff

These findings could not be posted inline.

  • P1 TUI setup skips automatic fixes strix/interface/cli_args.py:384 ▶

    When someone opens the TUI without a target and chooses a source target there, parse_arguments returns before setting the default for auto_fix. The TUI treats that unset value as False, so the scan starts no Fix agents and creates no fix branches. Set the default before this return.

@strix-security strix-security 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.

Strix flagged 2 new security findings below. See the pinned summary comment for the full PR status.

Comment thread strix/fix/prepare.py Outdated
Comment thread strix/fix/contracts.py Outdated
Comment thread strix/fix/prepare.py Outdated
Comment thread strix/fix/locations.py Outdated
- Resolve edit/anchor/manifest paths and require workspace containment so
  committed symlinks cannot redirect reads or writes outside the checkout.
- Treat unreadable or non-UTF-8 anchor targets as missing instead of
  raising, and never let candidate anchoring block report persistence.
- Enforce the declared command policy: subprocess env is an allowlist plus
  credentials_allowed, and commands run in a network namespace (unshare)
  when network_allowed is false, or are rejected when isolation is
  unavailable.
- Require a clean worktree in addition to a matching HEAD commit so
  pre-existing uncommitted changes are not attributed to the fix.
- Expand untracked directories into per-file manifest entries.
- Surface failed optional checks as gaps instead of silent readiness.
- SARIF fixes emit only the verified candidate (digest must match the
  recorded fix_candidate), not the stale draft locations.
@devin-ai-integration

Copy link
Copy Markdown
Contributor

Fixed in 49eca20. Every consumer path now resolves and containment-checks before touching the filesystem: anchor_location reads through _read_anchor_source (resolve + is_relative_to + UTF-8/decode guard), _apply_edits resolves each edit path and raises when it escapes the resolved workspace, and build_git_manifest only hashes files whose resolved path stays inside the workspace (directory expansion is containment-checked per file too).

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Fixed in 49eca20. anchor_location no longer raises on unreadable or non-UTF-8 files (they anchor as missing and surface as candidate gaps), and _build_fix_candidate now catches any residual anchoring failure and keeps the un-anchored candidate with a known_gaps entry, so report persistence is never blocked by an unusable candidate.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Fixed in 49eca20. git status now runs with --untracked-files=all so new directories expand to per-file records, and any residual ?? directory entry is expanded recursively (with per-file workspace containment) so every new file gets a manifest entry and hash.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Fixed in 49eca20. _verify_source now requires a clean worktree (git status --porcelain must be empty) in addition to the matching HEAD commit, so a checkout carrying pre-existing uncommitted changes is rejected as stale rather than folded into the prepared manifest.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Fixed in 49eca20. Non-required checks that did not pass now contribute name: optional check <status> entries to gaps, which demotes the result to ready_with_gaps (or needs_review) so the failure is visible on the result instead of disappearing into a ready.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Fixed in 49eca20. The request's policy is now threaded into the default command runner: the subprocess environment is an allowlist (PATH/HOME/TEMP/VIRTUAL_ENV/etc.) plus only the variables named in credentials_allowed, and when network_allowed is false commands run under unshare in an empty network namespace — or are marked unavailable rather than executed when the platform cannot isolate egress.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Fixed in 49eca20. run_command no longer passes **os.environ: the subprocess environment is built from an explicit allowlist plus only the names listed in the request's credentials_allowed. The request's network_allowed is enforced too — when false, commands execute inside an unshare network namespace (probed once), and are returned unavailable instead of running when isolation isn't possible. prepare_fix binds both fields into the default runner for check and reproduction commands.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Fixed in 49eca20 — same change as the thread on prepare.py: both fields are now read by prepare_fix and bound into the default run_command (env allowlist + credentials_allowed passthrough; network_allowed=False runs commands under unshare or rejects them when isolation is unavailable).

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Fixed in 49eca20. _apply_edits now resolves each edit path and raises ValueError when the resolved target is outside the resolved workspace, before any read or write. The same realpath containment was applied in anchor_location (escaping paths anchor as missing) and build_git_manifest (content is hashed only when the resolved path stays inside the workspace).

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Fixed in 49eca20. anchor_location now reads through _read_anchor_source, which resolves the path, requires it to stay inside the resolved root, and returns missing for escapes or non-UTF-8/unreadable files — so the read primitive can no longer be redirected through a committed symlink.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

This gate is the point of the PR, not a bug: SARIF fixes are automatically applicable, so emitting them for an unverified draft was the unsafe behavior being removed. prepare_fix is the engine's exported entrypoint — the preparation orchestrator runs it post-scan and writes the result back through update_vulnerability_report (fix_preparation became an updatable field in this PR), so a ready result does reach SARIF once preparation runs. In 49eca20 the gate also verifies candidate_digest against the stored fix_candidate and emits exactly the verified candidate's edits, so a ready result can never offer draft replacements that differ from what was actually verified.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

@greptile

@devin-ai-integration

Copy link
Copy Markdown
Contributor

@strix-security

Comment thread strix/tools/reporting/tool.py Outdated
Comment thread strix/report/sarif.py
A report revision whose locations can no longer form a candidate now
still supersedes any recorded preparation instead of leaving a ready
result pointing at superseded locations.

Preparation results carry the candidate that was actually verified so
SARIF emits the anchor-corrected edits rather than comparing the stored
draft's digest against a re-anchored one.
@devin-ai-integration

Copy link
Copy Markdown
Contributor

Fixed in 2b413da. _refresh_fix_candidate now supersedes the recorded preparation whenever candidate-affecting fields change and the report carries a fix candidate or preparation — even when the revised fields can't rebuild one (e.g. ./file.py locations). The stale ready result can no longer outlive the locations it was prepared against.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Fixed in 2b413da. FixPreparationResultV1 now carries the candidate preparation actually verified (result.candidate, with candidate_digest binding it), so SARIF emits the anchor-corrected candidate's edits directly instead of digest-matching the stored draft against the re-anchored one. Stored fix_candidate remains only a fallback for write-backs that predate this field.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

@greptile

Comment thread strix/report/sarif.py
Repair can change files beyond the candidate's draft edits while the
manifest still verifies. Comparing the applied draft hashes against the
final manifest now demotes the result to ready_with_gaps, so SARIF and
other auto-apply consumers never offer a fix that omits verified
changes.
@devin-ai-integration

Copy link
Copy Markdown
Contributor

Fixed in be1c2e4. Right after _apply_edits, preparation now hashes each edited file, and compares that set against the final manifest: any extra file, revised edit, or reverted edit yields the gap "The verified change extends beyond the recorded draft edits." and demotes the result to ready_with_gaps — which the SARIF gate already refuses to auto-apply. So a one-click fix can now only ever equal the change set preparation verified; anything wider stays visible via the gap and the manifest.

Comment thread strix/tools/reporting/tool.py Outdated

@strix-security strix-security 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.

Strix flagged a new security finding below. See the pinned summary comment for the full PR status.

Comment thread strix/fix/runtime.py
Comment thread strix/fix/runtime.py Outdated
Comment thread strix/fix/contracts.py
Comment thread strix/interface/fix_cli.py
@yoni-at-strix yoni-at-strix changed the title feat: prepare and review security fixes in OSS Strix feat: fix confirmed findings during scans with native agents Sep 30, 2026
@yoni-at-strix

Copy link
Copy Markdown
Contributor Author

@strix

@yoni-at-strix

Copy link
Copy Markdown
Contributor Author

@greptile

Comment thread strix/fix/prepare.py Outdated
Comment thread strix/fix/scan.py
Comment thread strix/interface/fix_cli.py Outdated
@yoni-at-strix

Copy link
Copy Markdown
Contributor Author

@greptile

@yoni-at-strix

Copy link
Copy Markdown
Contributor Author

@strix

@yoni-at-strix

Copy link
Copy Markdown
Contributor Author

@strix

@yoni-at-strix

Copy link
Copy Markdown
Contributor Author

@greptile

@yoni-at-strix

Copy link
Copy Markdown
Contributor Author

@greptile

@yoni-at-strix

Copy link
Copy Markdown
Contributor Author

@greptile

Comment thread strix/core/runner.py
Comment thread strix/fix/scan.py
@yoni-at-strix

Copy link
Copy Markdown
Contributor Author

@greptile

@yoni-at-strix

Copy link
Copy Markdown
Contributor Author

@strix

@yoni-at-strix

Copy link
Copy Markdown
Contributor Author

@greptile

This branch has not been deployed

No deployments
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