Agent Policy Linter

Agent policy linter illustration

Overview

The Agent Policy Linter is a static analysis tool that validates policy and guardrail definitions for agentic AI systems before they reach production. In environments where autonomous agents make decisions, invoke external tools, and handle sensitive data, a misconfigured policy file can silently introduce catastrophic security gaps. An agent granted unrestricted database access, missing an escalation rule for financial transactions, or lacking a timeout constraint on recursive tool calls represents a real and immediate threat surface.

The linter catches these issues early. It parses your policy definitions — written in YAML or JSON — against a strict schema, applies a library of security rules, and produces actionable output identifying errors, warnings, and informational notes. Think of it as ESLint for your agent guardrails: it enforces consistency, prevents common misconfigurations, and integrates directly into your CI/CD pipeline so that no unvalidated policy ever reaches a deployed agent.

A policy that is syntactically valid but semantically permissive is worse than no policy at all — it creates a false sense of security. The Agent Policy Linter validates both structure and intent.

The tool is designed for security engineers, platform teams, and DevOps practitioners operating in regulated Australian industries including finance, healthcare, and government, where compliance frameworks such as the Australian Privacy Principles (APPs) and the AI Ethics Framework demand demonstrable guardrails on automated decision-making systems.

What It Checks

The linter performs five categories of validation, each targeting a distinct attack surface in agentic AI deployments.

Permission Boundaries

Every agent must operate within a clearly defined permission envelope. The linter verifies that max_permissions fields are present and do not exceed organisational thresholds. It flags agents configured with admin or root level access unless an explicit override with justification is provided. It also checks for permission inheritance conflicts where a child agent could inadvertently escalate beyond its parent's boundary.

Tool Access Rules

Agents that invoke external tools — APIs, databases, file systems, or other agents — must declare which tools they are permitted to use. The linter validates the allowed_tools list against a known tool registry and flags any wildcard entries ("*") as critical errors. It also cross-references tool permissions with the agent's stated purpose to detect over-provisioning.

Escalation Paths

When an agent encounters a situation beyond its authority, it must know how to escalate. The linter checks that escalation_rules are defined, that each rule specifies a valid target (human operator, supervisor agent, or fallback workflow), and that circular escalation chains do not exist. Missing escalation paths for high-risk actions are flagged as errors.

Data Handling Constraints

The data_classification field determines what categories of data an agent may process. The linter validates that agents handling protected or highly_protected data have corresponding encryption, logging, and retention policies configured. It also checks that data classification levels are consistent across agent chains — a downstream agent should never receive data above its clearance.

Timeout and Retry Limits

Unbounded retries and missing timeouts are a common source of runaway agent behaviour. The linter enforces that every tool invocation and external call has explicit timeout_seconds and max_retries values. It warns when timeout values exceed sensible defaults (e.g., 300 seconds for a synchronous API call) and errors when retries are set to unlimited.

Policy Schema

Agent policies are defined in YAML (recommended) or JSON. The schema below represents a complete policy definition for a single agent. Each field is validated for type, range, and cross-field consistency.

# agent-policy.yaml
apiVersion: securight/v1
kind: AgentPolicy
metadata:
  name: invoice-processing-agent
  environment: production
  owner: finance-platform-team
  last_reviewed: "2026-02-15"

spec:
  max_permissions: read_write
  data_classification: protected

  allowed_tools:
    - name: supabase_query
      permissions: [read]
      timeout_seconds: 30
      max_retries: 3
    - name: email_send
      permissions: [execute]
      timeout_seconds: 15
      max_retries: 1
    - name: pdf_generator
      permissions: [execute]
      timeout_seconds: 60
      max_retries: 2

  denied_tools:
    - name: shell_exec
      reason: "No shell access permitted for finance agents"
    - name: admin_api
      reason: "Administrative operations require human operator"

  escalation_rules:
    - trigger: amount_exceeds_threshold
      condition: "transaction.amount > 10000"
      action: require_human_approval
      target: finance-approvers@securight.au
      timeout_seconds: 3600
    - trigger: classification_mismatch
      condition: "data.classification > agent.clearance"
      action: reject_and_log
      target: security-alerts

  human_approval_required:
    - action: delete_record
      scope: all
    - action: modify_permissions
      scope: all
    - action: send_external_email
      scope: "recipient.domain != 'securight.au'"

  guardrails:
    max_actions_per_session: 50
    max_cost_per_session_aud: 25.00
    require_audit_log: true
    pii_detection: enabled
    output_filtering: enabled
