Skip to content

Security audit: CI shell injection, potential API key exposure, and XSS patterns #741

@LukePercy

Description

@LukePercy

Summary

A static security scan via repowatch.io identified 18 findings across the repository. The most actionable items are grouped below by priority.

Overall score: 59/100 — strong code quality (95) and test confidence (90), but security hygiene was pulled to 0 by the findings below.

Full scan report: docs/repowatch-scan-2026-04-01.md


🔴 Critical: potential API key in test file

Location: tests/vite-hmr-websocket.test.ts:32

Gitleaks flagged a value matching the generic-api-key pattern. If this is a live credential, it should be rotated immediately and moved to environment-based secrets. If it's a test fixture/dummy value, no action is needed.


🟠 High: GitHub Actions shell injection (4 locations)

All four use ${{ github.event.* }} context data directly inside run: steps. An attacker can craft a PR title or branch name to inject arbitrary shell commands, exfiltrating secrets or writing to the repo.

Locations:

  • .github/workflows/publish.yml:47
  • .github/workflows/nextjs-tracker.yml:42
  • .github/workflows/nextjs-tracker.yml:73
  • .github/workflows/nextjs-tracker.yml:137

Fix pattern: Use an intermediate environment variable instead of inline interpolation:

# Before (vulnerable)
run: echo "${{ github.event.pull_request.title }}"

# After (safe)
env:
  PR_TITLE: ${{ github.event.pull_request.title }}
run: echo "$PR_TITLE"

🟠 High: innerHTML in script shim

Location: packages/vinext/src/shims/script.tsx:159

innerHTML is used with potentially dynamic content. If the input is always developer-controlled (script config), this is acceptable by design. If user-supplied data can reach this path, it's an XSS vector.


🟡 Medium: non-literal RegExp (6 locations)

new RegExp(variable) patterns — potential ReDoS if the variable is user-controlled. In this codebase these appear to be internal config values (pageExtensions, fileNames, etc.), so likely low risk. Worth confirming no user input flows to these.

Locations:

  • packages/vinext/src/shims/head.ts:276
  • packages/vinext/src/shims/image-config.ts:58
  • packages/vinext/src/config/config-matchers.ts:341
  • packages/vinext/src/config/config-matchers.ts:974
  • packages/vinext/src/routing/file-matcher.ts:51
  • packages/vinext/src/routing/file-matcher.ts:54

🟡 Medium: dangerouslySetInnerHTML in example

Location: examples/hackernews/components/comment.jsx:29

Renders API-sourced HTML without sanitization. Consider using DOMPurify if the HN API response is not fully trusted.


🔵 Low: unsafe format strings (2 locations)

Server-side debug logs using string concatenation in console.log. Minimal risk.

  • packages/vinext/src/server/isr-cache.ts:93
  • packages/vinext/src/shims/fetch-cache.ts:679

Report generated by Repo Watch — static risk audit for repositories. Findings are directional signals, not proof of exploitability.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions