Skip to content

Escape HTML in markdown report to prevent injection - #29

Open
elhoim wants to merge 1 commit into
MISP:mainfrom
elhoim:fix/20-markdown-html-injection
Open

Escape HTML in markdown report to prevent injection#29
elhoim wants to merge 1 commit into
MISP:mainfrom
elhoim:fix/20-markdown-html-injection

Conversation

@elhoim

@elhoim elhoim commented Aug 31, 2026

Copy link
Copy Markdown
Member

Finding 20 (Medium) — bin/cli.py:403-422,442-470

Problem

response_to_table escapes only newlines and pipes - <script> from a module response reaches the markdown report verbatim, and to_inline emits a literal
, implying HTML rendering. The summary/heading fields at :442-470 (input_value, module, reason) get NO escaping. Module output (WHOIS, passive DNS, sandbox) is attacker-influenced.

Fix

Finding 20 (markdown-html-injection, medium): format_markdown_output in bin/cli.py embedded attacker-influenced module response data (WHOIS, passive DNS, sandbox output) and summary/heading fields into the generated markdown report with only newline/pipe sanitization, no HTML escaping. Since to_inline emits a literal <br> (implying HTML rendering) and the report can be rendered as HTML, a module response containing <script> or other tags reached the output verbatim. Fix: added html.escape() calls for every scalar (keys and values) in scalar_to_text (used throughout format_nested_value, to_inline, and response_to_table's fallback scalar branch), for keys in response_to_table's dict branch, and for the summary/heading fields (input_value, explicit_type, selected_modules, module, attribute_type, status, reason, queried_at, cache), in addition to the existing pipe/newline handling. Escaping happens before the pipe/newline replace, which is order-independent since html.escape does not touch | or \n.

Verification

Reproduced against the unmodified code at 9b8c605, then re-checked after the change.

Before
python3 repro script calling format_markdown_output with input_value="<script>alert('input')</script>" and a record containing module="<script>alert(1)</script>" and response={"whois": "<img src=x onerror=alert(2)>", "nested": {"a": "<b>bold</b>"}}. Output before fix contained the raw tags verbatim, e.g.:
`- Input value: \`<script>alert('input')</script>\``
`### 1. Module \`<script>alert(1)</script>\` / Type \`domain\``
`| \`whois\` | <img src=x onerror=alert(2)> |`
After
Same script after the fix. Output now shows escaped entities:
`- Input value: \`&lt;script&gt;alert(&#x27;input&#x27;)&lt;/script&gt;\``
`### 1. Module \`&lt;script&gt;alert(1)&lt;/script&gt;\` / Type \`domain\``
`| \`whois\` | &lt;img src=x onerror=alert(2)&gt; |`
`| \`nested\` | a: &lt;b&gt;bold&lt;/b&gt; |`

python bin/cli.py --help exits 0 and the module still imports cleanly. Verification was performed offline against the pure functions — no running misp-modules instance is required.

Branched from 9b8c605. This PR addresses only this finding; the other findings from the same review are in separate PRs, so they will need rebasing against each other as they merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DYX4TKA5inzByJ4qGWKjqh

response_to_table and the summary/heading fields embedded attacker-influenced
module output (WHOIS, passive DNS, sandbox responses) into the generated
markdown report with only newline/pipe sanitization. Since the report is
often rendered as HTML (e.g. via a markdown viewer), a module response
containing <script> or other tags reached the output verbatim, and
to_inline's literal <br> implied HTML rendering was expected.

Fix: html.escape() every scalar (keys and values) before embedding, in
addition to the existing pipe/newline handling, in scalar_to_text,
response_to_table's key/value pairs, and the summary/heading fields
(input_value, explicit_type, selected_modules, module, attribute_type,
status, reason, queried_at, cache).
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