Resolve the "changes required" review on the CSPT PR:
- SARIF: add a shared `_class_token` helper preferring the structured
`finding_class` over a title keyword, used in BOTH `_class_fingerprint` and
the synthetic branch of `_primary_fingerprint`, so a locationless CSPT finding
no longer collapses onto a server-side traversal's primary fingerprint.
Require CSPT reports to carry a discriminator (endpoint/method or a code
location) so multiple locationless CSPT findings stay distinct.
- Dedup: include `finding_class` in the candidate and in
`_prepare_report_for_comparison`; teach the judge that a differing
finding_class under a shared CWE is not a duplicate.
- Skill/runbook: correct the %2f guidance — the browser does not decode `%2f`
during normalization; only literal-slash dot-segments traverse, and
`%2f`-based traversal is an app-layer decode. Switch fragment examples to raw
`../../`. Split confirmation into a CSPT *primitive* (path steered) vs. an
*impact* level (credentials/reach/chain) to stop false negatives. Fix
non-loadable skill names to the underscore keys (`client_side_path_traversal`,
`path_traversal_lfi_rfi`), including the skill frontmatter.
- Fixtures: drive `positive_normalized` from the fragment so it serves under a
plain static server.
- Tests: add `test_cspt_browser_capture.py` — headless Chromium (Playwright) +
a local request-capture server asserting the exact outbound path per positive
and no traversal per negative, plus WHATWG ground-truth tests proving raw
`../../` and `%2e%2e/` collapse while un-decoded `..%2f..%2f` does not. Add
`test_cspt_dataflow.py` — a deterministic taint tracker that fails when a
fixture's source and sink are present but unconnected. Add primary-fingerprint
and dedup regressions. Add playwright to dev deps.
- Docs: list the CSPT skill in the public skills catalog.
Known follow-up (not blocking): `uv.lock` is left at format revision 2; run
`uv lock` with the repo's pinned uv to add the playwright dev dep without the
unrelated revision-2->3 rewrite. There is no test CI workflow yet; when one is
added it should run `uv run playwright install chromium` so the browser tests
execute (they importorskip otherwise).
Resolve the "changes required" review on the CSPT PR:
- SARIF: add a shared `_class_token` helper preferring the structured
`finding_class` over a title keyword, and use it in BOTH `_class_fingerprint`
and the synthetic branch of `_primary_fingerprint`, so a locationless CSPT
finding no longer collapses onto a server-side traversal's primary
fingerprint. Require CSPT reports to carry a discriminator (endpoint/method or
a code location) so multiple locationless CSPT findings stay distinct.
- Dedup: include `finding_class` in the candidate and in
`_prepare_report_for_comparison`, and teach the judge that a differing
finding_class under a shared CWE is not a duplicate.
- Skill/runbook: correct the %2f guidance — the browser does not decode `%2f`
during normalization; only literal-slash dot-segments traverse, and
`%2f`-based traversal is an app-layer decode. Switch fragment examples to raw
`../../`. Split confirmation into a CSPT *primitive* (path steered) vs. an
*impact* level (credentials/reach/chain) to stop false negatives. Fix
non-loadable skill names (`client_side_path_traversal`,
`path_traversal_lfi_rfi`).
- Fixtures: drive `positive_normalized` from the fragment so it serves under a
plain static server.
- Tests: add `test_cspt_browser_capture.py` (headless Chromium + local
request-capture server asserting the exact outbound path per positive and no
traversal per negative) and `test_cspt_dataflow.py` (deterministic taint
tracker that fails when source and sink are present but unconnected); add
primary-fingerprint and dedup regressions. Add playwright to dev deps.
- Docs: list the CSPT skill in the public skills catalog.
- SARIF class fingerprint now prefers the structured finding_class over
title-keyword matching, so a CSPT finding whose title contains "path
traversal" no longer collapses onto the server-side traversal fingerprint
- Correct documented normalized paths in positive_direct/positive_encoded
fixtures, the evidence runbook, and the skill (leading ../ segments pop
both path prefixes, e.g. /admin/keys/detail, not /api/admin/keys)
- Add regression tests covering the fingerprint precedence and the corrected
fixture paths
Addresses PR #789 review (Closes#776)
An httpx.Timeout in ModelSettings.extra_args crashes
ModelSettings.to_json_dict() (PydanticSerializationError) on the Chat
Completions and LiteLLM model paths, which serialize settings for their
tracing generation span — failing every model turn on those paths. Pass
the timeout as a plain float, which httpx-based clients apply as the
read (inactivity) timeout.
The SDK's http_status retry policy only retries errors carrying a known
HTTP status code, but quota/billing (and other provider-side) failures
often surface inside a streamed response as a bare error with no status
code, so they were failing on the first attempt. Add a statusless retry
policy to DEFAULT_MODEL_RETRY (retry count and backoff unchanged) so they
are retried before a genuine exhaustion fails the run; user aborts are
never retried.
The SDK's http_status retry policy only retries errors carrying a known
HTTP status code, but quota/billing (and other provider-side) failures
often surface inside a streamed response as a bare error with no status
code, so they were failing on the first attempt. Add a statusless retry
policy to DEFAULT_MODEL_RETRY so they are retried (before any content is
streamed; user aborts are never retried), restoring the pre-SDK engine's
resilience. If the provider is genuinely exhausted, the error still
propagates and fails the scan after retries.
* fix(proxy,tooling): serialize+reconnect Caido client, actionable HTTPQL errors, sandbox tool guidance
Addresses the top recurring agent tool-call failures observed in telemetry:
- proxy: the shared Caido client had no locking or reconnect, so concurrent
agent calls raced ("Transport is already connected") and a dead transport
poisoned the rest of the run ("Connector is closed"/"Server disconnected").
Add an asyncio lock + bounded reconnect in caido_api.call_with_client (sandbox
path) and a scan-wide caido_lock in the run context that host-side proxy tools
hold around every call. Deterministic errors are not retried.
- proxy: list_requests now returns Caido's exact parser message, echoes the
offending query, and includes a corrected-syntax hint so agents self-correct
instead of retrying a broken HTTPQL filter.
- shell/prompt: document that write_stdin requires a process started with
tty=true; nudge toward writing Python to a file over deeply-nested one-liners;
note the venv pre-installs common libs.
- agent-browser: distinguish daemon/connection failures (run doctor, don't loop)
from malformed commands; invoke directly (no sh -c wrapper).
- containers: use POSIX '.' instead of the bashism 'source' in generated rc
files (fixes 'sh: source: not found'); add file + xxd and pre-install
requests/httpx/beautifulsoup4/lxml/pyjwt/cryptography in the sandbox venv.
- tests: cover proxy serialization/reconnect/no-retry and HTTPQL errors.
* fix(proxy): host-side reconnect, close stale clients, don't retry mutations
Addresses Greptile review on the reconnect logic:
- Host path had no reconnect: a dead shared context client (Caido restart /
network blip) previously disabled proxy tools for the rest of the scan. Add
SharedCaidoClient, a serialized reconnect-safe holder stored once per scan in
the run context and shared across agents. On a dead transport it rebuilds via
reconnect_caido, which re-selects the SAME Caido project (preserving captured
traffic) instead of creating a new empty one.
- Don't repeat completed mutations: call_with_client / SharedCaidoClient.call
take idempotent=. Reads retry once on reconnect; replay + scope
create/update/delete heal the client but re-raise instead of risking a
double-apply.
- Don't leak replaced clients: the stale client is aclose()d (best-effort) on
every reconnect.
- Extend tests to cover close-on-reconnect, non-idempotent re-raise, and the
SharedCaidoClient holder.
* fix(proxy): close replacement Caido client when project.select fails
Addresses Greptile P1: in reconnect_caido (and bootstrap_caido) a successful
connect() followed by a failing project.select()/create() discarded the
connected client without closing it, so a missing/unavailable project could
leak a transport on every retry. Close the client before re-raising.
---------
Co-authored-by: Alex Schapiro <bearsyankees@gmail.com>
* fix(prompt): treat demo/sample data and demo environments as low severity or skip
* Update system_prompt.jinja
* fix(prompt): use demo context as a skip signal, not a CVSS override
* fix(prompt): let demo context honestly inform CVSS impact metrics
* fix(prompt): focus on detecting demo environments to inform CVSS impact
* fix(prompt): keep demo-environment check concise
* fix(prompt): trim demo-environment check to a short addendum
---------
Co-authored-by: Alex Schapiro <bearsyankees@gmail.com>
Co-authored-by: alex s <46074070+bearsyankees@users.noreply.github.com>
The sandbox never creates /workspace/scratch, so guidance pointing agents
there failed on first write. Make the Python/exec_command and recon
output-hygiene guidance path-agnostic (write to a file, relative to the
working dir) instead of naming a directory that may not exist.
Per projectdiscovery katana docs, add the flags that actually bound
crawl output size and a reduce-then-delete workflow:
- -mdp (max-domain-pages; default is unlimited), -fsu (filter-similar),
-fs scope, -f url (URL-only), -or/-ob (omit raw/body), -mrs.
- Baseline now includes -mdp 2000 -fsu; new 'Keeping output small'
section: bound scope/volume, shrink records, distil then delete raw
crawls.
Address Greptile review:
- system_prompt: only clean up your own task's files; don't delete
another agent's files in the shared workspace unless confirmed unused.
- katana.md: extract+dedupe URLs with jq before removing raw .jsonl
(sort -u on JSONL compares whole records, not URLs).
Add lightweight, always-on disk-hygiene guidance so agents keep recon
artifacts bounded on the shared /workspace instead of writing very large
uncapped crawl output.
- system_prompt.jinja: DISK & SCRATCH HYGIENE note in the shared-workspace
block; recon PHASE 1 crawl bullet asks to bound each crawl and tidy up.
- skills/tooling/katana.md: bound the baseline/deep examples with -ct,
add a Keeping-output-manageable note (bound by -ct/-d, reserve -jsl/-kf
all for narrowed targets, check du -sh, dedupe and remove raw .jsonl).
Apply cgroup caps (mem_limit, shm_size, nano_cpus, pids_limit) to the
sandbox container from STRIX_SANDBOX_* env vars. Unset values keep
docker's unbounded default, so behavior is unchanged unless opted in.
The sandbox SDK's LocalDir walker rejects any symlink outright
(LocalDirReadError, reason=symlink_not_supported), so uploading a cloned
repository that commits symlinks (common in JS/TS monorepos) aborts before
the agent starts. Stage such trees into a temp copy first: in-tree links
are dereferenced; out-of-tree, dangling, and cyclic links are dropped and
never followed, preserving the walker's path-escape safety. Symlink-free
trees are uploaded as-is.
* fix(deps): cap openai<2.45 and add litellm[proxy] so fresh installs can run
* chore(deps): sync uv.lock with openai cap and litellm[proxy]
Regenerate the lockfile so locked/frozen installs pick up the openai<2.45 cap and litellm[proxy] extras (fastapi, orjson, ...); remove inline dependency comments.
Co-Authored-By: Ahmed Allam <ahmed39652003@gmail.com>
---------
Co-authored-by: Ahmed Allam <ahmed39652003@gmail.com>
feat(inputs): implement logic for required tool choice based on model
test(inputs): add tests for force_required_tool_choice behavior
test(runner): update tests to include force_required_tool_choice in settings