Files
iimp0ster-detection-chokepo…/_includes/ttp-graph.html
imposterandClaude Opus 4.8 0c3709aa7e security: harden site supply chain, escaping, Actions, and governance
XSS:
- Escape `</` in all 5 jsonify-into-<script> data blobs so contributed
  YAML cannot break out of the script context (verified: JSON still
  parses, no </script breakout)
- Add `| escape` to contributor-controlled fields in chokepoint-card.html
  and ~71 value outputs in the chokepoint detail layout

Supply chain (SRI):
- Pin highlight.js, d3, and Chart.js CDN includes with sha384 integrity +
  crossorigin (hashes computed from the immutable versioned URLs)
- Document why cdn.tailwindcss.com cannot take SRI + the real fix

GitHub Actions:
- SHA-pin all 7 third-party actions to commit SHAs (version in comment)

Governance:
- SECURITY.md (private disclosure policy + scope: detection content is
  intentional, not a vuln)
- CODEOWNERS routing review to @iimp0ster
- Dependabot for github-actions / bundler / npm / pip

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 20:24:30 -06:00

70 lines
2.9 KiB
HTML

{% comment %}
_includes/ttp-graph.html
──────────────────────────────────────────────────────────────────────
D3 bipartite-column relationship graph for TTP Overlap sections.
Paired with _includes/ttp-vertical-diagram.html (Grid fallback).
ttp-graph.js reveals the toggle bar and defaults to Graph on desktop.
Requires: page.ttp_data_key, D3 v7 (loaded by attack-chain.html).
{% endcomment %}
{% assign ttp_data = site.data[page.ttp_data_key] %}
{% if ttp_data %}
<!-- Toggle bar - hidden by default; ttp-graph.js reveals it on successful boot -->
<div id="ttp-view-toggle" hidden aria-label="Switch between Graph and Grid views">
<button class="ttp-view-btn" data-view="graph" aria-pressed="false">Graph</button>
<button class="ttp-view-btn active" data-view="grid" aria-pressed="true">Grid</button>
</div>
<!-- Graph view - hidden by default; shown by JS on desktop after render -->
<div id="ttp-graph-view" hidden>
<!-- Controls: zoom + actor filter -->
<div class="ttp-graph-controls" role="group" aria-label="Graph controls">
<button class="ttp-graph-zoom-btn" id="ttp-gz-in" title="Zoom in" aria-label="Zoom in">+</button>
<button class="ttp-graph-zoom-btn" id="ttp-gz-out" title="Zoom out" aria-label="Zoom out">&minus;</button>
<button class="ttp-graph-zoom-btn" id="ttp-gz-reset" title="Reset zoom" aria-label="Reset zoom">Reset</button>
<span class="ttp-graph-zoom-sep" aria-hidden="true"></span>
{% for group in ttp_data.groups %}
<button class="ttp-graph-actor-btn"
data-actor="{{ group.id }}"
style="--actor-color: {{ group.color }}"
aria-pressed="false"
type="button">
<span class="dot" style="background: {{ group.color }}" aria-hidden="true"></span>
{{ group.name }}
</button>
{% endfor %}
<button class="ttp-graph-clear-btn" id="ttp-graph-clear" type="button">Clear</button>
</div>
<!-- SVG container + tooltip (tooltip positioned relative to wrap) -->
<div id="ttp-graph-svg-wrap" style="position: relative;">
<div id="ttp-graph-svg"></div>
<div class="ttp-graph-tooltip" role="tooltip" aria-hidden="true">
<div class="tt-title"></div>
<div class="tt-meta"></div>
</div>
</div>
<!-- Legend -->
<div class="ttp-graph-legend" aria-hidden="true">
<span><span class="ttp-graph-legend-ring"></span>Universal (all actors)</span>
<span>Actors left &rarr; Techniques by phase</span>
<span>Click actor or button to filter (union)</span>
</div>
</div><!-- /#ttp-graph-view -->
<!-- Data injection: raw groups + phases arrays; ttp-graph.js builds nodes/links -->
<script>
window.__TTP_GRAPH_DATA = {
groups: {{ ttp_data.groups | jsonify | replace: '</', '<\/' }},
phases: {{ ttp_data.phases | jsonify | replace: '</', '<\/' }}
};
</script>
{% endif %}