Schema versioning matters. The apiVersion field ensures backward compatibility. The linter validates against the schema version declared in your policy file, so older policies continue to pass validation while new policies benefit from stricter checks introduced in later schema versions.

Example Rules

The following examples demonstrate common policy configurations and their validation outcomes.

Rule 1: No Wildcard Tool Access

Granting an agent access to all tools defeats the purpose of a policy. The linter rejects wildcard entries.

Invalid configuration:

allowed_tools:
  - name: "*"
    permissions: [read, write, execute]

Valid configuration:

allowed_tools:
  - name: supabase_query
    permissions: [read]
    timeout_seconds: 30
    max_retries: 3
  - name: document_store
    permissions: [read, write]
    timeout_seconds: 45
    max_retries: 2

The linter produces a critical error for wildcard entries: E001: Wildcard tool access is prohibited. Explicitly list each permitted tool.

Rule 2: Escalation Required for High-Risk Actions

Any policy that includes high-risk actions (data deletion, permission changes, financial transactions above a threshold) must define corresponding escalation rules.

Invalid configuration:

spec:
  allowed_tools:
    - name: database_admin
      permissions: [read, write, delete]
  escalation_rules: []

Valid configuration:

spec:
  allowed_tools:
    - name: database_admin
      permissions: [read, write, delete]
  escalation_rules:
    - trigger: delete_operation
      condition: "operation.type == 'delete'"
      action: require_human_approval
      target: dba-team@securight.au
      timeout_seconds: 1800

Rule 3: Timeouts Must Be Bounded

Every tool invocation requires a finite timeout. The linter rejects missing or zero-value timeouts and warns on excessively high values.

Invalid configuration:

allowed_tools:
  - name: long_running_analysis
    permissions: [execute]
    timeout_seconds: 0
    max_retries: -1

Valid configuration:

allowed_tools:
  - name: long_running_analysis
    permissions: [execute]
    timeout_seconds: 120
    max_retries: 3

Rule 4: Data Classification Consistency

An agent's data classification level must be compatible with the tools it accesses. A public classification agent must not access tools that return protected data.

Invalid configuration:

spec:
  data_classification: public
  allowed_tools:
    - name: patient_records_api
      permissions: [read]
      data_classification: highly_protected

Valid configuration:

spec:
  data_classification: highly_protected
  allowed_tools:
    - name: patient_records_api
      permissions: [read]
      data_classification: highly_protected

Usage Guide

CLI Usage

The simplest way to run the linter is against a single policy file from the command line.

# Validate a single policy file
securight lint policy.yaml

# Validate all policies in a directory
securight lint ./policies/

# Validate with strict mode (warnings treated as errors)
securight lint --strict policy.yaml

# Output results as JSON for programmatic consumption
securight lint --format json policy.yaml

# Validate against a specific schema version
securight lint --schema-version v1.2 policy.yaml

# Dry-run a policy against a live agent configuration
securight lint --dry-run --agent invoice-processor policy.yaml

CI/CD Integration

The linter is designed to run as a step in your deployment pipeline. Below is an example GitHub Actions workflow.

# .github/workflows/policy-lint.yml
name: Agent Policy Lint
on:
  pull_request:
    paths:
      - 'policies/**'
      - 'agents/**/policy.yaml'

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install SecuRight CLI
        run: |
          curl -fsSL https://cli.securight.au/install.sh | bash
          securight --version

      - name: Lint agent policies
        run: |
          securight lint --strict --format github-actions ./policies/

      - name: Upload lint report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: policy-lint-report
          path: .securight/lint-report.json

Pre-Deployment Checks

For teams deploying agents through infrastructure-as-code, the linter can be invoked as a pre-deployment validation step.

# In a deployment script
echo "Validating agent policies..."
securight lint --strict --fail-on-warning ./deploy/policies/

