T09 · Insecure Skill Coding Practices
- Location
generate-book-article.py:121- Finding
Unescaped JSON Content Permits Stored HTML and JavaScript Injection
- Content
View full analysis
str: """为一条金句生成独立 HTML 块。""" text = gq.get('text', '') source = gq.get('source', '') if source: return GOLD_QUOTE_BLOCK_TEMPLATE.replace('{TEXT}', text).replace('{SOURCE}', source) else: return GOLD_QUOTE_BLOCK_NO_SOURCE_TEMPLATE.replace('{TEXT}', text) ``` ```python title = data.get('title', '') if data.get('title_line2'): line2 = data['title_line2'] line2 = (line2 .replace('{count}', str(total_cards)) .replace('{all_quotes}', str(total_all_quotes)) .replace('{quotes}', str(total_quotes))) title += '
' + line2 html = html.replace('{{TITLE}}', title) html = html.replace('{{SUBTITLE}}', data.get('subtitle', '')) intro = data.get('intro_paragraphs', []) html = html.replace('{{INTRO_PARAGRAPH_1}}', intro[0] if len(intro) > 0 else '') html = html.replace('{{INTRO_PARAGRAPH_2}}', intro[1] if len(intro) > 1 else '') ``` ```python html = f'\n\n\n\n\n{data.get("title", "")}\n\n\n{html}\n\n' ``` ### Technical Analysis The generator treats JSON values as trusted HTML and inserts them directly into HTML templates through string replacement and f-string interpolation. It does not apply HTML escaping, contextual output encoding, or markup sanitization. The affected data includes card numbers, titles, descriptions, quote text, quote sources, article tit ...[truncated 2766 chars]- Remediation
View remediation
str: return escape(str(value), quote=True) ``` 2. Escape values before inserting them into text, attribute, or `` contexts: ```python safe_text = escape_text(q.get('text', '')) safe_index = escape_text(q.get('index', f'{j + 1:02d}')) quote_parts.append( QUOTE_ITEM_TEMPLATE .replace('{INDEX}', safe_index) .replace('{TEXT}', safe_text) ) ``` 3. Do not preserve arbitrary HTML merely to support line breaks. Instead, escape the entire value and then convert only an explicitly supported representation: ```python def escape_with_breaks(value) -> str: safe = escape(str(value), quote=True) return safe.replace('\r\n', '\n').replace('\r', '\n').replace('\n', '<br>') ``` If backward compatibility with literal `<br>` is required, normalize and allow only exact `<br>`, `<br/>`, or `<br />` tokens after escaping. Do not use a broad tag-removal regular expression as a sanitizer. 4. Use a template engine with automatic escaping enabled rather than constructing HTML through unrestricted `str.replace()` operations. Treat any deliberately safe HTML fragment as a separate, explicitly typed value. 5. Add strict schema validation: - Require expected types for all fields. - Reject objects or arrays where strings are expected. - Validate `after_card` as a bounded non-negative integer. - Impose reasonable length and collection-size limits. - Reject unsupported fields or markup where practical. 6. Add regression tests covering payloads in every interpolated field, including: ```htmllink ``` Tests should confirm that these values appear only as inert text in the generated document while supported line breaks continue to render correctly. 7. Consider applying a res ...[truncated 157 chars]
