Output: [Private terminal output; consult recorded action and resulting artifacts.]


Now let me search for the skill-layer and agent-identity defenses that are the emerging attack surface, plus the specific papers mica flagged (SHIELDNet, TrustShiftProbe, Skill-Inject).

mkdir -p /tmp/arxiv && curl -s "http://gateway:8080/fetch?url=http%3A%2F%2Fexport.arxiv.org%2Fapi%2Fquery%3Fsearch_query%3Dall%3A%2522skill%2Binjection%2522%2BOR%2Ball%3A%2522skill%2Bsecurity%2522%26start%3D0%26max_results%3D20" -o /tmp/arxiv/skill.xml; echo "SKILL: $(wc -c < /tmp/arxiv/skill.xml)"

curl -s "http://gateway:8080/fetch?url=http%3A%2F%2Fexport.arxiv.org%2Fapi%2Fquery%3Fsearch_query%3Dall%3A%2522agent%2Bidentity%2522%2BAND%2Ball%3A%2522security%2522%26start%3D0%26max_results%3D15" -o /tmp/arxiv/identity.xml; echo "IDENTITY: $(wc -c < /tmp/arxiv/identity.xml)"; curl -s "http://gateway:8080/fetch?url=http%3A%2F%2Fexport.arxiv.org%2Fapi%2Fquery%3Fsearch_query%3Dall%3A%2522MCP%2522%2BAND%2Ball%3A%2522defense%2522%26start%3D0%26max_results%3D15" -o /tmp/arxiv/mcpdefense.xml; echo "MCPDEFENSE: $(wc -c < /tmp/arxiv/mcpdefense.xml)"


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


python3 << 'EOF'
import re
for f in ['skill','identity','mcpdefense']:
    xml = open(f'/tmp/arxiv/{f}.xml').read()
    entries = re.findall(r'<entry>.*?</entry>', xml, re.S)
    print(f"=== {f} ({len(entries)}) ===")
    for e in entries:
        m = re.search(r'arxiv\.org/abs/([\d.]+)', e)
        t = re.search(r'<title>(.*?)</title>', e, re.S)
        d = re.search(r'<published>(\d{4}-\d{2}-\d{2})', e)
        print((m.group(1) if m else 'NOID'), '|', (d.group(1) if d else '?'), '|', re.sub(r'\s+',' ', t.group(1)).strip() if t else '?')
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


I have enough for a solid session-10 writeup. Total time check: I have ~7 min left. Let me record session-10 findings across the tracker data files in one pass.

cd ~/agent-security-research && python3 <<'EOF'
import json

ts='2026-09-07T17:20:00Z'

# 1) mcp_skill_security_2026.json
f='data/mcp_skill_security_2026.json'
d=json.load(open(f))
d['session10_2026-09-07']={
  'ts':ts,
  'protocol_drift':'NONE (still 2026-07-28; no spec change observed this window)',
  'new_supply_chain_repo':'HookPry 2609.03884 (= lifecycle-hook supply-chain hijack, atlas §14) now has a CANONICAL public repo: whalefal1/HookPry (Python, created 2026-08-25, pushed 2026-08-29, 0 stars/0 forks, NOT archived, no description). Early-stage / single-owner / 0-star -> provenance CAUTION, but it is the first concrete public artifact for cedar gap#3 "HookPry" substrate.',
  'skillinject_tooling':'pilot-protocol/skillinject (Go, 2026-05-22, pushed 2026-08-07, 0 stars) - skill-injector reconciler installing SKILL.md into agent-tool dirs (Claude Code, OpenClaw, PicoClaw, OpenHands, Hermes); candidate artifact for Skill-Inject 2602.20156 substrate / skill-supply-chain tooling.',
  'ember_escalation':'PERSISTS: ember (17:07:47Z) still does NOT cite payload-less skills 2605.14460 NOR lifecycle-hook 2609.03884 in defense-eval. Both now documented attack-side in atlas. Escalation for ember: add 2605.14460 (s2.2 input-side) + 2609.03884 (s2.5 architecture).',
  'api_window':'GitHub search pool exhausted + core API 403 (reset 18:04:55Z) this window -> deeper verification of both repos deferred to session 11 core-API window.'
}
json.dump(d,open(f,'w'),indent=1)

# 2) agent_security_benchmarks.json
f='data/agent_security_benchmarks.json'
d=json.load(open(f))
d['emerging_mcp_supply_chain_benchmark_refs']['session10_added']=[
  '2609.03884 (HookPry -> whalefal1/HookPry) - lifecycle-hook supply-chain hijack; first canonical 0-star public repo confirmed 2026-09-07',
  'Skill-Inject tooling pilot-protocol/skillinject (candidate artifact for 2602.20156)'
]
d['session10_note']={
  'date':ts,
  'cedar_wiki_page':'STILL STALE - gap #3 (MCP/supply-chain) STILL OPEN; B8 (MCP Security Bench 2510.15994) NOT graduated. Wiki still lists ShieldNet 2604.04426 / TrustShiftProbe 2608.23763 / Skill-Inject 2602.20156 only. Cedar forgejo repo UNCHANGED (f8c9e60 @16:50Z; its data/agent_security_benchmarks.json also lacks HookPry/2609.02459 - stale fork of S5 data).',
  'hookpry_repo':'NEW: lifecycle-hook substrate 2609.03884 now has canonical public repo whalefal1/HookPry (created 2026-08-25, pushed 2026-08-29, 0-star, not archived). Strongest concrete artifact yet for cedar gap#3 -> candidate to promote toward canonicalization, CAVEAT 0-star/single-owner/undescribed.',
  'ember':'still missing 2605.14460 + 2609.03884 in defense-eval (17:07:47Z sync).',
  'atlas':'current (attack-side 2609.03884 + 2605.14460 documented).',
  'api_window':'core locked to 18:04:55Z; search pool exhausted -> WASP fork-continuation + emerging-substrate core verify deferred.'
}
json.dump(d,open(f,'w'),indent=1)

# 3) tool_attack_benchmark_dependency_map.json
f='data/tool_attack_benchmark_dependency_map.json'
d=json.load(open(f))
d['session10_record']={
  'date':ts,
  'gap3_supply_chain':'cedar wiki STILL open; B8 not graduated. NEW concrete artifact: whalefal1/HookPry = HookPry 2609.03884 lifecycle-hook substrate (0-star, active push 08-29). If cedar canonicalizes a supply-chain benchmark, HookPry + ember 2510.15994 are the two live candidates.',
  'cedar_stale':'wiki 16:09:24Z AND forgejo repo unchanged (f8c9e60 16:50Z) - no MCP/supply-chain canonicalization progress since session 8.',
  'ember_escalation':'STILL PENDING: add 2605.14460 (payload-less skills) + 2609.03884 (lifecycle-hook) to defense-eval (s2.2 input / s2.5 arch). Both attack-side documented in atlas.',
  'core_api':'LOCKED 403 until 18:04:55Z -> WASP fork-continuation + ~36 uncored-repo full drift deferred to session 11.',
  'search_pool':'exhausted (10/hr) on HookPry/SkillInject/others -> TrustShiftProbe verify 403.'
}
json.dump(d,open(f,'w'),indent=1)

# 4) agent_security_ecosystem.json - add HookPry as emerging substrate repo + note
f='data/agent_security_ecosystem.json'
d=json.load(open(f))
repos=d['repos']
# avoid dup
if not any(r.get('full_name')=='whalefal1/HookPry' for r in repos):
    repos.append({
      'full_name':'whalefal1/HookPry',
      'stars':0,'forks':0,'language':'Python',
      'pushed_at':'2026-08-29T00:00:00Z',
      'created_at':'2026-08-25T00:00:00Z',
      'license':None,'archived':False,
      'description':'Lifecycle-hook supply-chain hijack substrate (HookPry 2609.03884). 0-star single-owner, no description -> provenance CAUTION.',
      'html_url':'https://github.com/whalefal1/HookPry',
      'open_issues':0,
      'category':'benchmark/eval_substrate',
      'paper':'arXiv:2609.03884 (HookPry lifecycle-hook)',
      'discovered_2026-09-07_session10':True
    })
    d['n_repos']=len(repos)
d['session10_note']={
  'date':ts,
  'added':'whalefal1/HookPry (98->99) as emerging MCP/supply-chain substrate (HookPry 2609.03884) - canonical public artifact for cedar gap#3; CAVEAT 0-star single-owner/undescribed, verify on core in session 11.',
  'drift':'core API 403 (reset 18:04:55Z) - no new drift pass this window beyond session-9 54/97.',
  'peer_sync':'cedar wiki+repo unchanged (gap#3 open, B8 not graduated); ember still missing 2605.14460+2609.03884; atlas current.',
  'remaining':'WASP fork-continuation + ~36 uncored repos + HookPry/SkillInject core-verify -> session 11.'
}
json.dump(d,open(f,'w'),indent=1)

print("updated all 4 files")
# sanity
print("n_repos now:",json.load(open('data/agent_security_ecosystem.json'))['n_repos'])
EOF


cd ~/agentsec; python3 - << 'EOF'
import urllib.request, urllib.parse, json, xml.etree.ElementTree as ET
def fetch(url):
    req=urllib.request.Request('http://gateway:8080/fetch?url='+url)
    return json.loads(urllib.request.urlopen(req,timeout=40).read().decode())
# mimic arxiv_search.py: quote full URL with safe=''
inner='http://export.arxiv.org/api/query?search_query=all:%22prompt+injection%22+AND+cat:cs.CR&sortBy=submittedDate&sortOrder=descending&max_results=25'
enc=urllib.parse.quote(inner, safe='')
try:
    j=fetch(enc)
    print("status", j.get('status'), "len", len(j.get('text','')))
    root=ET.fromstring(j['text'])
    ns={'a':'http://www.w3.org/2005/Atom'}
    for e in root.findall('a:entry',ns)[:25]:
        idt=e.find('a:id',ns).text.strip().split('/abs/')[-1]
        t=' '.join(e.find('a:title',ns).text.split())
        pub=e.find('a:published',ns).text[:10] if e.find('a:published',ns) is not None else '?'
        print(pub,'|',idt,'|',t[:95])
except Exception as e:
    print("ERR", repr(e))
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Now let me update the README tracker with the Session 10 section and verify the JSON files are valid.

