Skip to content

Forge 1.0 security and privacy review

Decision

Approved for the 0.9.0-rc checkpoint with documented dispositions.

The review found no critical or high-severity issue that is exploitable through Forge’s supported generator, package, template, or automation paths. The one medium defense-in-depth finding was resolved through #140: repository secret scanning and push protection were enabled before stable 1.0.0.

This decision covers source commit f0a7a166453b84249e86b719c0c08d0e45965742, @rm-industries/content-model@1.0.0-rc.1, and @rm-industries/create-forge@1.0.0-rc.2. Re-run the affected review sections after a security-sensitive implementation or dependency change.

Scope and threat model

The review covered:

  • command construction and child-process execution;
  • destination resolution, path traversal, symbolic links, overwrites, rollback, and interruption;
  • npm lifecycle behavior, dependency advisories, package contents, provenance, and release immutability;
  • GitHub Actions triggers, permissions, credential persistence, secret handling, untrusted input, and action pinning;
  • generated URLs, browser-side network behavior, analytics, cookies, telemetry, CMS access, and public metadata;
  • source, generated output, and published packages for credentials, maintainer identifiers, and unresolved template tokens.

The primary attacker models are malicious CLI input, a hostile existing destination tree, compromised or unexpected dependency behavior, untrusted pull request content, forged automation input, and accidental credential or personal data publication. A user who explicitly approves overwriting files or installing npm dependencies remains responsible for those deliberate local actions.

Evidence

EvidenceResult
Repository npm run auditPass: 0 vulnerabilities across the workspace dependency graph
Generated-template npm run auditPass under the reviewed allowlist; 13 transitive development findings, with no production dependency finding
CodeQL (actions, javascript-typescript)Pass; no open code-scanning alert
Dependency reviewPass on the release and public test-site pull requests
Workflow securityZizmor and actionlint pass after #137 corrected the generated action annotation
Generator testsTraversal, root destination, symlink collision, overwrite refusal, rollback, interruption, and fixed command execution are covered
Published package inspection15 files in content-model and 95 files in create-forge; expected contents only
Registry integritycontent-model sha512-AqsJ8X0WLjR2VXEpl45Thbfl54w4PzVAUqf2WXexpppOVEELl0VvgCVgkyIUbHpmlxb2ZbClNifY8qJoMbw4kw==; create-forge sha512-f7m2E5EF7Md17JoYyljVGHkyptg1Yvysoos3BhIZ23xXsoF7PFQj3bQkOLbwuc4AuH1Hn21cC555BoFlRCCq1A==
Package and identifier scanNo private-key marker, common service token pattern, or Rahul-specific identifier in either registry tarball
Registry-backed test siteAll applicable CI, CodeQL, dependency-review, browser, accessibility, Lighthouse, and workflow-security checks pass on rm-industries.github.io#1

The package list and integrity values came from downloading the exact public npm versions with lifecycle scripts disabled. Both publications carry npm provenance and corresponding immutable GitHub release tags.

Manual review checklist

Generator and filesystem

  • The generator invokes only fixed npm and git executables with argument arrays; it does not invoke a shell or interpolate user input into a command.
  • Destinations containing a parent segment are rejected, and a resolved filesystem root cannot be selected.
  • Template paths are checked to remain beneath the destination.
  • Symbolic-link or non-regular-file collisions are rejected before copying.
  • A non-empty destination requires interactive confirmation. Non-interactive execution cannot silently opt into overwrite behavior.
  • Unrelated existing files are preserved, replaced files are backed up, and failed or interrupted materialization rolls back invocation-owned changes.
  • Generated TypeScript strings escape backslashes, quotes, and Unicode line separators; control characters are rejected from plain-text inputs.
  • Package names and repository identifiers use constrained validators.
  • Canonical site URLs require HTTP(S); generated site validation rejects URL credentials, query strings, and fragments.

