.md` as items to migrate.
3. **Decide replacement strategy (per assignment)**
- **Static text**: replace with `element.textContent = "..."`.
- **Create elements**: replace markup with `document.createElement()` / `el.classList.add()` / `el.setAttribute()` and `appendChild`. For multiple nodes use `DocumentFragment`.
- **Template HTML with user data**:
- If **all interpolations are escaped**: build a template using safe concatenation of escaped values:
```js
import { escapeHtml } from '../utils/domUtils.js';
el.innerHTML = `${escapeHtml(name)} - ${escapeHtml(email)}
`;
```
- If possible prefer element creation over `innerHTML` even for templated markup: create nodes and set `textContent` for text nodes. Use `escapeHtml` only where building HTML is unavoidable.
- **User-derived HTML or rich HTML from server**:
- If it must be rendered as HTML, **ensure** server-side trusted source and sanitize (preferably via a safe sanitizer). If not available, **do not** render raw HTML; instead show safe text, or open an Issue for a review and leave unchanged with a clear comment.
- **Event handlers**:
- If handlers were embedded in HTML markup (e.g., `onclick="..."`) remove and use `element.addEventListener()` when reconstructing nodes.
Record the strategy per assignment in `src/decisions/DECISIONS_.md`.
4. **Perform replacements (one-by-one)**
- For each assignment:
- Replace the `innerHTML` assignment with the chosen safe alternative.
- Use `escapeHtml()` from `js/utils/domUtils.js` for any interpolated user data in templates. Import it at top:
```js
import { escapeHtml } from './utils/domUtils.js';
```
- Prefer building a `DocumentFragment` for groups of nodes to minimize reflows.
- Keep code readable and semantically clear; add comments indicating the former `innerHTML` assignment and why it was replaced.
5. **Run lint and tests**
- Run linter:
```
npm run lint
```
Fix lint issues introduced by imports/formatting.
- Run unit tests:
```
npm run test:unit
```
If tests fail, revert offending change(s) or adjust tests accordingly. Document failures in `src/test_logs/TEST_LOG_.md`; if the change is correct but tests need updating, update tests (prefer minimal changes).
6. **Manual verification**
- If the file affects UI, run a smoke test:
- Start dev server: `npm run dev` (or repo-specific dev command).
- Perform the user flows that exercise replaced code (profile modal open, search results, channel profile).
- Verify DOM is correct, text visible, events still fire, no console errors.
- Record steps and observations in `src/test_logs/TEST_LOG_.md`.
7. **Update baseline**
- After verifying, update the baseline counts so the `check-innerhtml` script knows this file was addressed:
```
node scripts/check-innerhtml.mjs --update
```
- The script prints a new `BASELINE` object. **Copy** the updated `BASELINE` object into `scripts/check-innerhtml.mjs` (replace existing `BASELINE`).
- Commit the updated script with a note in `src/decisions/DECISIONS_.md` explaining the baseline change.
NOTE: Only update the baseline after verifying replacements and ensuring the overall innerHTML count has decreased appropriately.
8. **Commit & PR**
- Create a focused branch:
```
git checkout -b ai/innerhtml-fix/
```
- Stage only the file(s) changed and the baseline script if updated plus the audit artifacts/docs.
- Commit with message prefix `[security]`:
```
git add ...
git commit -m "[security] replace innerHTML usages in js/path/to/chosenFile.js — convert to safe DOM APIs"
```
- Push and open a PR targeting ``. PR body must include:
- files in `src/context/`, `src/todo/`, `src/decisions/`, `src/test_logs/`
- A short summary of the file and changes:
- List of assignments replaced and strategy for each.
- Before/after innerHTML counts (run `node scripts/check-innerhtml.mjs --report` before and after and paste results).
- Lint and unit test results.
- Manual QA steps and results.
- Highlight any remaining `innerHTML` assignments intentionally left (with rationale).
- Label PR with `security`, `requires-review`.
9. **Follow-up**
- Respond to review comments.
- Once merged, ensure CI passes and the baseline in repo is updated.
===============================================================================
PRIORITY ORDER for file selection
1. **RISKY** files first (user data without escaping).
2. **High-count** files next (profileModalController, searchView, channelProfile).
3. **Low-count** static files last.
Pick ONE RISKY or high-count file. Do not attempt multiple files in the same branch/PR.
===============================================================================
DETERMINING IF CONTENT IS USER-DERIVED
- Trace the variable back to its source:
- Data from `profileCache`, API responses, `event.content`, form inputs, `dataset`, or `location` is user-derived or external.
- Static constants or hard-coded strings are not user-derived.
- If unsure, err on the side of **treating as user-derived** and escape.
===============================================================================
HELPFUL CODE PATTERNS (examples)
**Static text**
```js
// before
el.innerHTML = "No results";
// after
el.textContent = "No results";
````
**Creating elements safely**
```js
// before
el.innerHTML = 'Item';
// after
const li = document.createElement('li');
li.className = 'item';
li.textContent = 'Item';
el.appendChild(li);
```
**Template with escaped user data**
```js
import { escapeHtml } from '../utils/domUtils.js';
// before
el.innerHTML = `${user.name}
${user.bio}
`;
// after (preferred)
const div = document.createElement('div');
div.textContent = user.name;
const p = document.createElement('p');
p.textContent = user.bio;
el.appendChild(div);
el.appendChild(p);
// or, if HTML structure is complex and must be templated:
el.innerHTML = `${escapeHtml(user.name)}
${escapeHtml(user.bio)}
`;
```
**DocumentFragment for multiple nodes**
```js
const frag = document.createDocumentFragment();
for (const item of items) {
const li = document.createElement('li');
li.textContent = item.title;
frag.appendChild(li);
}
el.appendChild(frag);
```
**Adding event listeners (do not embed handlers in HTML)**
```js
const btn = document.createElement('button');
btn.textContent = 'Click';
btn.addEventListener('click', () => handleClick(item.id));
el.appendChild(btn);
```
===============================================================================
TESTING / QA CHECKS (must run before PR)
* `npm run lint` — fix any lint issues.
* `npm run test:unit` — all unit tests must pass.
* Optional: `npm run test:integration` if available and relevant.
* Manual smoke test of UI flows touched. Record steps + screenshots (redact sensitive info).
Record all commands and outputs in `src/test_logs/TEST_LOG_.md`.
===============================================================================
ACCEPTANCE CRITERIA (before merging)
* The chosen file’s `innerHTML` assignments are replaced or documented; user data is escaped or DOM-built.
* Lint passes and unit tests pass locally and in CI.
* `scripts/check-innerhtml.mjs --report` shows reduced count for the chosen file.
* Updated `BASELINE` object copied into `scripts/check-innerhtml.mjs`.
* PR contains files in `src/context/`, `src/todo/`, `src/decisions/`, `src/test_logs/`, summary of before/after, and QA steps.
* Commit message prefix is `[security]`.
===============================================================================
WHEN TO OPEN AN ISSUE INSTEAD
* If an assignment cannot be safely replaced because rendering requires trusted HTML or the underlying approach requires a larger refactor (templating system change, server sanitization), open an Issue:
* Title: `innerHTML migration needed: js/path/to/file.js`
* Body: explain why migration can't proceed in a single PR, risk, and suggested options (sanitization, templating, server change).
===============================================================================
FINAL NOTES & Etiquette
* Do not attempt to be clever: prioritize safety and clarity.
* Keep changes small and reversible.
* If you have doubts about the nature of an `innerHTML`, stop and ask a maintainer.
* Document everything in `src/decisions/DECISIONS_.md` so reviewers can understand assumptions.
Begin now:
1. Run `node scripts/check-innerhtml.mjs --report`
2. Pick **ONE** file (RISKY/high-count preferred).
3. Follow the workflow above and open a `[security]` PR to `` when ready.
FAILURE MODES
- If preconditions are not met, stop.
- If no changes are needed, do nothing.
- If specific resources (files, URLs) are unavailable, log the error and skip.