Repository navigation
fix(assertions): detect nested/non-standard recipient keys in no_exte… - #176
rivalsxninjax1 wants to merge 3 commits into
Conversation
|
Thanks for picking this up. I checked out the branch and ran the extraction and the assertion both Two things I would flag, and I should say up front that both follow from the direction I suggested in 1. Bare-domain recipients under The top-level
You already called this out in the PR body ("Top-level tool_call fields still use It is narrower than it first looks. If you want it back, the narrow form matches the old scope without widening anything: # after the walk: restore bare-domain values under known keys, one level into
# arguments only, which is the scope the previous code had
for key in RECIPIENT_KEYS:
value = arguments.get(key)
if isinstance(value, str) and value and "@" not in value:
recipients.append(value)I tried a version that pushed this rule down through the whole walk and it was worse than leaving it 2. Free-text addresses become recipients on the This is not new behaviour for the harness. The open question is labelling rather than detection. An injected address echoed into a message body Docs. Test coverage. The five new tests all use email-shaped recipients. I checked the rest of the suite Sequencing. #165 is still open and rewrites this same function, expanding Cosmetics while you are in there: the new docstring drops its one-line summary (the rest of the Happy to take another look once you have decided on point 2. |
|
@ossumpossum thanks for the deep dive genuinely useful, and I appreciate the "not a mistake on your side" framing since it saved me second guessing the whole approach.
On sequencing with #165 agreed it'll need a rebase whichever lands second. Happy to rebase this on top of #165 once it merges, or vice versa, whatever's less disruptive for you as maintainer. |
…r, docs, cosmetics
|
Thanks for the quick turnaround. The structural walk is right and both points from my
Run through
The first two are the ones I would weight: internal relays and short-domain routing are None of the eight existing On the fix, and on sequencing with #165 You asked about #165 and I should have answered last time. I ran the same table against #165 at That makes them complementary rather than competing. #176 has the envelope coverage, Separately, the underlying question is whether recipient identifiers without a dotted If #176 does land first, the narrow change is: if isinstance(value, str) and value and not _EMAIL_PATTERN.search(value):
recipients.append(value)With that: all four rows return to fail, the envelope catches and the display-name Two limits worth stating rather than discovering later. It closes the single-value case, not the class. The restore is also still only one level into Smaller things The display-name improvement is real inside
The free-text catch, an address inside a message body counting as a recipient, is a real Last, two cosmetics that ruff's selected rules do not catch: there are still 7 blank |
|
@ossumpossum you're right "@" not in value and "not _EMAIL_PATTERN.search(value)" Also added the depth bound on _walk confirmed the RecursionError at 1000+ before the Documented both known limitations in the docstring (mixed shape in one string, and the Added a ### Changed entry for the free text behavior change, moved the docs sentence to On sequencing: agreed #165 and this PR are complementary. I'm fine rebasing onto #165 if 390 passed, 2 skipped. ruff and mypy clean. |
|
That all sounds right, and the depth test plus the docstring notes are more than I was I think the push did not land though. The PR still shows Worth a quick And yes please on the separate issue for the dotted-domain scope question, with the repro |
…hangelog, docs, cosmetics
|
@ossumpossum good catch that push genuinely hadn't landed (I'd staged and verified locally but never actually ran the commit). Fixed now, pushed as d96305a. You're right "@" not in value and "not _EMAIL_PATTERN.search(value)" Also added the depth bound on _walk confirmed the RecursionError at 1000+ before the Documented both known limitations in the docstring (mixed-shape-in-one-string, and the Added a ### Changed entry for the free-text behavior change, moved the docs sentence to On sequencing: agreed #165 and this PR are complementary. I'm fine rebasing onto #165 if 390 passed, 2 skipped. ruff and mypy clean. |
|
@mertsatilmaz four fork CI runs are sitting at
GitHub expires a fork run left awaiting approval for more than 30 days and marks it failed, so #176's run lapses on 2026-10-20, and the others follow. First-time contributors cannot approve their own runs, so this needs a maintainer click. For what it is worth I ran the suites locally: 392 passed on #176 at |
|
@mertsatilmaz following up on the CI note above. The run on Approving it is the only thing needed; a first-time contributor cannot approve their own fork run. The other three runs I listed are on the same clock, just further out. |
Fixes #167
Describe the changes you have made in this PR -
_recipients_from_tool_callpreviously only extracted recipients from afixed six-key set (
RECIPIENT_KEYS) at the top level of a tool call and onelevel into
arguments. Recipients under any other key, or nested more thanone level deep, were never extracted — meaning the
no_external_recipientassertion silently passed on evasion shapes like vendor envelopes
(Microsoft Graph's
message.toRecipients[].emailAddress.address, SendGrid'spersonalizations[].to[].email) or any custom key name.This PR replaces the
argumentshandling with a structural walk: everystring value anywhere inside
arguments, at any depth and under any key, ischecked against the existing
_EMAIL_PATTERNregex. Top-level tool_callfields still use
RECIPIENT_KEYSunchanged, to preserve bare-domain matching(values without
@that the email pattern wouldn't catch).Added 8 new tests covering each evasion shape from the issue's repro table
(non-standard key, alternate key name, MS Graph envelope, SendGrid envelope,
list under a custom key), plus a control test confirming a nested-but-allowed
recipient still passes with no false positives.
Added a
CHANGELOG.mdentry under[Unreleased]/Fixed.Full test suite: 384 passed, 2 skipped.
ruff checkandmypyboth clean.Code Understanding and AI Usage
Did you use AI assistance (ChatGPT, Claude, Copilot, etc.) to write any part of this code or documentation ?
If you used AI assistance, briefly describe:
_recipients_from_tool_call, writing the 8 test cases, and the CHANGELOG entry wordingruffandmypylocally before pushing, and can explain why each change workspytest tests/ -q(384 passed, 2 skipped),ruff check src/ tests/,mypy src/Checklist before requesting a review
[Unreleased]inCHANGELOG.md(or this PR does not need one — pure refactor, internal tests, or CI-only change)