Skip to content

v1.1.0

Latest

Choose a tag to compare

@ernestprovo23 ernestprovo23 released this 14 Jul 09:10
e16322e

Added

  • Injection-phrase FP-instrumentation (Refs #12). Shippable, non-default-changing
    groundwork for eventually promoting WRD-RES-INJECT-PHRASE to default-block once field
    false-positive (FP) data justifies it. No default posture changed — the fuzzy tier
    stays monitor-only, and WRD-RES-INJECT-PHRASE keeps its tier, its default action, and its
    place in the error-replacement set. Added: (1) a discrete matched_phrases array on the
    finding record (JSONL) + matchedPhrases SARIF property, so per-phrase aggregation reads
    a structured field instead of parsing message; (2) a run-summary JSONL record + SARIF
    run property carrying frames_inspected — the base-rate denominator for a per-phrase FP
    rate — plus inject_phrase_findings; (3) --block-inject-phrase-only <file>, a
    default-off, per-phrase opt-in that blocks ONLY the named exact phrases while every other
    curated phrase stays monitor (narrower than --block-inject-phrase; the future
    deterministic-subset promotion mechanism); (4) an operator record → inspect → label
    FP-collection workflow in docs/RESULT_INSPECTION.md §10. Security: the telemetry
    surface emits only the curated denylist phrase, rule id, metadata, action, and counts —
    never raw result content (which can carry secrets/PII); all aggregation is local-only,
    no phone-home. Issue #12 stays open (the default-block flip remains gated on the FP data
    this instrumentation collects).
  • CI coverage / lint / CVE gates. The CI workflow now enforces three new
    standing gates: (1) a coverage floorpytest --cov=mcp_warden --cov-fail-under=80 (whole-project coverage is ~86%; the floor is pinned below
    actual so it can only rise). Subprocess coverage of guard_list_gate.py (which
    only executes inside the guard child spawned by the strict-abort tests) is now
    captured via [tool.coverage.run] parallel = true + COVERAGE_PROCESS_START,
    taking it from a 0% visibility artifact to ~89%. (2) A ruff lint gate
    (ruff check .) with config in [tool.ruff] (pyflakes/pycodestyle/isort/
    bugbear; line-length = 100). (3) A pip-audit CVE gate in deps-locked
    that audits the resolved dependency closure and fails closed on any advisory.
  • Trust-root unit tests (tests/test_signing_unit.py). 25 new tests drive
    the internal signing.py sign/verify code paths directly (mocking the sigstore
    boundary — no network, OIDC, or Fulcio/Rekor traffic), lifting signing.py
    line coverage from 61% to 99%. Covers the sign path (ambient + explicit token),
    the verify path (identity/issuer plumbing), and every fail-closed branch.

Changed

  • Dependency refresh (security). Bumped the hash-locked dev/CI closure:
    pydantic-settings 2.14.1 → 2.14.2 (clears advisory GHSA-4xgf-cpjx-pc3j),
    plus current minors of mcp (1.27.2 → 1.28.1), cryptography (→ 49.0.0),
    anyio (4.13.0 → 4.14.2), and others. pytest-cov added to the dev extra.

  • Runtime DNS resolution SSRF bypass detection (WRD-RES-EXFIL-DNS-SSRF) (#11):
    the guard proxy now resolves URL hostnames from tools/call results at runtime
    and blocks (error-replace) when any resolved IP falls in a deny range
    (SSRF_NETWORKS — link-local, loopback, RFC1918, IPv6 ULA/loopback/link-local).
    This closes the bypass where WRD-RES-EXFIL-IP-LITERAL could not fire because the
    result contained a DNS hostname (e.g. 169.254.169.254.nip.io) rather than a raw
    IP literal. Resolution is bounded by 1 s across all hostnames per result frame,
    fail-open (any DNS error = no hit), and opt-out via --no-block-exfil-dns-ssrf
    (or --no-block-deterministic). Raw IP literals and the offline inspect command
    are unchanged. New module res_dns.py; 23 new tests.