◆QA Engine
← All runs

ivjames/artificial-atheist@main

Export JSON
repo started 2026-08-02 13:27 UTC · finished 2026-08-02 13:28 UTC · 334 pages · 56s scan · status done · #0d9f4dca5ca24497a2669cec932b30d4
4 distinct issues Grouped by rule across all scanned pages, worst severity first. Click the severity pills above to filter.
moderate code code/inner-html 12 pages
Raw HTML injection surface
Assigning markup strings into the DOM is an XSS surface; verify every interpolated value is escaped.
Affected pages (12)
Raw evidence (JSON)
[
  {
    "url": "app/(app)/review/prophecy/evaluations/page.tsx",
    "severity": "moderate",
    "title": "Raw HTML injection surface",
    "evidence": {
      "source": "app/(app)/review/prophecy/evaluations/page.tsx",
      "line": 430,
      "snippet": "dangerouslySetInnerHTML={{"
    }
  },
  {
    "url": "app/layout.tsx",
    "severity": "moderate",
    "title": "Raw HTML injection surface",
    "evidence": {
      "source": "app/layout.tsx",
      "line": 116,
      "snippet": "<script dangerouslySetInnerHTML={{ __html: themeScript }} />"
    }
  },
  {
    "url": "app/layout.tsx",
    "severity": "moderate",
    "title": "Raw HTML injection surface",
    "evidence": {
      "source": "app/layout.tsx",
      "line": 129,
      "snippet": "dangerouslySetInnerHTML={{ __html: gtagStub(site.analytics.gaId) }}"
    }
  },
  {
    "url": "app/posts/[slug]/page.tsx",
    "severity": "moderate",
    "title": "Raw HTML injection surface",
    "evidence": {
      "source": "app/posts/[slug]/page.tsx",
      "line": 96,
      "snippet": "dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}"
    }
  },
  {
    "url": "app/posts/[slug]/page.tsx",
    "severity": "moderate",
    "title": "Raw HTML injection surface",
    "evidence": {
      "source": "app/posts/[slug]/page.tsx",
      "line": 117,
      "snippet": "<div className=\"prose\" dangerouslySetInnerHTML={{ __html: post.html }} />"
    }
  },
  {
    "url": "components/site/pub.tsx",
    "severity": "moderate",
    "title": "Raw HTML injection surface",
    "evidence": {
      "source": "components/site/pub.tsx",
      "line": 22,
      "snippet": "dangerouslySetInnerHTML={{ __html: topicPattern(t, seed) }}"
    }
  },
  {
    "url": "tools/admin/index.html",
    "severity": "moderate",
    "title": "Raw HTML injection surface",
    "evidence": {
      "source": "tools/admin/index.html",
      "line": 176,
      "snippet": "$(\"cstats\").innerHTML ="
    }
  },
  {
    "url": "tools/admin/index.html",
    "severity": "moderate",
    "title": "Raw HTML injection surface",
    "evidence": {
      "source": "tools/admin/index.html",
      "line": 181,
      "snippet": "if(!q.length){ $(\"queue\").innerHTML='<div class=\"hint\">Nothing pending.</div>'; return; }"
    }
  },
  {
    "url": "tools/admin/index.html",
    "severity": "moderate",
    "title": "Raw HTML injection surface",
    "evidence": {
      "source": "tools/admin/index.html",
      "line": 182,
      "snippet": "$(\"queue\").innerHTML = q.map(c=>`"
    }
  },
  {
    "url": "tools/studio/index.html",
    "severity": "moderate",
    "title": "Raw HTML injection surface",
    "evidence": {
      "source": "tools/studio/index.html",
      "line": 122,
      "snippet": "function renderPreview(){ $(\"preview\").innerHTML = md.render($(\"body\").value || \"*Nothing yet.*\"); }"
    }
  },
  {
    "url": "tools/studio/index.html",
    "severity": "moderate",
    "title": "Raw HTML injection surface",
    "evidence": {
      "source": "tools/studio/index.html",
      "line": 139,
      "snippet": "$(\"titles\").innerHTML = c.titles.map(t=>\"• \"+t).join(\"<br>\") || \"none yet\";"
    }
  },
  {
    "url": "tools/studio/index.html",
    "severity": "moderate",
    "title": "Raw HTML injection surface",
    "evidence": {
      "source": "tools/studio/index.html",
      "line": 140,
      "snippet": "$(\"status\").innerHTML = `provider <b>${c.provider.provider}</b> · ${c.provider.model}` +"
    }
  }
]
moderate code code/logic 4 pages
TOCTOU race allows a token to be redeemed twice concurrently
consumeMagicLink reads the token with findUnique, checks usedAt/expiresAt, then issues a separate update marking it used. Two concurrent requests with the same raw token can both pass the usedAt check before either update commits, both proceeding to authenticate as the user. Fix by making the check-and-mark atomic, e.g. `update` with a `where` clause requiring `usedAt: null` (and checking the returned row), wrapped in a transaction, or a conditional update that fails if already used.
Affected pages (4)
Raw evidence (JSON)
[
  {
    "url": "lib/auth/session.ts",
    "severity": "minor",
    "title": "Session age check does not reject negative age",
    "evidence": {
      "selector": "lib/auth/session.ts:69",
      "snippet": "if (!Number.isFinite(age) || age > MAX_AGE_SECONDS) return null;"
    }
  },
  {
    "url": "lib/auth/tokens.ts",
    "severity": "moderate",
    "title": "TOCTOU race allows a token to be redeemed twice concurrently",
    "evidence": {
      "selector": "lib/auth/tokens.ts:99-105",
      "snippet": "const token = await prisma.loginToken.findUnique({ where: { tokenHash } });\n  if (!token || token.usedAt || token.expiresAt < new Date()) return null;\n\n  await prisma.loginToken.update({\n    where: { id: token.id },\n    data: { usedAt: new Date() },\n  });"
    }
  },
  {
    "url": "app/api/payments/webhook/route.ts",
    "severity": "moderate",
    "title": "No idempotency check on fulfillment",
    "evidence": {
      "selector": "app/api/payments/webhook/route.ts:29-31",
      "snippet": "if (event?.paid) {\n    await fulfillByRef(event.ref);\n  }"
    }
  },
  {
    "url": "app/api/payments/webhook/route.ts",
    "severity": "minor",
    "title": "Errors from fulfillByRef are unhandled",
    "evidence": {
      "selector": "app/api/payments/webhook/route.ts:29-31",
      "snippet": "await fulfillByRef(event.ref);"
    }
  }
]
moderate security security/xss 4 pages
Unsanitized HTML rendered via dangerouslySetInnerHTML
post.html (rendered markdown) is injected directly into the DOM without any sanitization step (e.g. DOMPurify/rehype-sanitize) visible in this file or confirmed upstream. If post content ever originates from or passes through less-trusted input (contributor markdown, CMS, user-submitted content), this is a stored XSS vector. Even for trusted static markdown files, this pattern is fragile: any future move to accept external post/markdown submissions would immediately introduce XSS. Verify that lib/posts.ts sanitizes the HTML output of the markdown renderer; if not, add sanitization (e.g. rehype-sanitize or DOMPurify) before rendering.
Affected pages (4)
Raw evidence (JSON)
[
  {
    "url": "app/(app)/review/prophecy/evaluations/page.tsx",
    "severity": "info",
    "title": "dangerouslySetInnerHTML use is a static, hardcoded style string (not user input)",
    "evidence": {
      "selector": "app/(app)/review/prophecy/evaluations/page.tsx",
      "snippet": "dangerouslySetInnerHTML={{ __html: \".eval-charts{--s0:#7d251f;...}\" }}",
      "note": "Static literal, no template interpolation of external data."
    }
  },
  {
    "url": "app/layout.tsx",
    "severity": "info",
    "title": "innerHTML usage confined to build-time constant scripts",
    "evidence": {
      "selector": "app/layout.tsx:120",
      "snippet": "<script dangerouslySetInnerHTML={{ __html: themeScript }} />",
      "note": "no diff available; reviewed as static file"
    }
  },
  {
    "url": "app/posts/[slug]/page.tsx",
    "severity": "moderate",
    "title": "Unsanitized HTML rendered via dangerouslySetInnerHTML",
    "evidence": {
      "selector": "app/posts/[slug]/page.tsx:120",
      "snippet": "<div className=\"prose\" dangerouslySetInnerHTML={{ __html: post.html }} />"
    }
  },
  {
    "url": "components/site/pub.tsx",
    "severity": "minor",
    "title": "dangerouslySetInnerHTML fed by topic/seed derived from data",
    "evidence": {
      "selector": "components/site/pub.tsx:22-25",
      "snippet": "dangerouslySetInnerHTML={{ __html: topicPattern(t, seed) }}"
    }
  }
]
moderate security generic-credential 1 page
Possible hard-coded credential exposed
A key/value pair matching common secret naming conventions (api_key, secret, password, token) was found in tests/preview.test.ts; verify this is not a live credential.
Affected pages (1)
Raw evidence (JSON)
[
  {
    "url": "tests/preview.test.ts",
    "severity": "moderate",
    "title": "Possible hard-coded credential exposed",
    "evidence": {
      "source": "tests/preview.test.ts",
      "rule": "generic-credential",
      "match": "TOKE…ink\"",
      "offset": 808
    }
  }
]