Skip to content

security: lock down pack --format html with strict CSP#2

Open
CryptoJones wants to merge 1 commit into
mainfrom
security/pack-html-csp
Open

security: lock down pack --format html with strict CSP#2
CryptoJones wants to merge 1 commit into
mainfrom
security/pack-html-csp

Conversation

@CryptoJones
Copy link
Copy Markdown
Owner

python-markdown (used by socrates pack --format html) has no safe
mode in v3+ — it passes raw <script>, <iframe>, inline event handlers,
etc. straight through. Any planning file containing such content (a
code-example in DOMAIN.md, an error log paste in RISKS.md) would
execute in any browser that opens the rendered .html bundle.

Bundle is meant to be (a) pasted into an AI chat — which strips
scripts anyway — or (b) previewed in a browser. It never needs
JavaScript itself. So the fix is a strict CSP meta tag:

default-src 'none'
script-src 'none' <- blocks <script> AND inline onclick=/onerror=
object-src 'none' <- blocks /
frame-src 'none' <- blocks phishing-redirect via planted <iframe>
base-uri 'none' <- blocks tag rewriting relative URLs
form-action 'none' <- blocks form-post exfil
style-src 'unsafe-inline' <- needed for fenced_code highlighting
img-src data: <- allow data-URI images, no remote refs

Defense in depth: no bleach dependency, no API change, no behavior
change for trusted markdown content.

Tests added:

  • test_pack_html_emits_strict_csp_meta: asserts every CSP directive
    the docstring promises is present.
  • test_pack_html_passes_script_tags_through_but_csp_blocks_them:
    proves CSP is the load-bearing defense by planting a <script> in
    DECISIONS.md and asserting it BOTH reaches the rendered HTML AND
    the CSP meta tag is present to neutralize it. If a future change
    silently strips scripts (less defense in depth + masking the real
    risk), this test trips.

149/149 tests pass; ruff + mypy clean.

Self-review caveats (be aware before merging):

  • CSP via <meta http-equiv> is weaker than via HTTP header — cannot set frame-ancestors or report-uri.
  • Tested by string-presence on the meta tag, NOT by actual browser rendering.
  • A bleach-based second layer would be defense-in-depth; deliberately not added to keep this dependency-free.

python-markdown (used by `socrates pack --format html`) has no safe
mode in v3+ — it passes raw <script>, <iframe>, inline event handlers,
etc. straight through. Any planning file containing such content (a
code-example in DOMAIN.md, an error log paste in RISKS.md) would
execute in any browser that opens the rendered .html bundle.

Bundle is meant to be (a) pasted into an AI chat — which strips
scripts anyway — or (b) previewed in a browser. It never needs
JavaScript itself. So the fix is a strict CSP meta tag:

  default-src 'none'
  script-src 'none'       <- blocks <script> AND inline onclick=/onerror=
  object-src 'none'       <- blocks <object>/<embed>
  frame-src 'none'        <- blocks phishing-redirect via planted <iframe>
  base-uri 'none'         <- blocks <base> tag rewriting relative URLs
  form-action 'none'      <- blocks form-post exfil
  style-src 'unsafe-inline' <- needed for fenced_code highlighting
  img-src data:           <- allow data-URI images, no remote refs

Defense in depth: no `bleach` dependency, no API change, no behavior
change for trusted markdown content.

Tests added:
- test_pack_html_emits_strict_csp_meta: asserts every CSP directive
  the docstring promises is present.
- test_pack_html_passes_script_tags_through_but_csp_blocks_them:
  proves CSP is the load-bearing defense by planting a <script> in
  DECISIONS.md and asserting it BOTH reaches the rendered HTML AND
  the CSP meta tag is present to neutralize it. If a future change
  silently strips scripts (less defense in depth + masking the real
  risk), this test trips.

149/149 tests pass; ruff + mypy clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant