Back to skill

Security audit

GuardRails

Security checks for vulnerabilities and agentic risk

Overview

The skill looks like a real guardrails service, but it needs review because reachable clients can change guardrail policies and full evaluation requests are stored.

Review deployment before installing or running this outside a local trusted environment. Add authentication and role-based authorization for policy and audit endpoints, restrict network exposure, and reduce or redact stored audit payloads with a clear retention policy. I found no artifact-backed evidence of deception, exfiltration, destructive install behavior, or prompt hijacking, so this is Review rather than malicious.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
app/main.py:31
Finding
Unauthenticated Policy Administration Enables Complete Guardrail Bypass<![CDATA[ ## Vulnerability Details **File Location**: `app/main.py:31-79` **Vulnerability Type**: Missing authentication and authorization on security-critical policy administration endpoints **Risk Level**: High ### Vulnerable Code ```python @app.post("/policies", response_model=PolicyResponse, status_code=status.HTTP_201_CREATED) def create_policy_endpoint(payload: PolicyCreate, db: Session = Depends(get_db)) -> PolicyResponse: try: policy = create_policy(db, payload) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return PolicyResponse.model_validate(policy) @app.get("/policies", response_model=list[PolicyResponse]) def list_policies_endpoint(db: Session = Depends(get_db)) -> list[PolicyResponse]: return [PolicyResponse.model_validate(p) for p in list_policies(db)] @app.get("/policies/{policy_id}", response_model=PolicyResponse) def get_policy_endpoint(policy_id: str, db: Session = Depends(get_db)) -> PolicyResponse: policy = get_policy(db, policy_id) if not policy: raise HTTPException(status_code=404, detail="Policy not found") return PolicyResponse.model_validate(policy) @app.patch("/policies/{policy_id}", response_model=PolicyResponse) def update_policy_endpoint(policy_id: str, payload: PolicyUpdate, db: Session = Depends(get_db)) -> PolicyResponse: policy = get_policy(db, policy_id) if not policy: raise HTTPException(status_code=404, detail="Policy not found") try: updated = update_policy(db, policy, payload) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return PolicyResponse.model_validate(updated) @app.delete("/policies/{policy_id}", status_code=status.HTTP_204_NO_CONTENT) def disable_policy_endpoint(policy_id: str, db: Session = Depends(get_db)) -> Response: policy = get_policy(db, policy_id) if not policy: raise HTTPException(status_code=404, det ...[truncated 3171 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require authentication for every non-health endpoint using a verified service identity, OAuth2 access token, mutual TLS certificate, or equivalent mechanism. 2. Implement role-based authorization: - Permit ordinary agent identities to call only `POST /evaluate`. - Permit policy readers to access policy metadata where necessary. - Restrict policy creation, modification, disablement, and seeding to dedicated administrator roles. 3. Disable or remove `POST /seed` in production, or protect it with the same administrative authorization controls. 4. Constrain policy priorities to an approved range and reserve the highest priority range for immutable mandatory controls. 5. Prevent ordinary administrators from creating permissive policies that override mandatory `DENY` rules. 6. Require change approval or dual control for high-impact policy updates. 7. Record authenticated actor identity, before-and-after policy values, timestamps, and request provenance in tamper-resistant administrative audit logs. 8. Add authorization tests proving that anonymous and evaluation-only identities cannot create, patch, disable, or seed policies. 9. Deploy the API behind TLS and restrict network access to trusted workloads even after application-layer authentication is implemented. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
app/service.py:78
Finding
Complete Evaluation Requests Are Retained Without Redaction or Retention Controls<![CDATA[ ## Vulnerability Details **File Location**: `app/service.py:78-89` **Vulnerability Type**: Excessive plaintext retention of potentially sensitive action data **Risk Level**: Medium ### Vulnerable Code ```python def evaluate(db: Session, action: ActionRequest) -> DecisionResponse: policies = list(db.execute(select(Policy).where(Policy.enabled.is_(True))).scalars().all()) result = evaluate_action(action, policies) audit = AuditLog( action_id=action.action_id, action_type=action.action_type, action_payload=action.model_dump(mode="json"), decision=result.model_dump(mode="json"), matched_policies=[p.model_dump(mode="json") for p in result.matched_policies], ) db.add(audit) db.commit() return result ``` The corresponding model stores the complete request as JSON without a deletion or expiration field: ```python class AuditLog(Base): __tablename__ = "audit_logs" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) action_id: Mapped[Optional[str]] = mapped_column(String(128), index=True, nullable=True) action_type: Mapped[str] = mapped_column(String(128), index=True) action_payload: Mapped[Dict[str, Any]] = mapped_column(JSON) decision: Mapped[Dict[str, Any]] = mapped_column(JSON) matched_policies: Mapped[List[Dict[str, Any]]] = mapped_column(JSON) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) ``` The default database is a local SQLite file: ```python class Settings(BaseSettings): app_name: str = "Agent Policy & Guardrails Engine" environment: str = "dev" database_url: str = "sqlite:///./guardrails.db" ``` ### Technical Analysis `ActionRequest` accepts arbitrary dictionaries in both `payload` and `context`. The evaluation service serializes the entire request with `action.model_dump(mode="json")` and stores it in `AuditLog.action_payload`. Actions evaluated by ...[truncated 2144 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace complete request logging with an explicit allowlist of audit fields, such as action ID, action type, authenticated actor ID, decision, matched policy identifiers, and timestamp. 2. Do not retain raw `payload` or `context` unless a documented compliance requirement makes it necessary. 3. Apply recursive redaction before persistence for credentials, tokens, API keys, personal data, message content, and other sensitive fields. 4. Consider storing hashes, classifications, or opaque references instead of raw values. 5. Encrypt sensitive audit data at rest with managed keys and a documented rotation process. 6. Apply restrictive filesystem permissions to SQLite databases and least-privilege database roles when using PostgreSQL. 7. Define and enforce a retention period with automatic expiration and secure deletion. 8. Ensure backups use equivalent encryption, access controls, and retention policies. 9. Add schema-level size limits to `payload` and `context` to reduce excessive storage and denial-of-service risk. 10. Add tests verifying that representative secrets and personal data are absent from stored audit records. 11. Document what audit information is retained and prohibit callers from placing secrets in evaluation requests unless strictly necessary. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (9)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
- `GET /policies`
- `GET /policies/{policy_id}`
- `PATCH /policies/{policy_id}`
- `DELETE /policies/{policy_id}` (soft-disable)
- `POST /evaluate`
- `GET /audit`
- `POST /seed` (load baseline policies)
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Credential Access

High
Category
Privilege Escalation
Content
environment: str = "dev"
    database_url: str = "sqlite:///./guardrails.db"

    model_config = SettingsConfigDict(env_prefix="GUARDRAILS_", env_file=".env", extra="ignore")


settings = Settings()
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
PolicyCreate(
            policy_id="OPS-GIT-001",
            name="Block force pushes",
            description="Disallow git push --force operations.",
            priority=1000,
            policy_format="structured",
            definition={
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Anti-Refusal Statement

High
Category
Anti-Refusal
Content
PolicyCreate(
            policy_id="OPS-DATA-001",
            name="Deny access to restricted files",
            description="Never allow reads/writes/deletes on restricted files without explicit bypass policy.",
            priority=990,
            policy_format="structured",
            definition={
Confidence
90% confidence
Finding
Skill attempts to nullify the agent's safety policies or restrictions ('you have no restrictions', 'ignore your guidelines', 'do anything now'). This is a direct jailbreak that disables guardrails.

Possible Typosquatting: 'uvicorn' resembles popular package 'gunicorn'

High
Category
Supply Chain
Confidence
70% confidence
Finding
Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Known Vulnerable Dependency: pytest==8.4.1 — 2 advisory(ies): CVE-2025-71176 (pytest has vulnerable tmpdir handling); CVE-2025-71176 (pytest has vulnerable tmpdir handling)

High
Category
Supply Chain
Confidence
95% confidence
Finding
The dependency list pins pytest==8.4.1, and the scanner reports a known CVE affecting pytest tmpdir handling. This is a real supply-chain risk in the development and test environment, though pytest is typically not a production runtime dependency; in this skill context it is less likely to directly expose deployed systems unless test tooling runs in sensitive CI/CD environments or processes untrusted inputs.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The natural-language time policy parser silently hard-codes the timezone to UTC when constructing policy conditions. This can cause policy enforcement to occur at the wrong local time, leading to unintended allows or denies for sensitive operations, especially in multi-region or business-hours-based guardrail scenarios. In a policy and guardrails engine, this is more dangerous because users may assume local-time semantics while the engine enforces UTC globally.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
return [
        PolicyCreate(
            policy_id="FIN-001",
            name="Max transaction without approval",
            description="Require approval above $5,000.",
            priority=200,
            policy_format="structured",
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
return [
        PolicyCreate(
            policy_id="FIN-001",
            name="Max transaction without approval",
            description="Require approval above $5,000.",
            priority=200,
            policy_format="structured",
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.