if [ $? -ne 0 ]; then
  echo "Policy validation failed. Deployment aborted."
  exit 1
fi

echo "Policies validated. Proceeding with deployment."
securight deploy --config ./deploy/agent-config.yaml

Sample Output

When the linter identifies issues, it produces structured output categorised by severity. The following is an example run against a policy file containing several misconfigurations.

$ securight lint --strict ./policies/data-pipeline-agent.yaml

SecuRight Agent Policy Linter v2.4.1
Validating: ./policies/data-pipeline-agent.yaml
Schema version: securight/v1

 ERRORS (2)

  E001  line 14   Wildcard tool access is prohibited.
                   allowed_tools[0].name is set to "*".
                   Explicitly list each permitted tool with scoped permissions.

  E007  line 31   Missing escalation rule for high-risk action.
                   Tool "database_admin" has [delete] permission but no
                   corresponding escalation_rules entry exists.
                   Add an escalation rule with trigger for delete operations.

 WARNINGS (3)

  W002  line 22   Timeout exceeds recommended maximum.
                   allowed_tools[2].timeout_seconds is 600 (max recommended: 300).
                   Consider breaking long operations into smaller tasks.

  W005  line 8    Policy review overdue.
                   metadata.last_reviewed is "2025-09-10" (178 days ago).
                   Policies should be reviewed at least every 90 days.

  W011  line 38   Human approval scope is broad.
                   human_approval_required[0].scope is "all".
                   Consider narrowing scope to reduce operator fatigue.

 INFO (1)

  I003  line 4    Environment set to "staging".
                   Staging policies are not enforced in production deployments.

Summary: 2 errors, 3 warnings, 1 info
Status: FAILED (--strict mode: warnings treated as errors)

Run `securight lint --fix ./policies/data-pipeline-agent.yaml` for auto-fix suggestions.
Exit codes matter for automation. The linter returns exit code 0 for pass, 1 for errors, and 2 for warnings when --strict is enabled. Use these codes in your pipeline logic to gate deployments appropriately.

Integration Patterns

The Agent Policy Linter fits into several points in a secure development lifecycle. The following patterns represent recommended integration strategies.

Git Hooks

A pre-commit hook ensures that no invalid policy is committed to the repository. This provides the fastest feedback loop for developers.

# .git/hooks/pre-commit
#!/bin/sh
POLICY_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(yaml|yml|json)$' | grep -i 'policy')

if [ -n "$POLICY_FILES" ]; then
  echo "Linting modified agent policies..."
  echo "$POLICY_FILES" | xargs securight lint --strict

  if [ $? -ne 0 ]; then
    echo "Commit rejected: policy lint errors found."
    echo "Fix the issues above or use --no-verify to bypass (not recommended)."
    exit 1
  fi
fi

Pull Request Checks

Integrating the linter as a required PR check ensures peer review is informed by automated validation. The --format github-actions flag produces inline annotations directly in the PR diff, making issues visible at the exact line where they occur. Teams using Bitbucket or GitLab can use --format codeclimate for equivalent functionality.

Deployment Pipeline Gates

The strongest integration point is as a mandatory gate in your deployment pipeline. Place the linter after policy files are resolved (environment variables substituted, templates rendered) but before any agent is deployed or updated. This catches issues that may not be visible in the raw template — for example, an environment variable that resolves to a wildcard or an overly permissive default.

# Terraform-style deployment with policy gate
resource "securight_agent" "invoice_processor" {
  name   = "invoice-processing-agent"
  policy = file("./policies/invoice-processor.yaml")

  lifecycle {
    precondition {
      condition     = securight_policy_lint.result.status == "passed"
      error_message = "Agent policy failed lint validation."
    }
  }
}

Continuous Monitoring

Policies can drift from their intended state. Schedule the linter to run periodically against deployed agent configurations, not just at commit time. A weekly cron job that pulls active policies from your agent runtime and validates them against current schema versions catches staleness issues (like the W005 review-overdue warning) and ensures ongoing compliance with evolving organisational standards.

Policy validation is not a one-time gate. It is a continuous discipline. The best security teams lint at commit, at deploy, and on a schedule — because the threat landscape changes even when your code does not.

Back to Resources