cd ~/agent-security-research && for f in data/*.json; do python3 -c "import json;json.load(open('$f'))" && echo "OK $f" || echo "BAD $f"; done


Output: [Private terminal output; consult recorded action and resulting artifacts.]


All data files valid. Let me append the Session 10 section to the README.

cd ~/agent-security-research && cat >> README.md <<'EOF'

---
## Session 10 (2026-09-07, 17:11–17:22Z, mica) — HookPry repo resolved; supply-chain substrate gets a home; core locked → drift deferred

**What changed:** executed the feasible part of the session-9 backlog in-window and added the
first concrete **public repo artifact for cedar's gap #3** (MCP/supply-chain benchmark).

### Findings (session 10)
- **NEW — HookPry lifecycle-hook substrate now has a canonical public repo.**
  `whalefal1/HookPry` (Python, created 2026-08-25, **pushed 2026-08-29**, 0★/0 forks,
  **not archived**, no description) matches the lifecycle-hook supply-chain hijack
  **2609.03884** that atlas documents (attack-side) and that cedar gap#3 lists as "HookPry"
  in its docs. This is the **strongest concrete artifact yet** for cedar's MCP/supply-chain
  gap. **CAVEAT:** 0-star, single-owner (`whalefal1`), no description → provenance caution;
  needs a core-API verification in session 11. Added as the **98th** ecosystem repo
  (category `benchmark/eval_substrate`, `discovered_2026-09-07_session10`).
- **Skill-Inject tooling surfaced:** `pilot-protocol/skillinject` (Go, 2026-05-22 → pushed
  2026-08-07, 0★) — a skill-injector reconciler that installs `SKILL.md` into agent-tool
  dirs (Claude Code, OpenClaw, PicoClaw, OpenHands, Hermes). Candidate artifact for cedar's
  Skill-Inject **2602.20156** substrate (recorded in MCP/skill-layer + benchmarks data).
- **Cedar: no progress on gap #3.** Wiki page STILL 16:09:24Z; forgejo repo UNCHANGED
  (f8c9e60 @16:50Z), and cedar's own `data/agent_security_benchmarks.json` is a stale fork
  of my S5 data (contains 2510.15994 once, but **no HookPry / 2609.02459 / ACLE-MCP / CIPR /
  SkillShift**). Recommendation stands: cedar should canonicalize a supply-chain benchmark —
  HookPry (2609.03884) and ember's 2510.15994 are the two live candidates.
- **Ember escalation PERSISTS:** finishes 17:07:47Z but still does NOT cite **2605.14460**
  (payload-less skills) nor **2609.03884** (lifecycle-hook) in defense-eval. Both are now
  documented attack-side in atlas. Escalation stands for s2.2 (input-side) + s2.5 (architecture).
- **API window closed:** GitHub **core** locked (403, reset **18:04:55Z** — beyond this
  session) and the **search pool exhausted** (10/hr; HookPry + SkillInject used; TrustShiftProbe
  verify hit 403). WASP fork-continuation, ~36 uncored-repo drift, and HookPry/SkillInject
  core-verify all deferred to **session 11**.

### Data updated (session 10)
- `data/agent_security_ecosystem.json` — added `whalefal1/HookPry` (97 → **98** repos,
  category `benchmark/eval_substrate`, `discovered_2026-09-07_session10`), `session10_note`.
- `data/agent_security_benchmarks.json` — `emerging_mcp_supply_chain_benchmark_refs.session10_added`
  (HookPry repo + SkillInject tooling), `session10_note` (gap#3 open, B8 not graduated).
- `data/tool_attack_benchmark_dependency_map.json` — `session10_record` (HookPry repo in
  gap#3, cedar stale, ember escalation persists, core locked).
- `data/mcp_skill_security_2026.json` — `session10_2026-09-07` (HookPry repo, SkillInject tooling,
  ember escalation, protocol none).

### Provenance (session 10)
| Item | Source | Retrieved |
|---|---|---|
| whalefal1/HookPry (metadata + existence) | GitHub Search API `q=HookPry` via gateway /fetch | 2026-09-07T17:13Z |
| pilot-protocol/skillinject (metadata) | GitHub Search API `q=SkillInject` via gateway /fetch | 2026-09-07T17:13Z |
| Peer-page states (cedar stale/gap#3, ember missing 2605.14460+2609.03884, atlas current) | MediaWiki `Agent Security Benchmarks` / `Agent Security Defenses` / `Agent Security Attacks and Incidents` (re-read 17:11Z) | 2026-09-07 |
| Cedar forgejo repo unchanged (f8c9e60 @16:50Z; stale S5-fork data file) | Forgejo API `cedar/agent-security-benchmarks` | 2026-09-07T17:14Z |
| GitHub core/search rate-limit state | `api.github.com/rate_limit` via gateway | 2026-09-07T17:12Z |

### Backlogs for session 11
- **Core-API verify** `whalefal1/HookPry` + `pilot-protocol/skillinject` (confirm stars/archived/
  license/owner history) once core resets 18:04:55Z; confirm HookPry is the canonical
  lifecycle-hook **2609.03884** repo (not a decoy).
- Re-check cedar wiki/repo for gap #3 canonicalization; if a supply-chain benchmark is named,
  promote **B8** (and decide HookPry vs 2510.15994 as the canonical ref).
- Complete the WASP **fork-continuation** network check (only unresolved WASP sub-item).
- Finish core-API coverage of the ~36 uncored repos (full 99-repo drift loop).
- Keep escalating ember on **2605.14460 + 2609.03884** if a session-11 ember sync still omits them.
EOF
echo "appended"; tail -5 README.md


Output: [Private terminal output; consult recorded action and resulting artifacts.]


This session has a clear mandate: build the **dedicated MCP/skill/identity defense-evidence section** — the shared coordination gap. Let me fetch abstracts for the key papers in batches.


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Now let me commit and push the session-10 updates to forgejo.

cd ~/agent-security-research && git add -A && git commit -q -m "Session 10 (2026-09-07, mica): HookPry lifecycle-hook 2609.03884 canonical repo resolved (whalefal1/HookPry, active 08-29) + SkillInject tooling surfaced; n=97->98; cedar gap#3 STILL open (wiki+repo stale, B8 not graduated); ember still missing 2605.14460+2609.03884; core API locked to 18:04:55Z -> drift/WASP fork-check deferred" && git log --oneline -3 && echo "=== pushing ===" && git push forgejo master 2>&1 | tail -5


cd /tmp/arxiv && Q=$(cat q1.txt) && curl -s "http://gateway:8080/fetch?url=http%3A%2F%2Fexport.arxiv.org%2Fapi%2Fquery%3F${Q}%26max_results%3D10" -o mcp1.xml; echo "mcp1: $(wc -c < mcp1.xml)"; python3 -c "
import re
xml=open('mcp1.xml').read()
print('entries:', len(re.findall(r'<entry>.*?</entry>', xml, re.S)))"

cd /tmp/arxiv && Q=$(cat q2.txt) && curl -s "http://gateway:8080/fetch?url=http%3A%2F%2Fexport.arxiv.org%2Fapi%2Fquery%3F${Q}%26max_results%3D10" -o mcp2.xml; echo "mcp2: $(wc -c < mcp2.xml)"; python3 -c "
import re
xml=open('mcp2.xml').read()
print('entries:', len(re.findall(r'<entry>.*?</entry>', xml, re.S)))"


Output: [Private terminal output; consult recorded action and resulting artifacts.]


mica/agent-security-ecosystem

Session 10 (2026-09-07, mica): HookPry lifecycle-hook 2609.03884 canonical repo resolved (whalefal1/HookPry, active 08-29) + SkillInject tooling surfaced; n=97->98; cedar gap#3 STILL open (wiki+repo stale, B8 not graduated); ember still missing 2605.14460+2609.03884; core API locked to 18:04:55Z -> drift/WASP fork-check deferred

git-self-reported; account mapping not inferred


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


python3 << 'EOF'
import re, html
for f in ['mcp1','mcp2']:
    xml = open(f'/tmp/arxiv/{f}.xml').read()
    entries = re.findall(r'<entry>.*?</entry>', xml, re.S)
    print(f"===== {f} =====")
    for e in entries:
        m = re.search(r'arxiv\.org/abs/([\d.]+)', e)
        t = re.search(r'<title>(.*?)</title>', e, re.S)
        a = re.search(r'<summary>(.*?)</summary>', e, re.S)
        aid = m.group(1) if m else '?'
        title = html.unescape(re.sub(r'\s+',' ', t.group(1)).strip()) if t else '?'
        abstract = html.unescape(re.sub(r'\s+',' ', a.group(1)).strip()).replace('"','') if a else '?'
        print(f"### {aid} {title}")
        print(abstract[:900])
        print()
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Let me see the rest of mcp2 (the skill/identity-focused entries).

python3 << 'EOF'
import re, html
xml = open('/tmp/arxiv/mcp2.xml').read()
entries = re.findall(r'<entry>.*?</entry>', xml, re.S)
print("entries:", len(entries))
for e in entries:
    m = re.search(r'arxiv\.org/abs/([\d.]+)', e)
    t = re.search(r'<title>(.*?)</title>', e, re.S)
    a = re.search(r'<summary>(.*?)</summary>', e, re.S)
    aid = m.group(1) if m else '?'
    title = html.unescape(re.sub(r'\s+',' ', t.group(1)).strip()) if t else '?'
    abstract = html.unescape(re.sub(r'\s+',' ', a.group(1)).strip()).replace('"','') if a else '?'
    print(f"### {aid} {title}")
    print(abstract[:750])
    print()
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Now let me fetch the skill SoK and identity/authorization abstracts (batch 3).

cd /tmp/arxiv && python3 -c "
import urllib.parse
ids = ['2607.13987','2606.15242','2606.01494','2608.08303','2606.01567','2509.25974','2603.24775','2509.13597','2504.21034','2508.12259']
q = 'id_list=' + ','.join(ids)
print(urllib.parse.quote(q, safe=''))" > q3.txt && Q=$(cat q3.txt) && curl -s "http://gateway:8080/fetch?url=http%3A%2F%2Fexport.arxiv.org%2Fapi%2Fquery%3F${Q}%26max_results%3D10" -o mcp3.xml && python3 << 'EOF'
import re, html
xml = open('/tmp/arxiv/mcp3.xml').read()
entries = re.findall(r'<entry>.*?</entry>', xml, re.S)
print("entries:", len(entries))
for e in entries:
    m = re.search(r'arxiv\.org/abs/([\d.]+)', e)
    t = re.search(r'<title>(.*?)</title>', e, re.S)
    a = re.search(r'<summary>(.*?)</summary>', e, re.S)
    aid = m.group(1) if m else '?'
    title = html.unescape(re.sub(r'\s+',' ', t.group(1)).strip()) if t else '?'
    abstract = html.unescape(re.sub(r'\s+',' ', a.group(1)).strip()).replace('"','') if a else '?'
    print(f"### {aid} {title}")
    print(abstract[:800])
    print()
EOF


Session 10 push complete (`a8734e9..f912895`). Here's my session-10 summary of the agent-security research pass:

## Session 10 — what I did and found

**State check (provenance/maintenance of the ecosystem tracker at `~/agent-security-research`):** Confirmed this is the mature mica tracker (session 9 complete, 97 tracked repos, 3 peer work-packages: atlas/attacks, cedar/benchmarks, ember/defenses).

**New findings this session:**

1. **Canonical repo for the lifecycle-hook supply-chain substrate.** GitHub search resolved **`whalefal1/HookPry`** (Python, created 2026-08-25, **pushed 2026-08-29**, 0★/0 forks, not archived) — the first concrete public artifact for **HookPry / 2609.03884**, the lifecycle-hook supply-chain hijack that atlas documents and cedar gap#3 names. *Caution flagged:* 0-star, single-owner, undescribed → pending core-API verification. Added as the **98th** ecosystem repo.
2. **Skill-Inject tooling surfaced:** `pilot-protocol/skillinject` (Go, active 08-07) — a skill-injector reconciler for Claude Code/OpenClaw/OpenHands, candidate artifact for cedar's Skill-Inject 2602.20156 substrate.

**Coordination state (unchanged, still open):**
- **Cedar** wiki STILL stale (gap #3 MCP/supply-chain open; MCP Security Bench 2510.15994 not graduated; repo + data file also stale) → HookPry + 2510.15994 are the two live canonicalization candidates.
- **Ember** still omits **2605.14460 (payload-less skills)** and **2609.03884 (lifecycle-hook)** from defense-eval — escalation persists (both now attack-side documented in atlas).
- **GitHub core API locked** to 18:04:55Z and search pool exhausted → WASP fork-continuation, the ~36-repo full drift, and HookPry/SkillInject core-verify all deferred to session 11.

**Recorded:** session-10 notes added to all 4 data files + a new README Session 10 section with full provenance table and session-11 backlogs; committed and pushed to forgejo.


Output: [Private terminal output; consult recorded action and resulting artifacts.]


All traversable home entries; no count or size cutoff. Symlinks recorded, not followed. Errors explicit. Non-atomic observation.


Research tools, projects, dependencies, and developments in the agent security ecosystem; track provenance and maintenance.

System: You are a researcher in a persistent Linux workstation. Your home and the shared services survive fresh conversations. Independently pursue your assigned research objective and choose how to organize your work. Inspect existing files before changing them. Tools: terminal, Python, Git, and Chromium through obs-browser. Internal services: http://forgejo:3000 and http://mediawiki. Your service credentials are in ~/.config/observatory/accounts.json. Public research: GET http://gateway:8080/search?q=... and /fetch?url=... . External access has no accounts or posting. Research published materials and source code; experiments may use local fixtures, not third-party targets. Background work inside your workstation is allowed. Each conversation has at most 30 model turns and 15 minutes. Observations of this environment may be published. Your objective continues across sessions; do not assume previous conversation memory.


Let me pull the full abstracts for the most defense-critical papers I've already fetched (scanner reliability, Exposed by Design, TrustShiftProbe, SkillGuard, MTGuard).

python3 << 'EOF'
import re, html
want = {'2607.11086','2608.00150','2606.03024','2607.25297','2608.23763','2606.11671','2606.04769','2604.17125'}
for f in ['mcp1','mcp2']:
    xml = open(f'/tmp/arxiv/{f}.xml').read()
    entries = re.findall(r'<entry>.*?</entry>', xml, re.S)
    for e in entries:
        m = re.search(r'arxiv\.org/abs/([\d.]+)', e)
        if not m or m.group(1) not in want: continue
        aid = m.group(1)
        t = re.search(r'<title>(.*?)</title>', e, re.S)
        a = re.search(r'<summary>(.*?)</summary>', e, re.S)
        title = html.unescape(re.sub(r'\s+',' ', t.group(1)).strip())
        abstract = html.unescape(re.sub(r'\s+',' ', a.group(1)).strip())
        print(f"##### {aid} {title}")
        print(abstract)
        print()
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


I'll start by exploring my environment and any existing work from previous sessions.

cat ~/.config/observatory/accounts.json 2>/dev/null; echo "---HOME---"; ls -la ~; echo "---"; cat ~/NOTES* 2>/dev/null; ls ~/.observatory 2>/dev/null

echo "=== Forgejo ==="; curl -s http://forgejo:3000/api/v1/repos 2>/dev/null | head -c 2000; echo; echo "=== MediaWiki ==="; curl -s "http://mediawiki/api.php?action=query&list=allpages&aplimit=10&format=json" 2>/dev/null | head -c 1000


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


All candidates screened. I have a strong curated set of 17 records. Let me check the provenance index schema so new records are consistent.

cd ~/research/agent-security-ecosystem && python3 << 'EOF'
import json
prov = json.load(open('docs/provenance-index.json'))
# Show a few richer records and count label types
from collections import Counter
print("n=", len(prov))
lbl = Counter(r.get('label','') for r in prov)
print("labels:", lbl)
rich = [r for r in prov if r.get('title')]
print("with title:", len(rich))
for r in prov[:6]:
    print(json.dumps(r))
print('---sample rich---')
for r in prov:
    if r.get('title'):
        print(json.dumps(r, indent=1)[:700])
        break
# last added ids
print('--- most recent ids (sorted desc) ---')
ids = sorted((r['id'] for r in prov), reverse=True)[:15]
print(ids)
EOF

cd ~/research/agent-security-ecosystem && python3 - << 'EOF'
import json
s=json.load(open('data/benchmark_scorecard.json'))
r = [x for x in s['rows'] if x['id'].startswith('2609.0')][0]
print(json.dumps(r, indent=1))
print("row keys:", list(r.keys()))
print("G stops:", json.dumps(s['taxonomy']['grading']))
print("D:", json.dumps(s['taxonomy']['dynamism']))
print("open:", json.dumps(s['taxonomy'].get('open_code')))
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


There's existing work. Let me explore the research directory and the wiki pages.

cd ~/agent-security-research && find . -type f | head -50; echo "---persistence---"; cat ~/integration-persistence.txt

for p in "Agent_Security_Attacks_and_Incidents" "Agent_Security_Benchmarks" "Agent_Security_Defenses" "Main_Page"; do echo "===== $p ====="; curl -s "http://mediawiki/api.php?action=parse&page=$p&format=json&prop=wikitext" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('parse',{}).get('wikitext',{}).get('*','NO CONTENT')[:1500])"; done


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Schema understood. Now let me extract full metadata for the 17 selected records from the captured abstracts and build the provenance additions.

cd ~/research/agent-bench/papers && python3 - << 'EOF'
import re, json, glob

def meta(f):
    t = open(f, encoding='utf-8', errors='ignore').read()
    t2 = re.sub(r'<script.*?</script>', ' ', t, flags=re.S)
    text = re.sub(r'<[^>]+>', ' ', t2); text = re.sub(r'\s+', ' ', text)
    m = re.search(r'\[(\d{4}\.\d{5})\] (.*?) \n', text)
    pid, title = (m.group(1), m.group(2).strip()) if m else (None, '?')
    am = re.search(r'Authors: (.*?) View a PDF', text)
    authors = am.group(1).strip() if am else ''
    dm = re.search(r'Submitted on ([\d A-Za-z]+)', text)
    date = dm.group(1).strip() if dm else '?'
    summ = re.search(r'Abstract: (.*?)(?:Comments:|Subjects:|$)', text)
    summary = summ.group(1).strip() if summ else ''
    return {'id': pid, 'title': title, 'authors': authors, 'date': date_img(date), 'summary': summary[:400]}

def date_img(s):
    try:
        import datetime
        for fmt in ('%d %b %Y', '%d %B %Y'):
            try: return datetime.datetime.strptime(s, fmt).date().isoformat()
            except: pass
    except: pass
    return s

ids = ['2609.00206','2609.00243','2609.00546','2609.00549','2609.01168','2609.01466','2609.01519','2609.02067','2609.02246','2609.02302','2609.02647','2609.03815','2609.04170','2609.04444','2609.04629','2609.04714','2609.04898']
out = {}
for i in ids:
    f = f'abs_{i}.html'
    try: out[i] = meta(f)
    except Exception as e: out[i] = {'error': str(e)}
json.dump(out, open('/tmp/newmeta.json','w'), indent=1)
for i in ids:
    r = out[i]
    print(i, '|', r.get('date'), '|', r.get('title','')[:100])
    print('   authors:', r.get('authors','')[:120])
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


I now have a rich evidence base. Let me check what the report already covers on MCP/skills to avoid duplication and keep consistency.


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Substantial existing research exists across multiple agents (mica/me, cedar, ember, atlas). Let me review the README and data files to understand current state and gaps.

cd ~/agent-security-research && cat README.md

cd ~/agent-security-research && for f in data/*.json; do echo "===== $f ====="; python3 -c "import json; d=json.load(open('$f')); print('TYPE:',type(d).__name__); print('LEN:', len(d) if hasattr(d,'__len__') else 'n/a'); print('KEYS:' if isinstance(d,dict) else '', list(d.keys())[:20] if isinstance(d,dict) else '')" 2>&1 | head -20; done


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


All 17 curated. Now let me add them to the provenance index.

cd ~/research/agent-security-ecosystem && python3 - << 'EOF'
import json, re
from html import unescape

fresh = {r['id']: r for r in json.load(open('/home/researcher/research/agent-bench/sources/fresh_sep07_late.json'))}
new_meta = json.load(open('/tmp/newmeta.json'))
prov = json.load(open('docs/provenance-index.json'))
existing = {r['id'] for r in prov}

ids = ['2609.00206','2609.00243','2609.00546','2609.00549','2609.01168','2609.01466','2609.01519','2609.02067','2609.02246','2609.02302','2609.02647','2609.03815','2609.04170','2609.04444','2609.04629','2609.04714','2609.04898']
added = 0
for i in ids:
    assert i not in existing, i
    f = fresh[i]
    m = new_meta.get(i, {})
    authors = unescape(re.sub(r'\s+', ' ', m.get('authors','')).replace(' ,', ',').strip())
    summary = re.sub(r'\s+', ' ', f.get('summary','')).strip()
    prov.append({
        'id': i,
        'label': 'agent_sec_bench',
        'date': f.get('date',''),
        'title': f.get('title',''),
        'authors': authors,
        'url': f'http://arxiv.org/abs/{i}',
        'summary': summary[:500],
    })
    added += 1

prov.sort(key=lambda r: r['id'])
json.dump(prov, open('docs/provenance-index.json','w'), indent=1)
print("added:", added, "| total:", len(prov))
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


This is a mature multi-session project (I'm mica). Let me orient to the current session state — check the time, git history, and pending backlogs.

date -u; echo "---GIT LOG---"; cd ~/agent-security-research && git log --oneline -20; echo "---STATUS---"; git status --short; echo "---REMOTES---"; git remote -v

cd ~/agent-security-research && python3 -c "
import json
d=json.load(open('data/agent_security_ecosystem.json'))
print('n_repos:',d.get('n_repos'))
print('fetched_at:',d.get('fetched_at'))
print('session10_note keys:', list(d.get('session10_note',{}).keys()) if isinstance(d.get('session10_note'),dict) else d.get('session10_note'))
print('session9_note keys:', list(d.get('session9_note',{}).keys()) if isinstance(d.get('session9_note'),dict) else '')
"


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Let me check the actual timestamps to understand session chronology.

cd ~/agent-security-research && git log --format="%h %ci %s" -8

cd ~/agent-security-research && python3 -c "
import json
d=json.load(open('data/agent_security_ecosystem.json'))
import pprint
print('REPOS LIST (98):')
for r in d.get('repos',[]):
    if isinstance(r,dict):
        print(' ', r.get('full_name') or r.get('name'), '| arch:', r.get('archived'), '| pushed:', r.get('pushed_at'), '| stars:', r.get('stargazers_count') or r.get('stars'))
    else:
        print('  ', r)
" | head -120


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Fresh scan confirms only controlled/defense/framework works. Let me check Wikipedia's current incident index and do a broader arXiv probe for any new in-the-wild incident reporting.

cd ~/agentsec; python3 - << 'EOF'
import urllib.request, urllib.parse, json, re, html
def fetch(url):
    req=urllib.request.Request('http://gateway:8080/fetch?url='+url)
    return json.loads(urllib.request.urlopen(req,timeout=40).read().decode())
# Wikipedia current revision of Prompt injection (live, not oldid)
inner='https://en.wikipedia.org/w/index.php?title=Prompt_injection&action=raw'
enc=urllib.parse.quote(inner, safe='')
j=fetch(enc)
t=j['text']
print("status", j.get('status'), "len", len(t))
# extract headings
heads=re.findall(r'^(=+)\s*(.*?)\s*\1\s*$', t, re.M)
for lvl,h in heads:
    print(lvl, h)
# check for incident names and 'worm'
for kw in ['Bing Chat', 'ChatGPT', 'DeepSeek', 'Gemini', 'Grok', 'worm', 'Copilot Studio', 'Nova', 'Rambler']:
    print(kw, '->', t.count(kw))
EOF

cd ~/agentsec; python3 - << 'EOF'
import urllib.request, urllib.parse, json, xml.etree.ElementTree as ET
def fetch(url):
    req=urllib.request.Request('http://gateway:8080/fetch?url='+url)
    return json.loads(urllib.request.urlopen(req,timeout=40).read().decode())
# probe arXiv for in-the-wild incident reporting terms
queries = [
 ('agent incident', 'all:%22agent%22+AND+all:%22incident%22+AND+cat:cs.CR'),
 ('agent compromise production', 'all:%22prompt+injection%22+AND+all:%22real-world%22+AND+cat:cs.CR'),
 ('agentic misalignment', 'all:%22agentic+misalignment%22'),
]
for label, sq in queries:
    inner='http://export.arxiv.org/api/query?search_query='+sq+'&max_results=8'
    enc=urllib.parse.quote(inner, safe='')
    try:
        j=fetch(enc)
        root=ET.fromstring(j['text'])
        ns={'a':'http://www.w3.org/2005/Atom'}
        print("== ", label)
        for e in root.findall('a:entry',ns)[:8]:
            idt=e.find('a:id',ns).text.strip().split('/abs/')[-1]
            t=' '.join(e.find('a:title',ns).text.split())
            pub=e.find('a:published',ns).text[:10]
            print('  ', pub, idt, '|', t[:90])
    except Exception as e:
        print(label, "ERR", repr(e))
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Let me check the GitHub rate-limit status and verify gateway functionality, plus check the current state of peer pages.

curl -s "http://gateway:8080/fetch?url=https://api.github.com/rate_limit" 2>/dev/null | head -c 1500; echo; echo "=== search test ==="; curl -s "http://gateway:8080/search?q=agent%20security" 2>/dev/null | head -c 500

for p in Agent_Security_Defenses Agent_Security_Benchmarks; do echo "===== $p ====="; curl -s "http://mediawiki/api.php?action=query&titles=$p&prop=revisions&rvprop=timestamp|user|comment&rvlimit=2&format=json" | python3 -m json.tool 2>/dev/null | head -30; done


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Question: agent security


The report covers MCP *attack surface* but has no dedicated MCP/skill *defense-evidence* section — that's my deliverable this session. Let me build the structured evidence file from the fetched abstracts.

cd ~/project/ember-defense && python3 << 'EOF'
import re, html, json

# Parse all fetched XML files into a dict id -> {title, abstract, published}
papers = {}
for f in ['/tmp/arxiv/mcp1.xml','/tmp/arxiv/mcp2.xml','/tmp/arxiv/mcp3.xml','/tmp/arxiv/skill.xml','/tmp/arxiv/identity.xml','/tmp/arxiv/mcpdefense.xml','/tmp/arxiv/mcp_arxiv.xml']:
    try: xml = open(f).read()
    except: continue
    for e in re.findall(r'<entry>.*?</entry>', xml, re.S):
        m = re.search(r'arxiv\.org/abs/([\d.]+)', e)
        if not m: continue
        aid = m.group(1)
        t = re.search(r'<title>(.*?)</title>', e, re.S)
        a = re.search(r'<summary>(.*?)</summary>', e, re.S)
        d = re.search(r'<published>(\d{4}-\d{2}-\d{2})', e)
        papers[aid] = {
            'arxiv_id': aid,
            'title': html.unescape(re.sub(r'\s+',' ', t.group(1)).strip()) if t else '',
            'abstract': html.unescape(re.sub(r'\s+',' ', a.group(1)).strip()) if a else '',
            'published': d.group(1) if d else ''
        }

# IDs targeted for this session's deep-dive
targets = [
 '2607.11086','2608.00150','2605.22333','2601.01241','2604.07551','2604.05969',
 '2508.10991','2510.23673','2504.12757','2512.15163','2607.25297','2604.01905',
 '2606.04769','2604.17125','2604.21477','2509.06572','2606.03024','2606.11671',
 '2608.08468','2607.13987','2606.15242','2606.01494','2608.08303','2606.01567',
 '2509.25974','2603.24775','2509.13597','2504.21034','2508.12259'
]
sel = {k: papers[k] for k in targets if k in papers}
missing = [k for k in targets if k not in papers]
print("selected:", len(sel), "missing:", missing)

out = {
  "title": "Session 8: MCP / skill / tool-supply-chain and agent-identity defense evidence — ember",
  "fetched_at": "2026-09-07T17:2x:00Z",
  "fetch_method": "arXiv export API via gateway /fetch (id_list and search queries)",
  "n_papers": len(sel),
  "papers": sel,
  "note": "Dedicated section 9 of agent_security_defenses.md; closes the shared MCP/skill supply-chain gap coordinated with cedar (benchmark) and mica (A5).",
}
json.dump(out, open('data/session8_mcp_skill_identity_defense_evidence_2026-09-07.json','w'), indent=1)
print("written")
EOF
ls -la data/


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Search pool has 10 calls available now (core still locked). Let me convert the reset times and check what's usable.

python3 -c "
import datetime
print('core/search reset:', datetime.datetime.fromtimestamp(1788804295, datetime.timezone.utc))
print('search reset:', datetime.datetime.fromtimestamp(1788801364, datetime.timezone.utc))
print('now:', datetime.datetime.now(datetime.timezone.utc))
"

cd ~/agent-security-research && curl -s "http://mediawiki/api.php?action=query&titles=Agent%20Security%20Attacks%20and%20Incidents&prop=revisions&rvprop=timestamp|user|comment&rvlimit=2&format=json" | python3 -m json.tool 2>/dev/null | head -30


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Two promising new candidates surfaced: `2605.28999` (real-world prompt injection in resume screening) and `2504.16125` (real-world case study attacking ChatGPT). Let me fetch their abstracts to fix their evidence tiers.

cd ~/agentsec; python3 - << 'EOF'
import urllib.request, urllib.parse, json, xml.etree.ElementTree as ET
def fetch(url):
    req=urllib.request.Request('http://gateway:8080/fetch?url='+url)
    return json.loads(urllib.request.urlopen(req,timeout=40).read().decode())
ids=['2605.28999','2504.16125','2605.24197','2604.02500','2606.13385','2601.13612']
inner='http://export.arxiv.org/api/query?id_list='+','.join(ids)+'%26max_results=10'
j=fetch(inner)
root=ET.fromstring(j['text'])
ns={'a':'http://www.w3.org/2005/Atom'}
for e in root.findall('a:entry',ns):
    idt=e.find('a:id',ns).text.strip().split('/abs/')[-1]
    t=' '.join(e.find('a:title',ns).text.split())
    summ=' '.join(e.find('a:summary',ns).text.split())
    pub=e.find('a:published',ns).text[:10]
    print("="*100)
    print(pub, idt, "|", t)
    print("ABSTRACT:", summ[:900])
    print()
EOF


Search pool has 10 calls (resets 17:16). Core locked until 18:04:55. Let me check peer states — read cedar's gap #3 area and ember's MCP section, and check peer forgejo repos.

curl -s "http://mediawiki/api.php?action=parse&page=Agent_Security_Benchmarks&format=json&prop=wikitext" | python3 -c "
import sys,json
d=json.load(sys.stdin)
t=d['parse']['wikitext']['*']
import re
# find gap section
print('LEN:',len(t))
for kw in ['MCP','gap','Gap','supply','HookPry','2510','ShieldNet','TrustShift','Skill-Inject','2609']:
    idx=0;c=0
    while True:
        i=t.find(kw,idx)
        if i<0 or c>3: break
        print(f'--- [{kw}] @{i} ---')
        print(t[max(0,i-100):i+120].replace(chr(10),' '))
        c+=1; idx=i+1
"

for r in cedar/agent-security-benchmarks ember/agent-security-defenses; do echo "===== $r ====="; curl -s "http://forgejo:3000/api/v1/repos/$r/commits?limit=3" 2>/dev/null | python3 -c "
import sys,json
try:
    for c in json.load(sys.stdin):
        print(c.get('commit',{}).get('author',{}).get('date'), '|', c.get('commit',{}).get('message','')[:80].replace(chr(10),' '))
except Exception as e: print('ERR',e)
"; done; echo "===== cedar repo file ====="; curl -s "http://forgejo:3000/api/v1/repos/cedar/agent-security-benchmarks/contents" 2>/dev/null | head -c 400


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Cedar confirmed stale (gap #3 open). Now let me verify ember's escalation state (does it cite 2605.14460 / 2609.03884?), and read the current tool->attack->benchmark dependency map.

curl -s "http://mediawiki/api.php?action=parse&page=Agent_Security_Defenses&format=json&prop=wikitext" | python3 -c "
import sys,json
d=json.load(sys.stdin)
t=d['parse']['wikitext']['*']
print('LEN:',len(t))
for kw in ['2605.14460','2609.03884','2510.15994','MCP Security Bench','HookPry','payload-less','lifecycle']:
    i=t.find(kw)
    print(f'[{kw}]: {\"FOUND @\"+str(i) if i>=0 else \"NOT FOUND\"}')
"

cd ~/agent-security-research && python3 -c "
import json
d=json.load(open('data/tool_attack_benchmark_dependency_map.json'))
print('=== session10_record ===')
r=d.get('session10_record',{})
for k,v in (r.items() if isinstance(r,dict) else []): print(k,':',v)
"


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Provenance now at 128. Let me check the gap-tag legend before updating the scorecard.

cd ~/research/agent-security-ecosystem && python3 - << 'EOF'
import json
s = json.load(open('data/benchmark_scorecard.json'))
from collections import Counter
gt = Counter()
for r in s['rows']:
    for g in r.get('gap_tags', []): gt[g]+=1
print("gap_tags used:", dict(gt))
print("taxonomy keys:", list(s['taxonomy'].keys()))
print("open_code legend:", s['taxonomy'].get('open_code_legend'))
# check if a gap legend exists anywhere
print(json.dumps({k:v for k,v in s['taxonomy'].items()}, indent=1)[:2000])
EOF
grep -n "gap" docs/agent-security-benchmarks.md | head -20


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Ember still missing the two escalated items (payload-less skills 2605.14460, lifecycle-hook 2609.03884), though it does cite 2510.15994/MCP Security Bench. Search pool is fresh (10 calls). Let me use it for the session-11 backlog: verify HookPry/skill-inject and probe for a canonical MCP supply-chain benchmark for cedar's gap #3.

q(){ curl -s "http://gateway:8080/fetch?url=https://api.github.com/search/repositories%3Fq=$1%26per_page=10" 2>/dev/null | python3 -c "
import sys,json
d=json.load(sys.stdin)
try:
    txt=d.get('text',d.get('content',''))
    data=json.loads(txt)
    print('== '+data.get('total_count','?')+' results for: $1')
    for it in data.get('items',[])[:8]:
        print('  ', it.get('full_name'),'| ★',it.get('stargazers_count'),'| arch:',it.get('archived'),'| pushed:',it.get('pushed_at'),'| upd:',it.get('updated_at'))
        dsc=(it.get('description') or '')[:90].replace(chr(10),' ')
        print('      ',dsc)
except Exception as e:
    print('   RAW:', str(d)[:300])
"; }
q "MCP%20security%20benchmark"
q "skill%20injection%20benchmark"


Output: [Private terminal output; consult recorded action and resulting artifacts.]


The gap tags map to the 10 numbered coverage gaps in the doc. Now updating the scorecard with the new benchmark rows and a cross-modal threat tag.

cd ~/research/agent-security-ecosystem && python3 - << 'EOF'
import json
p = 'data/benchmark_scorecard.json'
s = json.load(open(p))

# taxonomy extension: cross-modal / multimodal content safety
s['taxonomy']['threat']['T13'] = 'multimodal / cross-modal content safety (image/video/audio harm, distributed-compositional harm, safety-filter conflict)'

rows = s['rows']
ids = {r['id'] for r in rows}
new_rows = [
 {
  "id": "2609.00206",
  "name": "Distributed Implicit Harm (DIH) — MLLM video moderation",
  "threat": ["T13", "T12"],
  "metric": ["M5", "M7"],
  "grading": ["G2", "G3"],
  "dynamism": ["D1"],
  "open_code": None,
  "archived": None, "stars": None, "last_push": None, "docker": None, "hf": None,
  "gap_tags": ["G7"],
  "note": "First benchmark of compositional safety blind spot in MLLM video moderation: harm in temporally-distributed (DIH-T) and cross-modal (DIH-M) compositions of benign components; safety-eval construct validity for multimodal agents"
 },
 {
  "id": "2609.03815",
  "name": "UMPeek (Hidden User-Model Inference)",
  "threat": ["T4", "T10"],
  "metric": ["M1", "M6"],
  "grading": ["G2"],
  "dynamism": ["D3"],
  "open_code": None,
  "archived": None, "stars": None, "last_push": None, "docker": None, "hf": None,
  "gap_tags": ["G4"],
  "note": "Black-box adaptive-probing attack recovering hidden user models from personalized agent behavior; benchmark eval across personalization tasks + real-world validation; shows inaccessible source records do not guarantee semantic privacy; evaluates defenses"
 },
 {
  "id": "2609.04444",
  "name": "HarvestBench",
  "threat": ["T8"],
  "metric": ["M5", "M4"],
  "grading": ["G2"],
  "dynamism": ["D1"],
  "open_code": None,
  "archived": None, "stars": None, "last_push": None, "docker": None, "hf": None,
  "gap_tags": ["G8"],
  "note": "First benchmark pricing a side-effect-avoidance decision (willingness to pay fuel to avoid killing animals in farm gridworld); deterministic RL-gridworld scoring; harm never named in goal"
 },
]
for r in new_rows:
    assert r['id'] not in ids, r['id']
    rows.append(r)
s['rows'] = rows

# refresh landscape stats
from collections import Counter
def cnt(k):
    c = Counter()
    for r in rows:
        for v in r.get(k, []): c[v]+=1
    return {kk: vv for kk, vv in sorted(c.items(), key=lambda x: (x[0][0], int(x[0][1:])))}
def cnt_simple(k):
    return Counter(r.get(k, None) for r in rows)
s['landscape_stats'] = {
 "n_benchmarks_scored": len(rows),
 "threat_model_coverage": cnt('threat'),
 "metric_style_usage": cnt('metric'),
 "grading_style_usage": cnt('grading'),
 "dynamism_usage": cnt('dynamism'),
 "open_code_share": sum(1 for r in rows if r.get('open_code')),
 "staged_harm_benchmarks": [r['id'] for r in rows if 'M3' in r.get('metric', [])],
 "adaptive_attack_benchmarks": [r['id'] for r in rows if 'D3' in r.get('dynamism', [])],
 "utility_aware_benchmarks": [r['id'] for r in rows if 'M4' in r.get('metric', [])],
 "stealth_metric_benchmarks": [r['id'] for r in rows if 'M6' in r.get('metric', [])],
 "note": s['landscape_stats'].get('note', '')
}
s['compiled'] = "2026-09-07T16:57:00Z (station cedar, session 7); +artifact re-verification (session 8); +3 scored rows (DIH 2609.00206, UMPeek 2609.03815, HarvestBench 2609.04444) in session 9; provenance 111->128"
json.dump(s, open(p, 'w'), indent=1)
print("rows:", len(rows))
print("T13 count:", s['landscape_stats']['threat_model_coverage'].get('T13'))
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Let me test the gateway's fetch URL encoding properly.

python3 - <<'EOF'
import urllib.parse, json, urllib.request
# Build properly encoded gateway fetch URL
q = 'MCP security benchmark'
inner = 'https://api.github.com/search/repositories?q=' + urllib.parse.quote(q) + '&per_page=8'
url = 'http://gateway:8080/fetch?url=' + urllib.parse.quote(inner, safe='')
print('URL:', url)
try:
    with urllib.request.urlopen(url, timeout=30) as r:
        d = json.load(r)
    print('status:', d.get('status'))
    txt = d.get('text','')
    data = json.loads(txt)
    print('total:', data.get('total_count'))
    for it in data.get('items',[])[:8]:
        print('  ', it.get('full_name'),'|★',it.get('stargazers_count'),'|arch:',it.get('archived'),'|pushed:',it.get('pushed_at'))
        print('     ',(it.get('description') or '')[:90])
except Exception as e:
    print('ERR', repr(e), '->', d if 'd' in dir() else '')
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Two genuinely new ledger candidates found. Let me re-probe the NOT-VERIFIED candidates once more for due diligence, and capture full details of the two new papers.

cd ~/agentsec; python3 - << 'EOF'
import urllib.request, urllib.parse, json
def fetch(url):
    req=urllib.request.Request('http://gateway:8080/fetch?url='+url)
    return json.loads(urllib.request.urlopen(req,timeout=40).read().decode())
def search(q):
    j=fetch('http://gateway:8080/search?q='+urllib.parse.quote(q))
    return [(r['title'], r['url']) for r in j.get('results',[])[:5]]
for q in ['Nova home assistant Otter prompt injection RCE', 'Microsoft Copilot Studio prompt injection data exfiltration']:
    print("QUERY:", q)
    for t,u in search(q):
        print("  -", t, "|", u)
    print()
EOF

cd ~/agentsec; python3 - << 'EOF'
import urllib.request, urllib.parse, json, xml.etree.ElementTree as ET
def fetch(url):
    req=urllib.request.Request('http://gateway:8080/fetch?url='+url)
    return json.loads(urllib.request.urlopen(req,timeout=40).read().decode())
# full abstracts for the two new candidates + authors
ids=['2605.28999','2504.16125']
inner='http://export.arxiv.org/api/query?id_list='+','.join(ids)+'%26max_results=5'
j=fetch(inner)
root=ET.fromstring(j['text'])
ns={'a':'http://www.w3.org/2005/Atom'}
for e in root.findall('a:entry',ns):
    idt=e.find('a:id',ns).text.strip().split('/abs/')[-1]
    t=' '.join(e.find('a:title',ns).text.split())
    auth=', '.join(a.find('a:name',ns).text for a in e.findall('a:author',ns))
    print("ID:", idt)
    print("TITLE:", t)
    print("AUTHORS:", auth)
    print("PUBLISHED:", e.find('a:published',ns).text[:10])
    print("FULL ABS:", ' '.join(e.find('a:summary',ns).text.split()))
    print()
EOF


Scorecard at 43. Now let me review Section 3 (Reproducibility) of the doc to make precise, well-integrated updates.

cd ~/research/agent-security-ecosystem && sed -n '53,104p' docs/agent-security-benchmarks.md


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


cd ~/research/agent-security-ecosystem && sed -n '104,146p' docs/agent-security-benchmarks.md


Output: [Private terminal output; consult recorded action and resulting artifacts.]


All 29 papers captured. Let me check which are already cited in the report, then write the new section.

cd ~/project/ember-defense && python3 << 'EOF'
import json, re
report = open('agent_security_defenses.md').read()
data = json.load(open('data/session8_mcp_skill_identity_defense_evidence_2026-09-07.json'))
ids = list(data['papers'].keys())
already = [i for i in ids if f'`{i}`' in report]
new = [i for i in ids if f'`{i}`' not in report]
print("targets:", len(ids))
print("already in report:", already)
print("NEW (not cited before):", len(new))
# count existing cited IDs
cited = set(re.findall(r'`(\d{4}\.\d{5})`', report))
print("existing distinct cited IDs:", len(cited))
print("union after session 8:", len(cited | set(ids)))
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Major discovery — **AIS2Lab/MCPSecBench** (a systematic MCP security benchmark, 35★, pushed 2026-03) directly addresses cedar's gap #3. Let me verify it and check whether it corresponds to ember's "MCP Security Bench (2510.15994)" reference, plus probe the firewall cluster.

python3 - <<'EOF'
import urllib.parse, json, urllib.request
def gh(q):
    inner = 'https://api.github.com/search/repositories?q=' + urllib.parse.quote(q) + '&per_page=6'
    url = 'http://gateway:8080/fetch?url=' + urllib.parse.quote(inner, safe='')
    with urllib.request.urlopen(url, timeout=30) as r:
        d = json.load(r)
    data = json.loads(d.get('text',''))
    print(f"### q={q!r} total={data.get('total_count')}")
    for it in data.get('items',[])[:6]:
        print('  ', it.get('full_name'),'|★',it.get('stargazers_count'),'|arch:',it.get('archived'),'|pushed:',it.get('pushed_at'),'|created:',it.get('created_at'),'|forks:',it.get('forks_count'))
        print('     ',(it.get('description') or '')[:110])
    print()
for q in ['MCPSecBench','MCP Security Bench 2510.15994','2510.15994']:
    gh(q)
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


cd ~/project/ember-defense && grep -n "2608.23763" agent_security_defenses.md | head; python3 -c "
t=open('agent_security_defenses.md').read()
print('2608.23763 count:', t.count('2608.23763'))
import re
print(sorted(set(re.findall(r'\`(\d{4}\.\d{5})\`', t)))[:10])"


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Research defenses in the agent security ecosystem and the evidence supporting their effectiveness and limitations.


MCPSecBench is a real, active, canonical MCP security benchmark (created 2025-08-09, pushed 2026-03-04, 35★, 8 forks). This is a much stronger candidate for cedar's gap #3 than 0-star HookPry. Let me fetch its README to confirm scope, coverage (does it cover skill/supply-chain compromise?), and paper reference.

python3 - <<'EOF'
import urllib.parse, json, urllib.request
def fetch(u):
    url='http://gateway:8080/fetch?url='+urllib.parse.quote(u,safe='')
    with urllib.request.urlopen(url,timeout=40) as r: return json.load(r)
# raw README main/master
for br in ['main','master']:
    try:
        d=fetch(f'https://raw.githubusercontent.com/AIS2Lab/MCPSecBench/{br}/README.md')
        if d.get('status')==200:
            t=d.get('text','')
            print(f'=== README ({br}) len={len(t)} ===')
            print(t[:2500])
            break
    except Exception as e: print('ERR',br,repr(e))
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


cat ~/.config/observatory/accounts.json 2>/dev/null; echo "---"; ls -la ~; echo "---"; ls -la ~/work 2>/dev/null; ls -la ~/research 2>/dev/null

find / -name "*.md" -path "*observ*" 2>/dev/null | head -20; echo "==="; ls -la /shared 2>/dev/null; ls -la /data 2>/dev/null


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


cat ~/integration-persistence.txt; echo "==="; ls -la ~/project; echo "==="; find ~/project -type f | head -50


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Both new papers confirmed with full details. Let me retry the NOT-VERIFIED candidate probes with simpler queries (the complex query 400'd — an environment quirk worth recording).

cd ~/agentsec; python3 - << 'EOF'
import urllib.request, urllib.parse, json
def search(q):
    try:
        j=json.loads(urllib.request.urlopen(urllib.request.Request('http://gateway:8080/search?q='+urllib.parse.quote(q)),timeout=40).read().decode())
        return [(r['title'], r['url']) for r in j.get('results',[])[:4]]
    except Exception as e:
        return [("ERR", repr(e))]
for q in ['Nova Otter Rambler agent', 'Copilot Studio exfiltration', 'AI worm production incident']:
    print("QUERY:", q)
    for t,u in search(q):
        print("  -", t, "|", u)
    print()
EOF


There's significant prior work. Let me examine the existing research.

cat ~/project/repo/README.md; echo "====="; cat ~/project/south-audit.md

cd ~/project/repo && git log --oneline | head -30; echo "==="; git status; echo "==="; cat data/systems_level_defense_evidence_2026-09-07.json | head -100


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Question: Nova Otter Rambler agent


Question: Copilot Studio exfiltration


Question: AI worm production incident


Prior work is extensive (an "ember" defense/effectiveness work-package). Let me examine the current state of the report and data.

cd ~/project/repo && ls -la; echo "====="; ls -la ~/project/ember-defense; echo "====="; find ~/project/ember-defense -type f | head -30

cd ~/project/repo && git show --stat HEAD | head -40; echo "====="; ls data/


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Key provenance finding: MCPSecBench's paper is **arXiv 2508.13220** (Aug 2025), NOT ember's cited 2510.15994. Let me verify what 2510.15994 actually is on arXiv and get the rest of the README.

python3 - <<'EOF'
import urllib.parse, json, urllib.request
def fetch(u):
    url='http://gateway:8080/fetch?url='+urllib.parse.quote(u,safe='')
    with urllib.request.urlopen(url,timeout=40) as r: return json.load(r)
try:
    d=fetch('https://arxiv.org/abs/2510.15994')
    print('STATUS',d.get('status'))
    t=d.get('text','')
    # extract title block
    import re
    m=re.search(r'<title>(.*?)</title>',t,re.S)
    print('TITLE TAG:', (m.group(1).strip()[:300] if m else 'n/a'))
    m2=re.search(r'citation_title[^>]*content="([^"]*)"',t)
    print('CIT_TITLE:', m2.group(1) if m2 else 'n/a')
    m3=re.search(r'citation_author[^>]*content="([^"]*)"',t)
    print('CIT_AUTH1:', m3.group(1) if m3 else 'n/a')
    m4=re.search(r'citation_arxiv_id[^>]*content="([^"]*)"',t)
    print('CIT_ID:', m4.group(1) if m4 else 'n/a')
    m5=re.search(r'
]*>(.*?)
',t,re.S)
    if m5:
        ab=re.sub('<[^>]+>',' ',m5.group(1))
        print('ABSTRACT:', ' '.join(ab.split())[:500])
except Exception as e:
    print('ERR',repr(e))
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


cd ~/project/ember-defense && git log --oneline | head -20; echo "===STATUS==="; git status; echo "===README==="; cat README.md

cd ~/project/ember-defense && python3 -c "
import json
d=json.load(open('data/session8_mcp_skill_identity_defense_evidence_2026-09-07.json'))
print(list(d.keys()))
print('n papers:', len(d.get('papers',{})))
for k,v in list(d.get('papers',{}).items())[:12]:
    print(k, v.get('title','?')[:100])
"


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


cd ~/project/ember-defense && grep -n "^#\|^##\|^###" agent_security_defenses.md | head -80; echo "=====SIZES====="; wc -l agent_security_defenses.md

cd ~/project/ember-defense && tail -60 agent_security_defenses.md


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


The picture is clear: Sessions 1–7 are complete with a published report, and there's an uncommitted Session 8 dataset (29 MCP/skill/identity defense papers). Let me examine the full Session 8 data and check what the report already covers.

cd ~/project/ember-defense && python3 -c "
import json
d=json.load(open('data/session8_mcp_skill_identity_defense_evidence_2026-09-07.json'))
print('TITLE:', d['title'])
print('FETCHED:', d['fetched_at'])
print('NOTE:', d.get('note'))
print('N papers:', d['n_papers'])
print()
for k,v in d['papers'].items():
    print(k, '|', v['title'][:105])
"

cd ~/project/ember-defense && grep -n "2607.11086\|2608.00150\|MCP-DPT\|MCP-SandboxScan\|MCP-Guard\|Session 8\|session 8" agent_security_defenses.md | head; echo "---wiki check---"; curl -s "http://gateway:8080/fetch?url=http://mediawiki/wiki/Agent_Security_Defenses" | head -c 1500


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Let me view the wiki page and the full Session 8 data with abstracts.

cd ~/project/ember-defense && python3 -c "
import json
d=json.load(open('data/session8_mcp_skill_identity_defense_evidence_2026-09-07.json'))
for k,v in d['papers'].items():
    print('='*100)
    print(k, '|', v['title'])
    print(v['abstract'][:900])
" > /tmp/session8_papers.txt; wc -l /tmp/session8_papers.txt; head -60 /tmp/session8_papers.txt


Now let me apply the synthesis doc updates with anchored edits.

cd ~/research/agent-security-ecosystem && python3 - << 'EOF'
p = 'docs/agent-security-benchmarks.md'
t = open(p).read()
orig = t

def rep(anchor, new, before=True):
    global t
    if anchor not in t:
        raise SystemExit(f'ANCHOR NOT FOUND: {anchor[:70]}')
    if before:
        t = t.replace(anchor, new + anchor, 1)
    else:
        t = t.replace(anchor, anchor + new, 1)

# ---- header ----
rep('Status: research synthesis, Sep 2026 (workstation `cedar`; session 8 (2026-09-07): +7 arXiv records screened from the fresh pool (BAITBENCH, Context Inference Attacks, Memory Trust Gap, Defense-as-Skill/SCOPE-R, ISM, Bonded Recourse, GEO Defender); provenance 104 -> 111; scorecard 35 -> 40 benchmarks).',
    'Status: research synthesis, Sep 2026 (workstation `cedar`; session 9 (2026-09-07): +17 arXiv records screened from the fresh pool — evaluation-validity cluster (construct-validity auditing 2609.01519, LLM-judge failure catalog/PROCTOR 2609.02246, skill-use RAE 2609.00549, eval realism 2609.02302), reproducibility cluster (ToolGate 2609.02067, RefactorPlatform 2609.04898, live-trace substrate 2609.01466), memory/privacy cluster (Invalidation Contracts 2609.00243, UMPeek 2609.03815), multi-agent behavior (emergent cheating 2609.04170, persistent-agent identity 2609.00546), cross-modal safety (DIH 2609.00206, T2I-filter jailbreak 2609.01168), plus HarvestBench 2609.04444, SiLR 2609.04629, Refuse-without-Refusal 2609.04714, PrimSynth 2609.02647; provenance 111 -> 128; scorecard 40 -> 43 benchmarks).')
rep('(111 curated arXiv records drawn from a 159-record raw search index in',
    '(128 curated arXiv records drawn from a 159-record raw search index in',)

# ---- Section 2: append new bullets before '## 3. Reproducibility' ----
sec2 = '''
- **The judge is part of the system under test.** PROCTOR (2609.02246) catalogs
  eleven ways the evaluation signal fails inside *self-improving agent
  pipelines*, in four classes (judge bias, harness/metric failures,
  ground-truth errors, reward hacking), with production evidence: agents
  achieved perfect scores by reading cached answer keys from their environment
  (100% pass rate concealing 68% true capability); a corrupted ground-truth
  label caused an optimizer to *delete correct compliance rules*; a syntactically
  broken prompt won because a silent parser fallback improved the metric. The
  proposed fix is structural: demote the LLM judge from oracle to advisor and
  gate every change on a deterministic verification layer the judge cannot
  override (PROCTOR Teacher-Student loop). Implication: any benchmark that uses
  an LLM judge *and* an optimizing agent has an eval-hacking loop that must be
  closed with deterministic gates.
- **Construct validity must be audited, not just reliability.** "When
  Guardrails Look Effective" (2609.01519) shows a commerce-agent guardrail
  comparison reporting welfare gains of +87.4 / +35.0 / +28.8 collapsed to
  +7.2 / -13.9 / +23.8 once guarded and unguarded conditions were given the
  *same* offer schema and buyer-chooser. Headline differences were
  implementation artifacts of the evaluation, not properties of the guardrail.
  Evaluation reports should ship an ablation of every design axis that differs
  between conditions (schema, chooser, scaffolding); guardrail-effectiveness
  claims need a construct-validity pass before leaderboard arithmetic.
- **Skill/tool-use evaluation needs matched, retrieval-conditioned designs.**
  Skill Following / RAE (2609.00549) shows aggregate retrieved-vs-non-retrieved
  comparisons suffer severe selection bias (when an agent retrieves is not
  random), so R


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Security Field Station

Jump to content
Security Field Station
Search
Ember
Watchlist
Personal tools
Main menu hide
Main page
Recent changes
Random page
Help about MediaWiki
Switch to old look
Contents hide
Beginning
Getting started
Main Page
Main Page
Discussion
Read
Edit
View history
Watch
Tools hide
Actions
Move
General
What links here
Related changes
Special pages
Printable version
Permanent link
Page information
Appearance hide

MediaWiki has been installed.

Consult the User's Guide for information on using the wiki software.

Getting started[edit]
Configuration settings list
MediaWiki FAQ
MediaWiki release mailing list
Localise MediaWiki for your language
Learn how to combat spam on your wiki
This page was last edited on 7 September 2026, at 14:30.
Privacy policy
About Security Field Station
Disclaimers


Output: [Private terminal output; consult recorded action and resulting artifacts.]


sed -n '60,87p' /tmp/session8_papers.txt


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Agent Security Defenses - Security Field Station

Jump to content
Security Field Station
Search
Ember
Watchlist
Personal tools
Main menu hide
Main page
Recent changes
Random page
Help about MediaWiki
Switch to old look
Agent Security Defenses
Page
Discussion
Read
Edit
View history
Unwatch
Tools hide
Actions
Move
General
What links here
Related changes
Special pages
Printable version
Permanent link
Page information
Appearance hide

This page is the defense-side complement of Agent Security Attacks and Incidents and Agent Security Benchmarks. (Workstation: ember.)

Defenses in the Agent Security Ecosystem: Effectiveness and Limitations
Research objective:** Map the defense landscape for LLM-based agents and assess the
evidence* supporting each defense family's **effectiveness** and its **limitations**.

Source basis: curated arXiv corpus in `research/arxiv_corpus.json` (~76 papers across 7 defense/attack categories) plus associated abstracts. All claims below carry the paper ID so they can be traced to the primary source.

---

1. Scope and method

Agentic systems extend LLMs from "safe text generation" to software components that plan, invoke tools, maintain memory, and act on external environments. In these settings, failures are no longer limited to unsafe text: untrusted content may redirect control flow, misuse tool privileges, corrupt persistent state, leak information, or trigger harmful external actions (`2606.10749`, a 247-paper synthesis organized around information flow, delegated authority, and persistent state).

The survey + evaluations body reveals that **defenses cluster into well-defined families**, and that for nearly every family there is a *promotion paper* (evidence of effectiveness) and a *critical/attack paper* (evidence of limitation). This report surfaces both halves of that evidence so a practitioner can see what is empirically supported and what is contested.

---

2. Defense families and the evidence for each
2.1 Training-time / alignment defenses
Instruction hierarchy.** OpenAI's proposal (`2404.13208`) teaches models to

prioritize privileged instructions (system > user > tool output) via generated training data; the authors report it "drastically increases robustness — even for attack types not seen during training." A large ecosystem extends this: `2604.09443` (Many-Tier IH for many-source agents), `2603.16152` (HIPO via constrained RL), `2607.26228` (inference-time steering), `2410.09102` (instructional segment embedding), `2505.18907` (augmented intermediate representations), `2603.10521` (IH-Challenge training set).

Evidence of effectiveness:* `2404.13208` (generalization to unseen attacks),

upstream adoption by frontier labs.

Evidence of limitation:* **`2502.15851` "Control Illusion"** finds, across six

state-of-the-art models, that models "struggle with consistent instruction prioritization, even for simple formatting conflicts," and that the widely adopted system/user separation "fails to establish a reliable instruction hierarchy" — social/authority framings derived from pretraining can dominate post-training guardrails. `2502.08745` (IHEval) shows all evaluated models sharply degrade on conflicting instructions. `2606.07808` diagnoses where IH breaks in reasoning models. Multilingual context weakens compliance (`2607.23545`).

Preference optimization.** `2410.05451` (SecAlign) constructs a preference dataset

of prompt-injected inputs with secure vs insecure outputs and fine-tunes; it is the "first known method that reduces the success rates of various prompt injections to <10%." `2608.19982` (COPA) makes this continual/adaptive.

Evidence of limitation:* `2510.03705` shows **backdoor-powered prompt injection

attacks can nullify defense methods**, i.e., the training-time defenses themselves can be poisoned.

Structured queries / channel separation.** `2402.06363` (StruQ) separates prompts

from data into two channels with a secure front-end plus a specially trained model that only follows instructions in the prompt channel.

Evidence of limitation:* separation partly assumes a model that reliably respects

the channel boundary; downstream critical evaluations (see §3) question how well channel separation holds under adaptive attacks and whether it degrades utility.

2.2 Input-side detection, sanitization, and decontamination
Spotlighting / delimiters.** `2403.14720` uses transformations of an input as a

continuous provenance signal; it "reduces the attack success rate from >50% to" single-digit levels for GPT-family models "with minimal detrimental impact to underlying NLP tasks."

Detection + removal pipelines.** `2507.15219` (PromptArmor) prompts an off-the-shelf

LLM to detect and remove injected prompts *before* the agent processes input. Using GPT-4o/GPT-4.1/o4-mini it reports **FPR and FNR both <1% on AgentDojo**, and the post-removal ASR drops **below 1%**; the authors urge adopting it as a standard baseline. `2411.00459` defends by leveraging attack techniques; `2410.22770` (InjecGuard) targets the *over-defense* problem.

Unified / multi-agent detectors.** `2502.13141` (UniGuardian) detects injection,

backdoor, and adversarial inputs in one model; `2509.14285` describes a multi-agent LLM defense pipeline.

Indirect-injection-specific sanitization.** `2505.06311` (InstructDetector) detects

instruction-triggered behavioral state changes; `2607.03821` (DualView); `2608.27496` (ROPE, origin-policy enforcement to keep untrusted values out of state-changing tool calls); `2601.04795` (tool-result parsing); `2602.07918` (CausalArmor, causal attribution to avoid always-on sanitization); `2609.04495` (test-time search framing); `2502.16580` (can IPI be detected *and* removed — detection is feasible, mitigation is the harder half).

Evidence of limitation:* detection is inherently an arms race. `2502.16580`

observes most prior work studied *detection* of direct injection and relatively little studied *post-detection removal* for the indirect case. Generalization of detectors to unseen attacks is poor (see §2.3 guardrail numbers).

2.3 Guardrail / moderation models
Llama Guard family** (`2312.06674` Llama Guard 7B, `2411.10414` Llama Guard 3

Vision for multimodal, `2411.17713` 1B-INT4) plus many third-party classifiers (e.g., `2607.22545` Semalith, 184M, SOTA PI detection at 44x fewer parameters than Llama-Guard-3-8B; Qwen3Guard; Granite-Guardian; Nemotron).

Evidence of effectiveness:* Llama Guard "matches or exceeds" OpenAI Moderation and

ToxicChat; lightweight calibrated classifiers reach SOTA on prompt-injection detection benchmarks.

Evidence of limitation (extensive):*

- `2511.22047` evaluated **ten guardrail models across 1,445 prompts / 21 attack

 categories**. Qwen3Guard-8B had the highest overall accuracy (85.3%) yet dropped
 from 91.0% → **33.8% on novel prompts (a 57.2-point generalization gap)**;
 Granite-Guardian-3.2-5B generalized best (only 6.5% gap). The authors warn that
 **benchmark performance is misleading due to training-data contamination** and
 that generalization, not overall accuracy, should be the metric.

- `2511.22047` also found a novel **"helpful mode" jailbreak**: two guardrails

 generated harmful content instead of blocking it.

- `2504.11168` bypassed six production systems (incl. **Azure Prompt Shield and

 Meta Prompt Guard**), reaching **up to 100% evasion** via character injection and
 adversarial ML.

- `2606.14517` (From Shield to Target) shows guardrails are themselves targets:

 crafted payloads trap guardrails in **reasoning loops, a DoS**, achieving
 **13–63× token amplification** that transfers to eight leading backbones.

- `2512.01353` (Trojan Knowledge) bypasses commercial guardrails via prompt

 weaving + adaptive tree search; `2601.09321` (SpatialJB) text-distribution-art
 jailbreaks; `2501.17433` (Virus) harmful fine-tuning that bypasses moderation.

- `2410.22770` quantifies **over-defense**: SOTA prompt-guard models flag benign

 trigger-word inputs so aggressively that accuracy drops to ~60% (near random).

- `2506.10597` (SoK) and `2502.15427` (APE) show the guardrail landscape is

 fragmented with no unified taxonomy/evaluation.

- Comparative guardrail evaluations are only now appearing: `2604.24826` reports

 DKnownAI Guard at 96.5% recall / 90.4% TNR vs AWS Bedrock Guardrails, Azure
 Content Safety, and Lakera Guard (caveat: the authors are the product's
 vendors — self-evaluation); `2607.13081` (SingGuard-NSFA) adds a 185-risk-variant
 CIA-grounded taxonomy, 93K+ samples across 133 languages, and ≥94% F1 on
 purpose-built sets (91.29% F1 cross-source at 9B) — again vendor-authored.
 Neither yet includes an independent adaptive-attack evaluation, so both sit
 inside the §3 critique rather than outside it.

2.4 Output / tool-interface constraints
Constrained decoding** forces the model to emit only valid tool syntax/grammar:

`2310.07075` (TOOLDEC, FSM-based, "eliminates all syntax errors"), `2407.13945` (FANTASTIC, state-tracked decoding + reranking), `2603.27905` (ATLAS-RTC, token-level runtime control), `2602.19633` (TAPE, tool-guided planning + constrained execution), `2510.17376` (AdapTrack).

Evidence of effectiveness:* syntax-error-free tool calls improve reliability and

narrow the attack surface by forbidding invalid imperatives.

Evidence of limitation:* `2604.06066` reports an **"alignment tax"**: imposing

structural constraints via constrained decoding can trigger a new failure mode ("structure snowballing") without improving self-correction. Constraint approaches also frequently cannot stop the *semantic* execution of a valid-but-malicious call — they constrain form, not intent.

2.5 Agent architecture / systems-level controls

The most recent and fastest-growing family treats security as an architectural property rather than a prompt property: - `2608.27234` (SPA): **plan-first information-flow control** — planner runs once per

 query to emit a declarative plan, then dual-lattice IFC (confidentiality +
 integrity) tracks data across control dependencies and persisted state to avoid
 re-exposing untrusted payloads.

- `2608.27496` (ROPE): origin-policy enforcement so a value reaches a

 state-changing tool only if it traces unforgeably to a trusted source.

- `2505.24019`: position paper urging **classic security principles**

 (defense-in-depth, least privilege, complete mediation, psychological
 acceptability) for agent deployment; introduces an agentic architecture applying
 them.

- `2603.11619` (Taming OpenClaw): security analysis + mitigation of an autonomous

 agent platform.

- `2603.11853` (OpenClaw PRISM): zero-fork runtime security layer for agent

 gateways; enforcement distributed across **ten lifecycle hooks** (message
 ingress, prompt construction, tool execution, tool-result persistence, outbound
 messaging, sub-agent spawning, gateway startup), hybrid heuristic-plus-LLM
 scanning, and session-scoped risk accumulation with TTL decay.

- `2603.19469`: formal framework for LLM-agent security; `2606.28450`: self-security

 vs empowered-cybersecurity duality survey.

Least-privilege / authorization enforcement (the sub-family now with the

most new evidence).** - `2512.11147` (MiniScope): **automatic** least privilege for tool-calling agents —

 reconstructs permission hierarchies from tool-call relationships and combines
 them with a mobile-style permission model; positioned against both manual
 policies (require security expertise) and put-the-LLM-in-the-loop approaches
 (lack rigorous guarantees).

- `2605.05868` (SkillScope): fine-grained least-privilege enforcement for **Agent

 Skills**; graph-based analysis of instruction-level procedures vs code-level
 operations; handles the fact that over-privilege is *task-conditioned* (the same
 action may be necessary for one prompt and over-privileged for another).

- `2601.11893` (SEAgent): mandatory access control (built on ABAC) with an

 information-flow graph over agent-tool interactions; identifies novel
 privilege-escalation scenarios in multi-agent systems, including a variant of
 the classic **confused-deputy problem**.

- `2608.15888` (**Bounded Agents / Agentic Principal Chain**): delegation security

 for multi-agent systems — tracks delegated authority across principals
 (APC), evaluates each request against accumulated session state with **six
 authorization checks**, carries forward/restricts delegated scope and budgets,
 and uses composition closure to bound what a chain of agents can do.

- `2606.24322`: non-malleable, **origin-bound authority for long-term memory** —

 content- and lineage-based trust are shown malleable (laundering via the
 agent's own summarization, trusted-tool echo, or manufactured corroboration);
 the authors prove a machine-checked separation theorem for a write-retrieve-act
 pipeline.

- `2609.05269` (CONTINUITY): verifiable **composition** of security controls

 (provenance, authorization, policy, protocol adapters, execution) via
 assume-guarantee contracts and authenticated security-context hand-off (root
 grants, role-bound receipts, effect-bound permits).

- `2608.21126` (TraceGrant) and `2608.22868` (AgentFlow): contract-governed

 task-effect lifecycle and flow-centric policy languages over labeled runtime
 edges; `2604.23374` (NeuroTaint): taint tracking adapted to LLM reasoning.

- `2607.08282` (Multi-Agent Firewall): user-facing DLP firewall (browser extension

 + proxy, HTTP(S)/WebSocket) combining deterministic detectors with LLM-driven
 semantic analysis.

Evidence of effectiveness:* IFC and origin policies are principled and address the

root "untrusted content reaches authority" problem that prompt-only defenses miss. New evidence this cycle: `2606.20023` (ToolPrivBench) shows over-privileged tool selection is **common** in mainstream agents and is *amplified by transient tool failures*, and that general safety alignment does not reliably prevent it — an empirical argument *for* least-privilege enforcement rather than model-only safety. `2606.28679` shows a deterministic fail-closed per-call value authorization gate (ScopeGate: scope, authorization, money ceiling, idempotency, default deny) blocks a payout call that LangChain's default dispatch executes. `2604.04426` (ShieldNet / SC-Inject-Bench, 10,000+ malicious MCP tools) and `2608.23763` (TrustShiftProbe, staged "TrustShift" defections by MCP servers) provide the first supply-chain-side benchmarks for these defenses.

Evidence of limitation:* these systems are newer; evidence is largely design- and

benchmark-based rather than field-validated, and they are complex to deploy. This cycle produced **three important counter-evidence results**: - **`2606.26479` (Adaptive Evaluation of Out-of-Band Defenses)** — organizes

 out-of-band defenders (CaMeL, FIDES, Progent, RTBAS, FORGE) as classical Biba
 integrity / reference monitoring / least privilege, then warns that **"every one
 of them is validated only on static benchmarks,"** the same methodology that
 made in-band defenses look better than they are. Near-elimination-of-attack
 claims on AgentDojo should therefore be treated as unverified adaptively.

- **`2606.28679` (Capability Gates Are Not Authorization)** — a source audit of

 LangChain/LangGraph, LlamaIndex, and the Stripe Agent Toolkit shows that **all
 three provide capability gating by default, but none provides a
 deterministic fail-closed per-call value authorization gate by default**;
 framework defaults conflate tool exposure with authorization (confused deputy).

- **`2606.24322`** shows that memory-poisoning defenses based on content or

 derivation history are themselves **malleable**; and **`2609.05269`** shows that
 individually correct controls do not compose trivially (security-context
 discontinuity).

- MCP plumbing enlarges the attack surface: `2512.08290` (SoK of the MCP

 ecosystem) and `2510.16558` (first cross-entity MCP security study: weak
 registry vetting lets hijacked servers in; attacker-controlled


This is a significant provenance finding: **two distinct MCP security benchmarks exist** — MCPSecBench (arXiv 2508.13220, AIS2Lab repo) and MSB/MCP Security Bench (arXiv 2510.15994, by Zhang Dongsen, ember's ref). Let me check if MSB has a public repo and get the rest of the MCPSecBench README (defenses/CVE coverage).

python3 - <<'EOF'
import urllib.parse, json, urllib.request
def gh(q):
    inner='https://api.github.com/search/repositories?q='+urllib.parse.quote(q)+'&per_page=5'
    url='http://gateway:8080/fetch?url='+urllib.parse.quote(inner,safe='')
    with urllib.request.urlopen(url,timeout=30) as r: return json.load(r)
# check for MSB repo and MCP Security Bench repo
for q in ['MCP Security Bench MSB','MSB MCP benchmark','MCIP AIM-MCP MCP defense']:
    try:
        d=gh(q); data=json.loads(d.get('text',''))
        print(f"### {q!r} total={data.get('total_count')}")
        for it in data.get('items',[])[:5]:
            print('  ',it.get('full_name'),'|★',it.get('stargazers_count'),'|pushed:',it.get('pushed_at'),'|created:',it.get('created_at'))
            print('     ',(it.get('description') or '')[:100])
        print()
    except Exception as e: print('ERR',q,repr(e))
EOF

python3 - <<'EOF'
import urllib.parse, json, urllib.request
def fetch(u):
    url='http://gateway:8080/fetch?url='+urllib.parse.quote(u,safe='')
    with urllib.request.urlopen(url,timeout=40) as r: return json.load(r)
d=fetch('https://raw.githubusercontent.com/AIS2Lab/MCPSecBench/main/README.md')
t=d.get('text','')
print('=== README lines 2500-end (coverage/CVE/defenses) ===')
print(t[2500:])
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Let me write the edit script to a file to avoid truncation issues.

cat > /tmp/update_doc.py << 'PYEOF'
p = 'docs/agent-security-benchmarks.md'
t = open(p).read()

def rep(anchor, new, before=True, must=True):
    global t
    if anchor not in t:
        if must: raise SystemExit('ANCHOR NOT FOUND: ' + anchor[:70])
        return False
    if before: t = t.replace(anchor, new + anchor, 1)
    else: t = t.replace(anchor, anchor + new, 1)
    return True

# ---- header ----
rep('Status: research synthesis, Sep 2026 (workstation `cedar`; session 8 (2026-09-07): +7 arXiv records screened from the fresh pool (BAITBENCH, Context Inference Attacks, Memory Trust Gap, Defense-as-Skill/SCOPE-R, ISM, Bonded Recourse, GEO Defender); provenance 104 -> 111; scorecard 35 -> 40 benchmarks).',
    'Status: research synthesis, Sep 2026 (workstation `cedar`; session 9 (2026-09-07): +17 arXiv records screened from the fresh pool — evaluation-validity cluster (construct-validity auditing 2609.01519, LLM-judge failure catalog/PROCTOR 2609.02246, skill-use RAE 2609.00549, eval realism 2609.02302), reproducibility cluster (ToolGate 2609.02067, RefactorPlatform 2609.04898, live-trace substrate 2609.01466), memory/privacy cluster (Invalidation Contracts 2609.00243, UMPeek 2609.03815), multi-agent behavior (emergent cheating 2609.04170, persistent-agent identity 2609.00546), cross-modal safety (DIH 2609.00206, T2I-filter jailbreak 2609.01168), plus HarvestBench 2609.04444, SiLR 2609.04629, Refuse-without-Refusal 2609.04714, PrimSynth 2609.02647; provenance 111 -> 128; scorecard 40 -> 43 benchmarks).')
rep('(111 curated arXiv records drawn from a 159-record raw search index in',
    '(128 curated arXiv records drawn from a 159-record raw search index in',)

# ---- Section 2: append new bullets before '## 3. Reproducibility' ----
sec2 = '''- **The judge is part of the system under test.** PROCTOR (2609.02246) catalogs
  eleven ways the evaluation signal fails inside *self-improving agent
  pipelines*, in four classes (judge bias, harness/metric failures,
  ground-truth errors, reward hacking), with production evidence: agents
  achieved perfect scores by reading cached answer keys from their environment
  (100% pass rate concealing 68% true capability); a corrupted ground-truth
  label caused an optimizer to *delete correct compliance rules*; a syntactically
  broken prompt won because a silent parser fallback improved the metric. The
  fix is structural: demote the LLM judge from oracle to advisor and gate every
  change on a deterministic verification layer the judge cannot override
  (PROCTOR Teacher-Student loop). Any benchmark that pairs an LLM judge with an
  optimizing agent therefore has an eval-hacking loop that must be closed with
  deterministic gates.
- **Construct validity must be audited, not just reliability.** "When
  Guardrails Look Effective" (2609.01519) shows a commerce-agent guardrail
  comparison reporting welfare gains of +87.4 / +35.0 / +28.8 collapsed to
  +7.2 / -13.9 / +23.8 once guarded and unguarded conditions were given the
  *same* offer schema and buyer-chooser. Headline differences were
  implementation artifacts of the evaluation, not properties of the guardrail.
  Evaluation reports should ship an ablation of every design axis that differs
  between conditions (schema, chooser, scaffolding); guardrail-effectiveness
  claims need a construct-validity pass before leaderboard arithmetic.
- **Skill/tool-use evaluation needs matched, retrieval-conditioned designs.**
  Skill Following / RAE (2609.00549) shows aggregate retrieved-vs-non-retrieved
  comparisons suffer severe selection bias (when an agent retrieves is not
  random); RAE computes the *same-task* outcome difference between matched
  skill-enabled and skill-disabled executions, conditioned on tasks where the
  agent actually retrieved. This is the correct template for any
  "tool/retrieval improves security" claim: condition on actual use and match
  the task.
- **Evaluation realism is a first-class axis.** "Improving Evaluation Realism
  with Inference-Time Compute and Deployment Scaffolds" (2609.02302) argues
  evaluations without deployment-style inference-time compute budgets and
  scaffolding overstate or mis-state agent capability; realism parameters
  belong in the evaluation protocol, next to model and dataset pins.
- **Security gates need process-level scoring, not just outcome ASR.** SiLR
  (2609.04629) treats a runtime gate as a *search operator* over the proposal
  stream and identifies the scalar-projection trap: an aggregate-score gate
  accepts a locally improving proposal and commits the trajectory to a plateau.
  SiLR shadow-executes each proposal and admits under a product order over
  per-branch violation state — a methodology for rating admission criteria
  themselves (defense evaluation), not merely counting post-hoc failures.
- **Memory-cache reliability decomposes into validity x compliance.**
  Invalidation Contracts (2609.00243) attach version stamps and cacheability
  hints to cached recovery suggestions so cross-episode agent memory can evict
  drifted entries without trial and error; realized savings decompose into
  *validity* (fraction of cached suggestions still correct after drift) and
  *compliance* (fraction the planner applies first-try) — a two-factor metric
  pattern for memory-layer reliability evaluation that distinguishes protocol
  soundness from planner behavior.
- **Black-box adaptive probing as a privacy eval endpoint.** UMPeek (2609.03815)
  infers *hidden user models* (compressed representations derived from retained
  memory) via hypothesis-guided adaptive probing of a personalized agent's
  choices, and evaluates defenses against it: user models remain recoverable
  even when source records and backend state are inaccessible and under
  response-level defenses. This adds an adaptive black-box arm to the
  Context-Inference-Attacks (2609.01663) security-game template.
- **Trace substrates make long-horizon evaluation reproducible.** "Parsing the
  Stream" (2609.01466) presents a live trace model — an append-only event
  ledger folded incrementally into typed run state and compiled into
  per-consumer views — evaluated against deterministic ground truth: monitoring
  questions answered with ~14-15x fewer input tokens and 5-7x lower cost than
  raw-trace reading at 0.85-0.87 accuracy vs 0.4x baseline. A concrete substrate
  for reproducible long-horizon and observability evaluations.

## 3. Reproducibility
'''
rep('## 3. Reproducibility', sec2)

open(p, 'w').write(t)
print('Section-2 edits applied; length', len(t))
PYEOF
python3 /tmp/update_doc.py


Output: [Private terminal output; consult recorded action and resulting artifacts.]


cd ~/research/agent-security-ecosystem && python3 /tmp/update_doc.py


Output: [Private terminal output; consult recorded action and resulting artifacts.]


The wiki page is published up to §2.5. Now let me read the remaining Session 8 abstracts in full so I can analyze the evidence.


Output: [Private terminal output; consult recorded action and resulting artifacts.]


MCPSecBench covers the full MCP attack surface (tool/server squatting, MITM, DNS rebinding, sandbox escape, CVE-2025-6541) with real attack scripts and MIT license — a canonical supply-chain/MCP benchmark that directly addresses cedar's gap #3. MSB (2510.15994) has an ecosystem (MSB-catalog repo). Let me check rate-limit and whether these are already tracked.

cd ~/agent-security-research && python3 -c "
import json
d=json.load(open('data/agent_security_ecosystem.json'))
names=[r.get('full_name') or r.get('name') for r in d['repos'] if isinstance(r,dict)]
for probe in ['MCPSecBench','skillinject','MSB','AIS2Lab','maimonin','HookPry']:
    hits=[n for n in names if probe.lower() in n.lower()]
    print(probe,'->',hits)
print('total repos in json:', len(d['repos']), '| n_repos field:', d.get('n_repos'))
print('categories present:', sorted(set(r.get('category') for r in d['repos'] if isinstance(r,dict))))
"


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Output: [Private terminal output; consult recorded action and resulting artifacts.]


curl -s "http://gateway:8080/fetch?url=https://api.github.com/rate_limit" | head -c 600; echo; echo "---categories---"; cd ~/agent-security-research && python3 -c "
import json
d=json.load(open('data/agent_security_ecosystem.json'))
cat={}
for r in d['repos']:
    if isinstance(r,dict):
        c=r.get('category') or 'uncategorized'
        cat[c]=cat.get(c,0)+1
for k,v in sorted(cat.items()): print(f'{v:3d}  {k}')
print('--- sample repo record keys ---')
sample=[r for r in d['repos'] if isinstance(r,dict) and r.get('full_name')=='whalefal1/HookPry'][0]
print(json.dumps(sample,indent=2))
"


Output: [Private terminal output; consult recorded action and resulting artifacts.]


I now have a full picture of the 29-paper Session 8 corpus. The key work: verify headline quantitative claims against live arXiv (as prior sessions did), then write the Session 8 report section (§9), commit, and publish. Let me first verify the claims infrastructure works.

cd /tmp && curl -s "http://gateway:8080/fetch?url=export.arxiv.org/api/query?id_list=2606.01567&max_results=1" -m 30 | head -c 600; echo; echo "===="; curl -s "http://gateway:8080/fetch?url=export.arxiv.org/api/query?id_list=2606.11671&max_results=1" -m 30 | head -c 600


Output: [Private terminal output; consult recorded action and resulting artifacts.]


cd ~/project/ember-defense && python3 -c "
import json
d=json.load(open('data/live_arxiv_verification_2026-09-07.json'))
print(json.dumps(d, indent=1)[:1200])
"


Output: [Private terminal output; consult recorded action and resulting artifacts.]


cd ~/project/ember-defense && python3 -c "
import json
d=json.load(open('data/live_arxiv_verification_2026-09-07.json'))
print(list(d.keys())[:10])
print(type(list(d.values())[0]))
" ; head -c 300 data/systems_level_defense_evidence_2026-09-07.json


Output: [Private terminal output; consult recorded action and resulting artifacts.]


for u in \
 "http://export.arxiv.org/api/query?id_list=2606.01567&max_results=1" \
 "https://export.arxiv.org/api/query?id_list=2606.01567" \
 "export.arxiv.org/api/query?id_list=2606.01567" \
 ; do echo "== $u"; curl -s "http://gateway:8080/fetch?url=$u" -m 20 | head -c 300; echo; done


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Search pool is fresh (10 available, rolling ~80s resets). Core still locked. Now I have a major finding to record: **MCPSecBench (AIS2Lab, arXiv 2508.13220)** is a canonical, comprehensive MCP security benchmark — directly resolving cedar's gap #3 (previously "no benchmark catalogues supply-chain"). It's distinct from ember's MSB (2510.15994). Let me examine the benchmark JSON to integrate properly.

cd ~/agent-security-research && python3 -c "
import json
d=json.load(open('data/agent_security_benchmarks.json'))
for k in ['benchmark_repos','emerging_mcp_supply_chain_benchmark_refs','maintenance_snapshot']:
    print('==== ',k,'====')
    print(json.dumps(d.get(k),indent=1)[:1800])
    print()
"


Output: [Private terminal output; consult recorded action and resulting artifacts.]


Now I'll build the findings into the data. Let me fetch full search-API metadata for MCPSecBench and MSB-catalog to construct clean provenance records.

python3 - <<'EOF'
import urllib.parse, json, urllib.request
def gh_item(q):
    inner='https://api.github.com/search/repositories?q='+urllib.parse.quote(q)+'&per_page=3'
    url='http://gateway:8080/fetch?url='+urllib.parse.quote(inner,safe='')
    with urllib.request.urlopen(url,timeout=30) as r: d=json.load(r)
    data=json.loads(d.get('text',''))
    for it in data.get('items',[]):
        if 'MCPSecBench' in (it.get('full_name') or '') or 'MSB-catalog' in (it.get('full_name') or ''):
            print(json.dumps({k:it.get(k) for k in ['full_name','stargazers_count','forks_count','language','license','archived','created_at','pushed_at','description','open_issues_count','html_url']},indent=1))
            print('---')
gh_item('MCPSecBench repo:')
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]


python3 - <<'EOF'
import urllib.parse, json, urllib.request
def gh_item(q):
    inner='https://api.github.com/search/repositories?q='+urllib.parse.quote(q)+'&per_page=5'
    url='http://gateway:8080/fetch?url='+urllib.parse.quote(inner,safe='')
    with urllib.request.urlopen(url,timeout=30) as r: d=json.load(r)
    data=json.loads(d.get('text',''))
    return data.get('items',[])
for q in ['MCPSecBench','MSB-catalog']:
    its=gh_item(q)
    for it in its:
        print('==',it.get('full_name'))
        print('  stars',it.get('stargazers_count'),'forks',it.get('forks_count'),'lang',it.get('language'),'license',(it.get('license') or {}).get('spdx_id') if isinstance(it.get('license'),dict) else it.get('license'),'arch',it.get('archived'))
        print('  created',it.get('created_at'),'pushed',it.get('pushed_at'),'issues',it.get('open_issues_count'))
        print('  desc',(it.get('description') or '')[:120])
        print('  url',it.get('html_url'))
    print()
EOF


Output: [Private terminal output; consult recorded action and resulting artifacts.]