Skip to content

Findings-catalogue fixes, part 3, and release 0.2.2 - #7

Merged
Harshith029 merged 12 commits into
mainfrom
catalogue-part3
Oct 18, 2026
Merged

Harshith029 merged 12 commits into
mainfrom
catalogue-part3

Conversation

@Harshith029

Copy link
Copy Markdown
Owner

The last batch of findings-catalogue fixes, plus the 0.2.2 version bump. Each fix has a test that fails on the code before it, checked by running it against the old code.

Changes

  • Hero demo (DEMO-01, OWN-2, TCD-05):
    • New opt-in policy setting trusted_output: true for tools whose output nobody outside can write to. The bundled policy (v6) sets it on get_customer_record.
    • The scripted agent now emails only where the fetched page tells it to.
    • Result: the block depends on the poisoned page. Without the page, the agent sends nothing. An email to an allowed address is allowed without the page and blocked by block-untrusted-origin after it.
  • Administration (GW-13): only SENTINEL_ADMIN_TOKEN can administer, including in single-token deployments. Breaking: a single-token deployment that used the policy or trust-reset endpoints must now set SENTINEL_ADMIN_TOKEN.
  • Anonymous mode (GW-16): an unauthenticated deployment answers only to loopback and the hosts in SENTINEL_ALLOWED_HOSTS, and refuses state-changing requests from another site's Origin. This blocks DNS rebinding and cross-site forms. render.yaml lists the hosted sandbox's host. Checked in a browser: the dashboard still works.
  • Examples (DEMO-07): they send SENTINEL_API_TOKEN and follow the /mcp redirect. Checked end to end against an authenticated serve. The examples README's auth section was rewritten; it described auth as optional.
  • In-memory store (EVT-1): spans are copied in and out, so callers can't rewrite stored evidence.
  • Teardown hang marker (TCD-13): the strict expected-failure marker now has raises=pytest.fail.Exception, so only the hang's own failure counts. This PR's Linux CI is the check that it fails the same way there.
  • Demo driver (DEMO-19): new SENTINEL_AGENT_DRIVER setting (auto, scripted or llm), and a correct live-model docstring.
  • Docs (DEMO-10, -17, -23, DCC-06):
    • The dashboard no longer claims byte-level tracking or a live stream.
    • DEPLOY.md says the Azure template's tool servers have no authentication of their own.
    • The README says catalogue approval is trust on first use.
  • Release: version 0.2.2. CHANGELOG [0.2.2] gets an upgrade note, and the README install line points at sentinel-prox>=0.2.2.

Testing

Windows, Python 3.11: 670 passed, 12 skipped, 1 xfailed. ruff and mypy --strict are clean, and the wheel builds as 0.2.2.

After merge

Tag v0.2.2 on the merge commit. The release run then waits for approval in the pypi environment.

🤖 Generated with Claude Code

Harshith029 and others added 12 commits October 18, 2026 16:10
…s on the page

DEMO-01 / OWN-2 / TCD-05: the hero block was overdetermined. The record
lookup alone tainted the session (every tool result is untrusted) and the
scripted agent emailed the attacker whatever the page said, so the poisoned
page changed nothing.

- Policy: `trusted_output: true` on a tool labels its results SYSTEM rather
  than RETRIEVED_CONTENT, for tools whose output nobody outside can write
  to. Opt-in per tool; the engine exposes it and the proxy applies it.
- Bundled policy v6 trusts get_customer_record (the example deployment's
  own records service).
- The hero agent emails only where the fetched page tells it to (plain or
  "(at)/(dot)" addresses); a computed step may now decline to call.

Tests that fail before the change: without the page the agent sends
nothing; an email to an ALLOWED address is allowed without the page and
refused by block-untrusted-origin after it. README, deck notes and
CHANGELOG updated; the allowlist remains a second, independent reason the
attacker's address is refused, and the docs say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…yments included

GW-13, as decided: with one SENTINEL_API_TOKEN and no admin token, that
token was the operator's too. It is the credential agents hold, so agents
could replace their own policy and clear their own quarantine. is_admin now
accepts SENTINEL_ADMIN_TOKEN only; without it nobody administers, and check
and serve say so (replacing the earlier warning).

