T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/main.py:612
- Finding
- Unescaped Sample Identifiers Enable Stored HTML Injection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:612-617` **Related Input Source**: `scripts/main.py:650-654` **Vulnerability Type**: Stored HTML injection through unescaped input-derived content **Risk Level**: Medium ### Complete Code Snippet Input-derived sample identifiers are loaded directly from the first column of the supplied TSV file: ```python def load_otu_table(path: str) -> pd.DataFrame: """Load OTU table""" df = pd.read_csv(path, sep='\t', index_col=0, comment='#') # Transpose so that samples are rows and OTUs are columns if df.shape[0] > df.shape[1]: df = df.T return df ``` Those identifiers are subsequently inserted into the generated HTML without output encoding: ```python for sample, coords in pcoa['samples'].items(): html_content += f""" <tr> <td>{sample}</td> <td>{coords['PC1']:.3f}</td> <td>{coords['PC2']:.3f}</td> <td>{coords['PC3']:.3f}</td> </tr> """ ``` ### Technical Analysis The sample identifier is controlled by the contents of the user-supplied OTU/ASV table. It propagates from the DataFrame index into an HTML f-string without contextual escaping or sanitization. HTML generation must encode untrusted text before placing it into an HTML document. Because the value is inserted directly between `<td>` tags, an identifier containing HTML elements or event handlers is interpreted as markup rather than displayed as plain text. For example, a sample identifier could contain: ```html <img src=x onerror=alert(document.domain)> ``` The generated report would preserve that value as an active HTML element. When the report is opened in a browser, its event handler can execute in the report's browser context. ### Attack Path 1. An attacker creates or modifies an OTU/ASV TSV file. 2. The attacker places an HTML or JavaScript payload in a sample identifier. 3. A user runs the tool with the mali ...[truncated 1097 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Encode every input-derived value before inserting it into HTML. At minimum, apply `html.escape()` with quote escaping: ```python import html for sample, coords in pcoa['samples'].items(): safe_sample = html.escape(str(sample), quote=True) html_content += f""" <tr> <td>{safe_sample}</td> <td>{coords['PC1']:.3f}</td> <td>{coords['PC2']:.3f}</td> <td>{coords['PC3']:.3f}</td> </tr> """ ``` A stronger long-term approach is to use an HTML template engine with automatic escaping enabled. All values derived from OTU tables, metadata, labels, column names, and future user-configurable fields should be treated as untrusted. Add regression tests that generate reports using identifiers containing: - `<script>` elements. - Event-handler attributes such as `onerror`. - Quotes and angle brackets. - Encoded and nested markup. The tests should verify that the generated report contains encoded text such as `<` and does not contain executable attacker-supplied markup. ]]>
