| 1 |
Chat messages are injected into the DOM with innerHTML (addChatMsg uses the raw user text), enabling immediate XSS if a user types markup (e.g., <img onerror=alert()>). |
Insert messages with textContent or sanitize user input before rendering. |
| 2 |
Project names are written with innerHTML in renderProjects, so a crafted project name stored in localStorage will execute when the sidebar renders (stored XSS). |
Build project cards with DOM APIs or escape text before insertion. |
| 3 |
Inline onclick handlers embed unsanitized project ids in strings; a tampered localStorage entry containing quotes can break markup or execute script. |
Avoid inline handlers and bind events via addEventListener with sanitized data attributes. |
| 4 |
The preview iframe uses sandbox="allow-scripts allow-modals allow-forms allow-same-origin", so user/AI code runs with the same origin and can read or exfiltrate localStorage (saved projects). |
Drop allow-same-origin (and isolate previews via blob/data URLs) so preview code cannot touch app storage. |
| 5 |
allow-modals/allow-forms in the sandbox let untrusted preview code open blocking dialogs or silently submit forms (phishing vector). |
Remove unnecessary sandbox flags so preview content cannot open modal dialogs or submit forms outside the frame. |
| 6 |
window.open() for the detached preview is called without noopener/noreferrer, allowing the new window to control window.opener (tabnabbing) and steal data. |
Open with rel="noopener noreferrer" or set opener = null on the new window. |
| 7 |
External scripts/styles (Monaco loader, Font Awesome, Google Fonts) are pulled from CDNs without Subresource Integrity, exposing the app to supply-chain injections. |
Add integrity/crossorigin attributes or self-host the assets. |
| 8 |
No Content Security Policy is set; combined with multiple innerHTML uses, this leaves the page unprotected against XSS even after one injection point is fixed. |
Define a restrictive CSP (script/style/frame-src whitelists) and adjust code to comply. |
| 9 |
Social/about links that open in new tabs lack rel="noopener noreferrer", enabling reverse tabnabbing from external sites. |
Add rel="noopener noreferrer" to every target="_blank" anchor. |
| 10 |
The Pollinations request ships the entire project (HTML/CSS/JS) and user prompt to a third-party API without any warning or opt-in, leaking potentially sensitive code. |
Prompt for consent or gate AI calls behind a clear “send code to third-party” confirmation/setting. |
| 11 |
AI responses are not validated; if the API returns malformed text (no [HTML]/[CSS]/[JS] sections), setValue can wipe buffers while the UI still says “Code updated”. |
Validate the three sections and abort with an error message when parsing fails. |
| 12 |
chatArea.removeChild(chatArea.lastChild) assumes a spinner message exists; if the node is missing (e.g., DOM tampering), sendMessage throws and leaves the UI stuck disabled. |
Guard before removal or track the spinner element explicitly. |
| 13 |
Save/Run/Download/New/Open buttons are active before Monaco models are created; clicking them while the CDN is still loading throws because htmlModel/cssModel/jsModel are undefined. |
Disable controls until Monaco initialization completes or add null checks. |
| 14 |
getProjects() fails when localStorage is unavailable (private mode/blocked storage), preventing the editor from loading at all. |
Wrap storage access in try/catch and fall back to an empty list with a user warning. |
| 15 |
JSON.parse on localStorage data is unguarded; a corrupted entry crashes the app during startup render. |
Parse in try/catch and reset to a clean state on failure. |
| 16 |
localStorage.setItem in saveProject is not wrapped; quota errors or blocked storage throw and leave the UI mid-flow without feedback. |
Catch storage errors and surface a clear “save failed” message. |
| 17 |
Project cards are built with innerHTML, so any stored value containing markup breaks the sidebar structure even if XSS is mitigated elsewhere. |
Build DOM nodes explicitly instead of concatenating HTML strings. |
| 18 |
The autosave dot implies data persistence, but it only reflects preview updates—reloading the page loses edits unless the user manually saves. |
Either implement real autosave to storage or relabel the indicator to avoid false reassurance. |
| 19 |
Despite documentation promises, there is no actual autosave of drafts to localStorage, so closing the tab discards work silently. |
Add timed persistence of the models to storage with recovery on load. |
| 20 |
README advertises a light/dark mode toggle, but the UI only ships a dark theme with no toggle control. |
Implement the theme switcher or adjust the docs to match reality. |
| 21 |
README suggests a “Contact Us” channel, but no link or UI affordance exists in the app. |
Provide a working contact/support link or remove the claim. |
| 22 |
The README usage snippet references an HTMLEditor class/API that does not exist in the codebase, misleading integrators. |
Update usage docs to match the current static app or supply the missing API. |
| 23 |
The app claims to be “100% offline capable”, yet it depends on multiple CDNs and an external AI endpoint; it fails without network access. |
Bundle assets locally and make AI optional/disabled offline, or correct the claim. |
| 24 |
“Mandatory filename before download” is promised, but downloads proceed with a default name when the input is empty. |
Enforce non-empty filenames (or prompt) before allowing download. |
| 25 |
“Import sample templates” is listed as a capability, but there is no UI or code path providing templates. |
Either implement template importers or remove the advertised feature. |
| 26 |
README mentions a social footer; the live UI has none, so expected links are missing. |
Add the footer or update documentation/screenshots. |
| 27 |
<link rel="canonical"> points to https://altkriz.github.io/htmleditor/, which is incorrect for this deployment and hurts SEO. |
Update the canonical URL to match the live URL for this project. |
| 28 |
Open Graph/Twitter images reference https://altkriz.github.io/htmleditor/img/logo.png, which may 404 and shows the wrong branding for this repository. |
Host the assets within the repo and reference them with correct URLs. |
| 29 |
The favicon links to a remote icon (altkriz.github.io) even though favicon.ico exists locally, so offline/favicon loads can fail. |
Point favicon links to the local asset or include a fallback. |
| 30 |
Monaco loader is only pulled from a CDN with no fallback; if the CDN is blocked the editor never initializes and no error is shown. |
Add a local fallback bundle or display a graceful error when Monaco fails to load. |
| 31 |
AI calls lack timeouts/abort handling; a stalled request leaves the spinner for several seconds and provides no recovery guidance. |
Use AbortController with a reasonable timeout and surface retry guidance. |
| 32 |
AI fetch doesn’t check HTTP status; non-200 responses still flow to response.json() and can throw, surfacing as a generic “Error” message. |
Validate status codes and show a helpful error before parsing JSON. |
| 33 |
Body is overflow: hidden while the AI drawer can expand to 350px; on small screens content below the fold becomes unreachable (no scrolling). |
Allow vertical scrolling or adjust layout for small viewports. |
| 34 |
Sidebar width is fixed at 300px with no responsive adjustment, making it cover nearly the whole screen on mobile and harming usability. |
Add responsive widths or make the panel overlay dismissible on tap/esc with responsive sizing. |
| 35 |
Tab controls are plain <div>s without role="tab"/keyboard handling, so screen readers and keyboard users cannot navigate the editors properly. |
Implement ARIA tab semantics and keyboard navigation. |
| 36 |
Icon-only buttons (run, download, about, open-new, close sidebar, delete project) have no accessible labels. |
Add aria-label text to icon buttons. |
| 37 |
Text inputs (filename, AI prompt) lack associated <label>s or aria-labelledby, reducing accessibility and form usability. |
Provide visible/accessible labels or aria attributes. |
| 38 |
The modal lacks role="dialog"/aria-modal and doesn’t trap focus, so keyboard users can tab into background controls while it is open. |
Add proper dialog semantics and focus trapping. |
| 39 |
Sidebar opening doesn’t manage focus; keyboard focus remains in the editor, so keystrokes still edit code behind the overlay. |
Move focus into the panel and restore it on close. |
| 40 |
AI drawer toggle doesn’t expose aria-expanded/aria-controls, making its state invisible to assistive tech. |
Add the appropriate ARIA attributes and update them on toggle. |
| 41 |
Chat transcript isn’t marked as a live region; screen-reader users won’t be informed when AI responses arrive. |
Mark chat output with aria-live="polite" (or similar) for announcements. |
| 42 |
Project cards are plain <div>s and not keyboard focusable, preventing keyboard users from loading/deleting projects without a mouse. |
Make cards or their action buttons keyboard focusable with clear focus styles. |
| 43 |
Download flow creates an object URL but never revokes it, leaking memory across repeated downloads in long sessions. |
Call URL.revokeObjectURL after triggering the download. |
| 44 |
Filenames from user input are used directly for downloads; characters like slashes/newlines can produce invalid names or unexpected behavior. |
Sanitize the filename to a safe character set before download. |
| 45 |
When deleting the currently loaded project, the editor content remains and currentProjectId is nulled; a subsequent save silently re-creates the “deleted” project under a new id. |
Clear editors (or switch to defaults) when deleting the active project. |
| 46 |
Loading a missing/removed project id is a no-op with no feedback, leaving users unaware that nothing changed. |
Show an error toast/message when a requested project cannot be found. |
| 47 |
Rapid resize events call editor.layout() on every event without throttling, which can jank the UI on window drags. |
Throttle/debounce layout or enable Monaco’s automaticLayout. |
| 48 |
The app does not remember the last opened project or tab, so reloading drops users back to defaults and risks overwriting work if they forget to reload a project before saving. |
Persist last active project id/tab in storage and restore on load. |
| 49 |
AI prompt includes the full current code without truncation; large projects can exceed model limits and fail silently with a generic error. |
Truncate/stream the context or warn when the payload is too large. |
| 50 |
Preview/build pipeline has no tests or runtime checks, so regressions (e.g., broken Monaco load, storage failures) aren’t caught automatically. |
Add minimal automated tests or health checks for critical paths (Monaco init, save/load, preview). |