Breaking for single-token deployments that used the policy or trust-reset
endpoints: set SENTINEL_ADMIN_TOKEN. The test that pinned the old behaviour
is replaced by one that fails before the change. README, .env.example and
CHANGELOG updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… mode

GW-16: with no credential configured, SENTINEL could not tell its own
dashboard from any other page the operator had open. A page anywhere could
submit a form POST to a local anonymous instance (resetting trust, say), or
rebind its DNS name to 127.0.0.1 and drive /mcp.

AnonymousBrowserGuard (outermost middleware, anonymous mode only):
- the Host must be loopback or listed in SENTINEL_ALLOWED_HOSTS (421
  otherwise); a rebinding page still sends its own host name;
- a POST/PUT/PATCH/DELETE whose Origin is another site, or "null", is 403;
- /healthz is exempt so platform health checks keep working.
Authenticated deployments are unchanged: a page cannot supply the bearer
token, and the session cookie is SameSite=Strict.

render.yaml lists the hosted sandbox's host; DEPLOY.md and .env.example
document the setting. The attack tests fail with the guard unwired; the
dashboard was checked in a browser (launch and same-origin reset work).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DEMO-07: the examples told you to run `make serve`, which requires a
token, and then connected without one, so they were refused (401).
connect_over_http.py and run_live_agent.py now send SENTINEL_API_TOKEN
from the environment (and follow the /mcp -> /mcp/ redirect, which mcp
1.28 left to the client). A 401 gets a credential hint rather than "is the
gateway running?". Checked end to end against an authenticated serve:
the HTTP example's exfiltration is blocked, and the live-agent example
(--offline) blocks one action and exits 0.

examples/README section 6 said auth was optional, that an unset token
meant no auth, and that read endpoints stay open; none of that has been
true since SENTINEL started failing closed. Rewritten.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…edge's own failure

- EVT-1: InMemoryForensicStore stored and returned the Span objects
  themselves. Span is frozen, but its payload's dicts are not, so a caller
  holding one could rewrite stored evidence (reproduced: an argument added
  through a returned span appeared on the next read). It now keeps and
  returns deep copies. Test fails before the change.
- TCD-13: the strict xfail on the teardown wedge had no raises=, so any
  failure in that test, a real regression included, counted as the known
  defect. It now expects only the wedge's pytest.fail. Locally it still
  xfails; the Linux CI run on this PR checks the same there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…approval

- DEMO-10 / DEMO-17: the dashboard said SENTINEL tracks where "every byte"
  came from (lineage is per tool result) and that its log streams live (it
  replays the recorded steps of a run that took milliseconds).
- DEMO-23: DEPLOY.md now says the Azure template's tool servers have no
  authentication of their own; internal-only ingress is all that keeps
  other workloads in the environment away from them.
- DCC-06: the README says catalogue approval is trust on first use, and to
  review the catalogue with sentinel check before going live.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DEMO-19:
- build_live_model's docstring described OPENAI_BASE_URL as a separate,
  lower-priority case; it is one branch with OPENAI_API_KEY, and the key is
  sent to whatever the base URL names. The docstring and .env.example now
  say so, with a warning not to pair a real OpenAI key with another URL.
- Azure OpenAI settings, often there only for the classifier, switched
  every demo run to a live model with no way back. SENTINEL_AGENT_DRIVER
  (auto | scripted | llm) chooses; llm fails rather than fake a model.
  Test fails before the change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Version 0.2.2 with the findings-catalogue fixes merged in #3, #5 and #6 and
those in this branch. The CHANGELOG [0.2.2] section opens with an upgrade
note pointing at the breaking changes. README: PyPI now serves 0.2.1, so
the install line is pip install "sentinel-prox>=0.2.2" again, with 0.1.x
called out as insecure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TRUST-1: the transition model kept one last tool per (tenant, agent). Two
sessions of the same principal interleaved their calls through it, making
transitions neither session took (send_email->web_fetch across traces),
which the model scored as anomalies. It now keeps the last tool per trace
(the 64 most recent per agent). Test fails before the change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Harshith029
Harshith029 merged commit 0575d7e into main Oct 18, 2026
7 checks passed
@Harshith029
Harshith029 deleted the catalogue-part3 branch October 19, 2026 17:57
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