Residual note: filesystem checks and writes are not atomic against a separate local process deliberately racing the destination tree. This is accepted as a low local threat because generation is a user-owned local operation, symlinks present during inspection are rejected, and no elevated privilege is used.

Dependencies and lifecycle scripts

  • Neither published package defines preinstall, install, or postinstall.
  • Release packing and tarball inspection use --ignore-scripts.
  • Template lockfile regeneration uses --ignore-scripts.
  • A generated project’s requested npm install uses npm’s normal lifecycle policy and visibly reports install-script approvals. This is an intentional package-manager boundary, not hidden generator execution.
  • Runtime package dependencies have no current npm audit finding.
  • Dependabot and the weekly configured audit retain ongoing dependency visibility.

Workflows and secrets

  • Workflows default to no permissions and grant job-specific permissions.
  • Pull-request validation uses read-only contents access; deployment runs only after a push to main and receives only pages: write plus id-token: write.
  • npm publication requires a package-scoped immutable tag, a protected environment, OIDC, a previously inspected artifact, matching registry integrity, and no persistent npm token.
  • Checkout credentials are not persisted, and third-party actions are pinned to reviewed commits.
  • Peer and template automation accept credentials only in trusted scheduled or manual jobs. GitHub App tokens request only contents and pull-request write access and are not exposed to pull-request code.
  • Repository-dispatch data is validated against a numeric source run, a verified publication artifact, exact package identity, semantic version, tag, and registry integrity before it can produce a pull request.
  • User-controlled workflow values are passed through environment variables and constrained before branch or package use rather than interpolated directly into workflow source.
  • GitHub secret scanning and push protection are enabled. The initial repository-history scan reported no open secret alert. See #140.

Privacy and generated network behavior

  • The generated public site contains no analytics, telemetry, tracking pixel, cookie, browser storage, beacon, remote font, or remote script service.
  • Fonts and default visual assets are packaged locally.
  • External links are configuration or documentation examples; Forge does not transmit visitor data to them.
  • The /admin/ route is marked noindex, nofollow. Sveltia CMS is bundled as a package and accesses GitHub only after a maintainer opens the admin tool and authenticates for repository content operations.
  • Canonical, Open Graph, RSS, sitemap, and manifest URLs derive from the validated site configuration.
  • The nonvisual Forge attribution is a static generator meta tag and causes no network request.

Findings and dispositions

IDUpstream or assessed severityFindingDisposition
SEC-01MediumGitHub secret scanning and push protection were disabled.Resolved through #140 on 2026-09-03. Both controls are enabled, and the initial repository-history query reported no open secret alert.
SEC-02Medium upstream, low in contextuuid@8.3.2 is present through development-only @lhci/cli. The advisory affects caller-provided buffers in UUID v3/v5/v6; LHCI calls uuid.v4() without a buffer for a temporary filename.Tolerable development-tool risk. Keep visible in Dependabot and re-evaluate when LHCI updates its dependency.
SEC-03High upstream, low in contextLighthouse’s Puppeteer browser-download path includes vulnerable extract-zip. Forge and generated CI install supported browsers explicitly and pass the installed browser to Lighthouse; no attacker-provided archive is extracted by the reviewed path.Time-bounded allowlist through 2026-11-30. Remove or renew only after re-review.
SEC-04High and low upstream, low in contextLHCI’s legacy tmp dependencies have path and symlink advisories. The reviewed Lighthouse path uses tool-controlled temporary names; the optional interactive report-opening path is not run by Forge CI.Time-bounded high-advisory allowlist through 2026-11-30; low advisory remains visible below the configured high failure threshold.

The upstream severities describe potentially affected packages in other usage contexts. The assessed severity describes Forge’s reachable invocation paths. There is no open critical or high assessed finding.

Release conclusion

The 0.9.0-rc security and privacy gate passes. Secret protection is enabled; re-review any unexpired dependency disposition that changes before the stable checkpoint.