Skip to content

STRIDE

stride

STRIDE Threat Model Generator for AI agent systems.

Generates structured threat models using the STRIDE methodology (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege) tailored to AI agent replication and safety scenarios.

CLI usage::

python -m replication stride --component orchestrator
python -m replication stride --all
python -m replication stride --component worker --format json
python -m replication stride -o stride_report.html

Threat dataclass

A single STRIDE threat.

Source code in src/replication/stride.py
@dataclasses.dataclass
class Threat:
    """A single STRIDE threat."""
    category: str          # S, T, R, I, D, or E
    title: str
    description: str
    component: str
    severity: str          # Critical, High, Medium, Low
    likelihood: str        # High, Medium, Low
    mitigations: List[str]

    @property
    def risk_score(self) -> int:
        sev = {"Critical": 4, "High": 3, "Medium": 2, "Low": 1}
        lik = {"High": 3, "Medium": 2, "Low": 1}
        return sev.get(self.severity, 1) * lik.get(self.likelihood, 1)

    def to_dict(self) -> dict:
        d = dataclasses.asdict(self)
        d["risk_score"] = self.risk_score
        return d

get_threats(component: Optional[str] = None) -> List[Threat]

Return threats filtered by component, or all threats.

Source code in src/replication/stride.py
def get_threats(component: Optional[str] = None) -> List[Threat]:
    """Return threats filtered by component, or all threats."""
    if component:
        return list(_THREAT_LIBRARY.get(component, []))
    result = []
    for comp in COMPONENTS:
        result.extend(_THREAT_LIBRARY.get(comp, []))
    return result

summary_table(threats: List[Threat]) -> str

Plain-text summary table.

Source code in src/replication/stride.py
def summary_table(threats: List[Threat]) -> str:
    """Plain-text summary table."""
    lines = []
    lines.append(f"{'Cat':<4} {'Component':<20} {'Severity':<10} {'Risk':>5}  Title")
    lines.append("-" * 80)
    for t in sorted(threats, key=lambda x: -x.risk_score):
        lines.append(
            f"[{t.category}]  {t.component:<20} {t.severity:<10} {t.risk_score:>5}  {t.title}"
        )
    return "\n".join(lines)

risk_matrix(threats: List[Threat]) -> str

ASCII risk matrix (severity x likelihood).

Source code in src/replication/stride.py
def risk_matrix(threats: List[Threat]) -> str:
    """ASCII risk matrix (severity x likelihood)."""
    grid: Dict[tuple, int] = {}
    sevs = ["Critical", "High", "Medium", "Low"]
    liks = ["High", "Medium", "Low"]
    for t in threats:
        key = (t.severity, t.likelihood)
        grid[key] = grid.get(key, 0) + 1

    lines = ["\nRisk Matrix (count of threats):\n"]
    lines.append(f"{'Severity':<12} | {'High':>6} | {'Medium':>6} | {'Low':>6} |")
    lines.append("-" * 46)
    for s in sevs:
        h = grid.get((s, "High"), 0)
        m = grid.get((s, "Medium"), 0)
        lo = grid.get((s, "Low"), 0)
        lines.append(f"{s:<12} | {h:>6} | {m:>6} | {lo:>6} |")
    lines.append(f"\n{'':>14} {'<-- Likelihood -->'}")
    return "\n".join(lines)

generate_html(threats: List[Threat], title: str = 'STRIDE Threat Model') -> str

Generate an interactive HTML report.

Source code in src/replication/stride.py
def generate_html(threats: List[Threat], title: str = "STRIDE Threat Model") -> str:
    """Generate an interactive HTML report."""
    h = html.escape
    rows = ""
    for t in sorted(threats, key=lambda x: -x.risk_score):
        sev_color = {
            "Critical": "#dc3545", "High": "#fd7e14",
            "Medium": "#ffc107", "Low": "#28a745"
        }.get(t.severity, "#6c757d")
        mits_html = "".join(f"<li>{h(m)}</li>" for m in t.mitigations)
        rows += f"""<tr>
  <td><span class="badge" style="background:{sev_color}">{h(t.category)}</span></td>
  <td>{h(t.component)}</td>
  <td><strong>{h(t.title)}</strong><br><small>{h(t.description)}</small></td>
  <td><span class="badge" style="background:{sev_color}">{h(t.severity)}</span></td>
  <td>{h(t.likelihood)}</td>
  <td>{t.risk_score}</td>
  <td><ul>{mits_html}</ul></td>
</tr>"""

    # Category summary
    cat_counts: Dict[str, int] = {}
    for t in threats:
        cat_counts[t.category] = cat_counts.get(t.category, 0) + 1
    cat_summary = " | ".join(
        f"<strong>{STRIDE_FULL[c]}</strong>: {cat_counts.get(c, 0)}"
        for c in "STRIDE"
    )

    return f"""<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8">
<title>{h(title)}</title>
<style>
body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
       margin: 2em; background: #f8f9fa; color: #212529; }}
h1 {{ color: #343a40; }}
.summary {{ background: #fff; padding: 1em; border-radius: 8px;
            box-shadow: 0 1px 3px rgba(0,0,0,.1); margin-bottom: 1.5em; }}
table {{ width: 100%; border-collapse: collapse; background: #fff;
         box-shadow: 0 1px 3px rgba(0,0,0,.1); border-radius: 8px; overflow: hidden; }}
th {{ background: #343a40; color: #fff; padding: .75em; text-align: left; }}
td {{ padding: .75em; border-top: 1px solid #dee2e6; vertical-align: top; }}
tr:hover {{ background: #f1f3f5; }}
.badge {{ color: #fff; padding: 2px 8px; border-radius: 4px; font-size: .85em; }}
ul {{ margin: 0; padding-left: 1.2em; }}
li {{ font-size: .9em; }}
.filter {{ margin-bottom: 1em; }}
select {{ padding: .4em; border-radius: 4px; border: 1px solid #ced4da; }}
</style>
<script>
function filterTable() {{
  const comp = document.getElementById('fComp').value;
  const cat = document.getElementById('fCat').value;
  const sev = document.getElementById('fSev').value;
  document.querySelectorAll('tbody tr').forEach(r => {{
    const cells = r.cells;
    const show = (!comp || cells[1].textContent === comp)
              && (!cat || cells[0].textContent.trim() === cat)
              && (!sev || cells[3].textContent.trim() === sev);
    r.style.display = show ? '' : 'none';
  }});
}}
</script>
</head><body>
<h1>🛡️ {h(title)}</h1>
<div class="summary">
  <p><strong>{len(threats)}</strong> threats identified across
     <strong>{len(set(t.component for t in threats))}</strong> components</p>
  <p>{cat_summary}</p>
</div>
<div class="filter">
  <label>Component: <select id="fComp" onchange="filterTable()">
    <option value="">All</option>
    {"".join(f'<option>{h(c)}</option>' for c in sorted(set(t.component for t in threats)))}
  </select></label>
  <label> Category: <select id="fCat" onchange="filterTable()">
    <option value="">All</option>
    {"".join(f'<option value="{c}">{c}{STRIDE_FULL[c]}</option>' for c in "STRIDE")}
  </select></label>
  <label> Severity: <select id="fSev" onchange="filterTable()">
    <option value="">All</option>
    <option>Critical</option><option>High</option><option>Medium</option><option>Low</option>
  </select></label>
</div>
<table>
<thead><tr><th>Cat</th><th>Component</th><th>Threat</th><th>Severity</th><th>Likelihood</th><th>Risk</th><th>Mitigations</th></tr></thead>
<tbody>{rows}</tbody>
</table>
</body></html>"""