osr-security-hardening
.claude/skills/osr-security-hardening/SKILL.md
Harden OpenSmartRoute deployments against prompt injection, gadget/steering attacks, PII leakage, runaway agents and untrusted tool arguments - InputGuard and GuardMiddleware, learned gadget detector, PII redaction, ResourceLimits for tools and agents, OriginPolicy for tool-parameter provenance, signed MCP manifests, secret loading and the safety-routing red-team suite. Use when reviewing security of a routing setup, handling PII or private data boundaries, or wiring MCP tools safely.
- Package
- .claude/skills/osr-security-hardening
- Compatibility
- OpenSmartRoute >= 0.4, Python >= 3.10
- License
- Apache-2.0
- Domains
- coding legal general
- Quality prior
- 0.85
- Tags
- opensmartroute security injection pii mcp
Install by copying .claude/skills/osr-security-hardening/ into the skills folder of your coding assistant. To load every package as a routing target: osr --skills .claude/skills route "..." --plan.
Threat model: the router sits between untrusted text (users, retrieved documents, tool output) and expensive or state-changing targets. Defend the input, the catalogue, and the execution budget.
1. Input guard (every deployment)#
from opensmartroute.security import InputGuard, GuardMiddleware
guard = InputGuard(max_chars=32_000, max_tokens_est=8_000, gadget_threshold=0.6,
reject_on_gadget=False, learned=False, strip_steering=True)
app = RouterBuilder(reg).with_defaults().with_middleware(GuardMiddleware(guard, redact=True)).build()
guard.inspect(text) -> GuardReport(ok, reasons, gadget_suspected, clean_text, score, steering_removed).GuardMiddlewareraisesSecurityErrorwhennot ok; withredact=TruePII is replaced before routing (original inrequest.context["_original_text"], map incontext["_pii_map"]).learned=TrueusesGadgetDetector.default(); train your own withosr train --gadget gadget.jsonandGadgetDetector.load.- Pure functions for ad-hoc checks:
injection_risk(text),inspect_injection(text),strip_steering(text),sanitize_for_prompt(text),shannon_entropy(text).
2. Data boundaries and PII in the catalogue#
- Targets declare
constraints.data_boundary: public|private|on_premandpii_allowed. Requests setRequestConstraints(data_boundary=..., contains_pii=...); thePIISignalalso detects PII. - Policy rejects mismatches (
DataBoundaryRule,piirule) - verify intrace.policy_rejections. - Pair with a rule:
when: {contains_pii: true} prefer: [llm-onprem] pin: true. - Tenant isolation:
constraints.tenantson targets +TenantMiddleware(require=True).
3. Budgets for tools, agents and workflows#
from opensmartroute.security import ResourceLimits, ResourceLimiter, ResourceLimitMiddleware, apply_limits
limiter = ResourceLimiter(ResourceLimits(max_steps=50, max_tool_calls=100, max_depth=4,
max_total_tokens=500_000, max_cost_usd=5.0, max_wall_s=900.0))
apply_limits(registry, limiter, kinds=("tool", "agent", "workflow")) # wraps handlers per task_id
builder.with_middleware(ResourceLimitMiddleware(limiter))
Exceeding a limit raises ResourceLimitExceeded (a SecurityError). Budgets key on
request.context["task_id"].
4. Tool-parameter provenance (indirect injection)#
from opensmartroute.security import OriginPolicy, OriginRule, mark_untrusted, apply_origin_policy
mark_untrusted(request, retrieved_text, source="retrieved") # tag text you did not author
policy = OriginPolicy(rules=[OriginRule(state_changing=["delete_*", "send_*"], allowed_origins=["user"])])
apply_origin_policy(registry, policy) # wraps tool handlers
A state-changing tool whose arguments were copied from untrusted text raises OriginViolation.
5. MCP catalogues#
tools_from_mcp(payload, max_description_risk=0.6, guard=InputGuard())drops tools whose descriptions look like steering ("ignore previous instructions ...").- Sign catalogues offline and verify at load:
osr mcp-manifest tools.json --key-env OSR_MCP_KEY --server name --out manifest.json; thentools_from_manifest(manifest, key, require_signature=True, max_age_s=86400).--algorithm ed25519needs thecryptoextra.
6. Secrets#
load_secret("OPENAI_API_KEY") reads the env var or the file named by OPENAI_API_KEY_FILE
(Kubernetes secret mounts). Never put secrets in targets.yaml; handlers read them at call time.
Telemetry never logs raw text.
7. Red-team suite#
osr -t targets.yaml safety [--learned-guard] [--json] or run_safety_suite(app.route) runs
SafetyCases (jailbreaks, PII exfiltration, boundary hops, cost bombs) and reports pass_rate and
by_category. Add it next to osr eval --min-accuracy in CI.
Review checklist#
-
GuardMiddlewarefirst in the chain,redact=Truewhen any target ispii_allowed: false. - Every private/on-prem target has
data_boundaryandregionsset; requests carry tenant. -
apply_limitson alltool/agent/workflowtargets;max_cost_usdmatches the tenant budget. - MCP tools loaded from a signed manifest;
max_description_riskleft at or below 0.6. - Secrets via
load_secret; audit sink enabled;osr safetypasses in CI.