davila7/claude-code-templates · error

SEM_E017

SEM_E017

Error message

Inline event handler detected (XSS risk)

What it means

SEM_E017 is emitted by checkHtmlInjection when the content contains 'onclick=' (case-insensitive substring). Inline event-handler attributes execute JavaScript from HTML, so embedding them in distributed component markdown is flagged as an XSS risk.

Solutions

  1. Escape or paraphrase HTML examples rather than including literal 'onclick=' (e.g. describe: 'attach a click handler via addEventListener')
  2. For React examples, note the validator lowercases content, so onClick= matches too — use addEventListener-style examples or move code to a linked external file
  3. Remove inline handlers from real templates and bind events in referenced JS files

Example fix

# before
<button onclick="doThing()">Go</button>
# after
<button id="go">Go</button> <!-- bind via addEventListener in app.js -->
Defensive patterns

Strategy: validation

Validate before calling

if (component.content.toLowerCase().includes('onclick=')) {
  // escape/paraphrase handler examples
}

Prevention

When it happens

Trigger: validate() where content.toLowerCase().includes('onclick='). Any occurrence counts, including HTML examples in fenced code blocks, JSX/React sample code like <button onClick={...} (matches after lowercasing), or attribute listings.

Common situations: Agent/command markdown containing React or HTML snippets with onClick handlers; front-end tutorial components; copy-pasted markup examples.

Related errors


AI-assisted analysis of davila7/claude-code-templates@a0851ed10c (2026-08-28). Data as JSON: /api/errors/ddde787329829dfe. Report an issue: GitHub.

Appendix: source

Thrown at cli-tool/src/validation/validators/SemanticValidator.js:289

        this.addError(code, message, {
          path,
          severity,
          matches: contexts.length,
          examples: contexts.slice(0, 3)
        });
      }
    }
  }

  /**
   * Check for HTML/Script injection attempts
   */
  checkHtmlInjection(content, path) {
    const dangerousTags = [
      { tag: '<script', code: 'SEM_E014', message: '<script> tag detected (XSS risk)' },
      { tag: '<iframe', code: 'SEM_E015', message: '<iframe> tag detected (injection risk)' },
      { tag: 'javascript:', code: 'SEM_E016', message: 'javascript: protocol detected (XSS risk)' },
      { tag: 'onclick=', code: 'SEM_E017', message: 'Inline event handler detected (XSS risk)' },
      { tag: 'onerror=', code: 'SEM_E018', message: 'onerror handler detected (XSS risk)' }
    ];

    for (const { tag, code, message } of dangerousTags) {
      const lowerContent = content.toLowerCase();
      if (lowerContent.includes(tag.toLowerCase())) {
        const index = lowerContent.indexOf(tag.toLowerCase());
        const lineInfo = this.getLineFromIndex(content, index);

        this.addError(code, message, {
          path,
          severity: 'critical',
          line: lineInfo.line,
          column: lineInfo.column,
          position: lineInfo.position,
          lineText: lineInfo.lineText,
          context: this.getContext(content, index, 50)
        });

View on GitHub (pinned to a0851ed10c)