Skip to content

Why Wardline

Most Python security tools focus on known vulnerabilities — CVEs in dependencies, hardcoded secrets, SQL injection patterns. These are important, but they miss a deeper problem: trust boundary violations that happen when unvalidated data reaches code that assumes its input is safe.

Wardline catches these violations statically, before your code ships.


The Problem

Consider this common pattern:

def handle_webhook(payload: dict) -> None:
    user_id = payload["user_id"]
    update_user_preferences(user_id, payload["preferences"])

def update_user_preferences(user_id: str, prefs: dict) -> None:
    # This function trusts its arguments — no validation here
    db.execute("UPDATE users SET prefs = ? WHERE id = ?", json.dumps(prefs), user_id)

The update_user_preferences function trusts its arguments. It assumes user_id is a valid ID and prefs is a safe dictionary. But handle_webhook passes raw external input directly to it — no validation, no type checking, no sanitisation.

This isn't a SQL injection (parameterised queries handle that). It's a trust boundary violation: external data reached internal code without crossing a validation boundary.

Code reviews catch some of these. But in a large codebase with many contributors, they slip through. The function that trusts its input was written months ago; the caller that violates that trust is written today.


What Existing Tools Don't Solve

Tool What It Catches What It Misses
Type checkers (mypy, pyright) Type mismatches Trust assumptions — str doesn't mean "validated string"
Linters (ruff, pylint) Style issues, some anti-patterns Semantic trust boundaries
SAST tools (bandit, semgrep) Known vulnerability patterns Application-specific trust models
Runtime validation (pydantic, attrs) Invalid data at runtime Violations that pass validation by accident
Property testing (hypothesis) Edge cases in tested code Untested code paths, integration points

These tools are valuable — use them. But none answers the question: "Does external data ever reach this function without validation?"


The Wardline Approach

Wardline models your codebase as a four-tier trust hierarchy:

Tier Name Meaning
1 INTEGRAL Audit-critical. Database writes, compliance logs, cryptographic operations.
2 ASSURED Validated internal data. Business logic operating on checked inputs.
3 GUARDED Shape-validated but not semantically verified. Parsed but not trusted.
4 EXTERNAL_RAW Untrusted external input. API payloads, file contents, user input.

Data flows freely downward (Tier 1 can call Tier 4 code). Flowing upward requires crossing a validation boundary — a function decorated with @validates_shape or @validates_semantic.

The scanner walks your AST, propagates taint through the call graph, and reports violations where high-tier code receives low-tier data without a boundary crossing.

from wardline.decorators import external_boundary, validates_shape, integrity_critical

@external_boundary
def handle_webhook(payload: dict) -> None:
    validated = parse_payload(payload)  # Crosses a boundary
    record_audit_event(validated)       # Now safe

@validates_shape
def parse_payload(raw: dict) -> AuditRecord:
    if "action" not in raw:
        raise ValueError("missing action")
    return AuditRecord(action=raw["action"])

@integrity_critical
def record_audit_event(record: AuditRecord) -> None:
    db.write_audit(record)

Three Enforcement Layers

Wardline doesn't just detect — it enforces at multiple layers:

1. Static Analysis (Primary)

The scanner catches violations without running your code. It's fast, runs in CI, and gates merges on a clean scan.

wardline scan src/ --manifest wardline.yaml

2. Runtime Enforcement (Defense in Depth)

Descriptor-based boundary checks catch what static analysis can't prove — dynamic dispatch, plugin surfaces, late-bound calls.

from wardline.runtime import enforce_boundary

@enforce_boundary(min_tier=2)
def business_logic(data: ValidatedRecord) -> None:
    ...

3. Governance Register (Audit Trail)

Exceptions aren't just suppressed — they're tracked in a control-law state machine with request → approval → expiry → retirement lifecycle. SIEM export, retention rules, and audit trails are built in.


When to Use Wardline

Wardline is designed for codebases where trust matters:

  • Financial services — Untrusted input reaching transaction processing
  • Healthcare — External data flowing to patient record systems
  • SaaS platforms — Multi-tenant boundaries, API integrations
  • Compliance-heavy environments — IRAP, SOC 2, PCI-DSS audit requirements

If your code handles data from external sources and some internal functions assume their input is safe, Wardline can prove (or disprove) that assumption.


When Not to Use Wardline

Wardline adds overhead. It's not the right tool for:

  • Prototypes and throwaway code — No boundaries to enforce yet
  • Pure data science / notebooks — Trust model doesn't apply
  • Codebases with no external input — Nothing to validate
  • Teams not ready for static analysis discipline — Adoption requires buy-in

Getting Started

Ready to try it?

For the systems thinking behind Wardline's design — why it targets information flows and paradigms rather than just parameters — see Systems Thinking & Leverage Points.

Get Started Read the Specification