Skip to content

Commit 5c0e3e2

Browse files
committed
fix: validate packagist time field is a parseable date
Signed-off-by: anilb <epipav@gmail.com>
1 parent 9e72e3c commit 5c0e3e2

2 files changed

Lines changed: 7 additions & 4 deletions

File tree

services/apps/packages_worker/src/packagist/__tests__/fetchPackage.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ describe('fetchPackagistP2', () => {
226226
['license is a scalar string, not an array', [{ version: '1.0.0', license: 'MIT' }]],
227227
['homepage is an object', [{ version: '1.0.0', homepage: {} }]],
228228
['time is a number', [{ version: '1.0.0', time: 20240101 }]],
229+
['time is a string but not a parseable date', [{ version: '1.0.0', time: 'not-a-date' }]],
229230
])('maps a malformed version entry (%s) → MALFORMED, not a throw', async (_desc, versions) => {
230231
vi.stubGlobal(
231232
'fetch',

services/apps/packages_worker/src/packagist/fetchPackage.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,17 +198,19 @@ function isValidVersionField(value: unknown, isValidType: (v: unknown) => boolea
198198
// `version`/`version_normalized` via .startsWith()/.split()/.endsWith()
199199
// (isPackagistDevVersion/isPackagistPrerelease), `homepage` via blankToNull's .trim(),
200200
// `license` as the text[] written to versions.licenses, `time` as the timestamptz
201-
// written to versions.published_at. `require`/`require-dev` are already guarded at
202-
// their own call site (extractVersionDependencies checks typeof before Object.entries)
203-
// so aren't re-validated here.
201+
// written to versions.published_at — checking it's merely a string isn't enough, since
202+
// a non-date string like "not-a-date" would still be typeof 'string' but throw at the
203+
// v.pub::timestamptz SQL cast, deep past this guard. `require`/`require-dev` are already
204+
// guarded at their own call site (extractVersionDependencies checks typeof before
205+
// Object.entries) so aren't re-validated here.
204206
function isValidMinifiedVersion(v: unknown): boolean {
205207
if (typeof v !== 'object' || v === null) return false
206208
const entry = v as Record<string, unknown>
207209
return (
208210
typeof entry.version === 'string' &&
209211
isValidVersionField(entry.version_normalized, (x) => typeof x === 'string') &&
210212
isValidVersionField(entry.homepage, (x) => typeof x === 'string') &&
211-
isValidVersionField(entry.time, (x) => typeof x === 'string') &&
213+
isValidVersionField(entry.time, (x) => typeof x === 'string' && !isNaN(Date.parse(x))) &&
212214
isValidVersionField(
213215
entry.license,
214216
(x) => Array.isArray(x) && x.every((l) => typeof l === 'string'),

0 commit comments

Comments
 (0)