Skip to content

fix(core): guard JSON manifests that parse to a bare null - #122

Merged
Hydralerne merged 1 commit into
oblien:mainfrom
shuvamk:fix/core-null-json-manifest-guard
Jul 22, 2026
Merged

fix(core): guard JSON manifests that parse to a bare null#122
Hydralerne merged 1 commit into
oblien:mainfrom
shuvamk:fix/core-null-json-manifest-guard

Conversation

@shuvamk

@shuvamk shuvamk commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Problem

JSON.parse("null") succeeds and returns null, so a try/catch around the parse doesn't cover the property access that comes after it. Three parsers read a property straight off the result:

Parser File Access
parseVercelConfig metadata/vercel.ts parsed.installCommand
parsePackageJsonDeps languages/javascript.ts parsed.dependencies
parseComposerJson languages/php.ts parsed.require

Executed on main:

vercel.json   = null  -> THREW: null is not an object (evaluating 'parsed.installCommand')
package.json  = null  -> THREW: null is not an object (evaluating 'parsed.dependencies')
composer.json = null  -> THREW: null is not an object (evaluating 'parsed.require')

parseDeploymentMetadata({"vercel.json": "null"})
                      -> THREW: null is not an object (evaluating 'parsed.installCommand')

The TypeError escapes parseDeploymentMetadata and the language detectors, so one degenerate file aborts detection for the whole project instead of being ignored like every other unusable manifest.

This was already recognised once

metadata/railway.ts carries exactly this guard, with a comment naming the failure:

// `JSON.parse("null")` succeeds → guard before property access, or a stray
// `null`/primitive file would throw and crash the metadata pipeline.
if (typeof parsed !== "object" || parsed === null) return null;

Its three siblings never got it. This PR applies the same check, worded consistently and cross-referencing railway.ts.

Fix

Three lines, one per parser — return null for the vercel config, return {} for the two dependency maps, matching each function's existing "unusable input" path.

Scope note

A bare primitive (42, "str") never threw — JS boxes those on property access, so they already fell through to an empty result. null is the only JSON literal affected, which is why the guard is typeof !== "object" || === null rather than a broader shape check.

Tests

New packages/core/test/null-json-manifest.test.ts — 6 cases (@repo/core 89 → 95):

  • each of the three parsers returns its empty value for null
  • parseDeploymentMetadata doesn't throw and returns []
  • ordinary manifests still parse (vercel framework, npm deps + devDeps merged, composer require)
  • malformed JSON still returns empty — the existing catch path is untouched

Verified against the unfixed source: the 4 null cases fail, and the 2 regression guards pass either way.

Full suite: bun run test5/5 turbo tasks successful (api 714, adapters 235, core 95, db 25, dashboard 17).

`JSON.parse("null")` succeeds and returns null, so the `catch` around the
parse doesn't cover the property access that follows. Three parsers read a
property straight off the result and throw a TypeError:

  parseVercelConfig("null")            -> parsed.installCommand
  parsePackageJsonDeps("null")         -> parsed.dependencies
  parseComposerJson("null")            -> parsed.require

The throw escapes parseDeploymentMetadata / the language detectors, so one
degenerate file aborts detection for the entire project rather than being
ignored.

metadata/railway.ts already carries this exact guard, with a comment saying
an unguarded null "would throw and crash the metadata pipeline". This
applies the same three-line check to its siblings.

Note a bare primitive (`42`, `"str"`) never threw — JS boxes those on
property access — so `null` is the only JSON literal affected.
@Hydralerne
Hydralerne merged commit f301866 into oblien:main Jul 22, 2026
2 checks passed
@shuvamk
shuvamk deleted the fix/core-null-json-manifest-guard branch July 23, 2026 07:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants