
Security News
Happy Birthday, Shai-Hulud
It has been one year since Shai-Hulud made its first appearance on npm.
spendshield
Advanced tools
Policy & authorization layer between AI agents and money - ALLOW / human-approval / DENY with structured reasons, daily budgets, merchant allowlists, policy lifecycle, tamper-evident audit. Python library + MCP server. 258 tests; 11,351 adversarial attempts -> 0 unintended ALLOW.
Payment networks move money. SpendShield decides whether it should move at all.
What it is — a channel-agnostic financial authorization runtime for AI agents.
What it does — evaluates every spending action against policy before money moves: ALLOW / APPROVAL (human) / DENY, with a structured reason an LLM can consume.
What makes it different — Policy · Approval · Security · Lifecycle · Explainability · Tamper-evident Audit. Not just can it pay — is it authorized to?
What it is NOT — not a wallet, not a payment rail, not a payment processor. Stripe, x402, wallets stay downstream; SpendShield never holds your money.
Agent wants to spend $75:
AGENT ──► SpendShield ──► Policy: max $50
│
▼
❌ DENY — transaction $75.00 exceeds the $50.00 limit
│
└── MAX_TRANSACTION_EXCEEDED · audited · policy v2.0.0
One YAML policy. One authorize() call. Every payment decided, explained, audited — with a reason an LLM can consume, and a tamper-evident audit chain.
▶ 30-second interactive demo — watch an AI agent get stopped.
A real Claude session asked to spend on McDonald's. It got its $25 order… then the gate said no to $75… then said no again when it tried to push $125 through a $100 daily budget. No retries, no splitting, no second path — the recording is unedited.
▶ Play it inline on the demo page · direct mp4
propose spend decide move money?
┌─────────────┐ authorize_payment ┌──────────────┐ ALLOW only ┌──────────────┐
│ AI Agent │ ──────────────────► │ SpendShield │ ─────────────► │ Payment rail │
│ (Claude, │ │ policy rules │ │ (Stripe, │
│ scripts) │ ◄────────────────── │ + human │ ◄───────────── │ x402, │
└─────────────┘ decision + reason │ approval │ never │ wallet) │
└──────────────┘ └──────────────┘
│
DENY / APPROVAL — money does NOT move
The agent holds no payment credentials and has no payment tool. authorize_payment is the only path money can take — the decision is ALLOW / APPROVAL / DENY, the reason is structured for an LLM, and every attempt lands in the audit chain.
┌────────────────────────────────┐
│ GOVERNANCE review · apply · version · rollback │
├────────────────────────────────┤
│ AUTHORIZATION policy · ALLOW / APPROVAL / DENY · reason codes │
├────────────────────────────────┤
│ SECURITY scan · fuzz · 8 invariants │
├────────────────────────────────┤
│ EVIDENCE explainability · tamper-evident audit chain │
└────────────────────────────────┘
↓ Stripe / x402 / Wallet (channel-agnostic)
Not a demo — a working baseline. Every result in the demo is real engine output.
No config. No YAML. No account.
pip install spendshield
from spendshield import SpendShield
shield = SpendShield(budget=100, max_amount=50, dry_run=False)
# Agent tries to spend $75 — policy limit is $50
result = shield.authorize("", 75, "amazon.com")
print(result.decision, "—", result.reason)
❌ DENY — transaction $75.00 exceeds the $50.00 limit
⚡ Try SpendShield in 60 Seconds — no API key required: ▶ Open in Google Colab
pip install spendshield
1. Write a policy (policy.yaml):
version: "2.0.0"
policy:
budget: { daily: 100, monthly: 1000 } # hard ceilings
transaction: { max: 50 } # per-payment cap
merchants:
allowed: [amazon.com, walmart.com] # exact domain match
blocked: [scam-vip.com]
approval: { over: 30, new_merchant: true, channel: tg } # human sign-off
agents:
shopping-agent:
transaction: { max: 50 }
2. Gate your payment function:
from spendshield import SpendShield
# dry_run=False: 真实执行。默认是安全干跑模式(只评估不执行) — 接入真实支付前用它调试
shield = SpendShield(dry_run=False)
shield.load_policy("policy.yaml")
@shield.protect("order", agent="shopping-agent")
def place_order(amount, to):
return call_real_api(amount, to) # denied / needs-approval raises before this runs
Or use the result object directly:
result = shield.authorize("shopping-agent", 2000, "scam-vip.com")
print(result.decision) # "DENY"
print(result.reason) # "merchant 'scam-vip.com' is blocked"
3. Watch it work (real engine output):
❌ DENY
Reason: merchant 'scam-vip.com' is blocked
- MERCHANT_BLOCKED: merchant 'scam-vip.com' is blocked (block)
Policy version: 2.0.0
pip install spendshield
spendshield-mcp --policy policy.yaml # stdio MCP server, 16 tools
Claude Code / any MCP host gets: spend_authorize, spend_approve, policy_sim, policy_apply, policy_create → policy_review → policy_lifecycle_apply, policy_rollback… An agent can ask "will this be denied?" before spending, and humans approve the big ones.
V1 prevent reckless spending ✅ → V2 Policy Engine ✅ → V2.2 Security Harness ✅
→ v0.7.2 Known-Good baseline ✅ → 0.8 Policy Lifecycle ✅ (CREATE→VALIDATE→SIMULATE→SCAN→REVIEW→APPLY→ROLLBACK)
→ Reality Test (real agents, real money, real attacks) ← we are here
→ V3 Intent Layer → V4 Risk → V5 IAM → V6 Payment Rails → 1.0
The metric that matters: real agents protected, real transactions gated, real dollars saved — not stars.
On August 9, 2026, my automation ran a test order. I sent dry: true expecting a price preview — the server only honored ?dry=1. 4 orders of ¥99 were charged for real. The money was gone. When AI starts spending real money, who puts a gate in front of it? I turned my scar into a library.
SpendShield guards real money. Try to break it.
The challenge: make an unauthorized transaction get ALLOW — bypass the policy, forge an approval, race the budget, replay a payment, tamper with history. Anything.
Rules:
dry_run=True / test keys. Never point attacks at real payment systems.Current status: 240 tests · 16 security suites · 11,351 adversarial authorization attempts · 0 unintended ALLOW · 0 crashes (audit) · 0 known escapes.
⚠️ Precision: this is evidence from the current test suite against the current implementation — reproducible verification, not a mathematical proof of security. New attacks are always possible; every valid finding becomes a permanent regression test (see SECURITY.md).
policy_apply / policy_review are host-level operationsSpendShield: the layer I wish I had before my AI spent my money.
60 seconds: ▶ Run the demo in Colab — no install
5 minutes:
pip install spendshield # v0.8.0
from spendshield import SpendShield
shield = SpendShield(budget=100, max_amount=50)
@shield.protect("order")
def place_order(amount, to): ...
That's it. If it ever lets an unauthorized payment through — break the gate and get credited.
FAQs
Policy & authorization layer between AI agents and money - ALLOW / human-approval / DENY with structured reasons, daily budgets, merchant allowlists, policy lifecycle, tamper-evident audit. Python library + MCP server. 258 tests; 11,351 adversarial attempts -> 0 unintended ALLOW.
The pypi package spendshield receives a total of 49 weekly downloads. As such, spendshield popularity was classified as not popular.
We found that spendshield demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
It has been one year since Shai-Hulud made its first appearance on npm.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.