From 1567386f50a7c57f6c322dceef1c528e04f615d7 Mon Sep 17 00:00:00 2001 From: Jamie Magee <jamie.magee@gmail.com> Date: Sat, 11 Mar 2023 21:54:12 -0800 Subject: [PATCH] refactor: safely parse `Pipfile.lock` (#20825) --- lib/modules/manager/pipenv/artifacts.ts | 40 ++++++++++++++++--------- lib/modules/manager/pipenv/schema.ts | 24 +++++++++++++++ 2 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 lib/modules/manager/pipenv/schema.ts diff --git a/lib/modules/manager/pipenv/artifacts.ts b/lib/modules/manager/pipenv/artifacts.ts index 0e7e6909da..e695998471 100644 --- a/lib/modules/manager/pipenv/artifacts.ts +++ b/lib/modules/manager/pipenv/artifacts.ts @@ -15,11 +15,12 @@ import type { UpdateArtifactsConfig, UpdateArtifactsResult, } from '../types'; +import { PipfileLockSchema } from './schema'; function getPythonConstraint( existingLockFileContent: string, config: UpdateArtifactsConfig -): string | undefined | null { +): string | undefined { const { constraints = {} } = config; const { python } = constraints; @@ -28,14 +29,20 @@ function getPythonConstraint( return python; } try { - const pipfileLock = JSON.parse(existingLockFileContent); - if (pipfileLock?._meta?.requires?.python_version) { - const pythonVersion: string = pipfileLock._meta.requires.python_version; + const result = PipfileLockSchema.safeParse( + JSON.parse(existingLockFileContent) + ); + // istanbul ignore if: not easily testable + if (!result.success) { + logger.warn({ error: result.error }, 'Invalid Pipfile.lock'); + return undefined; + } + if (result.data._meta?.requires?.python_version) { + const pythonVersion = result.data._meta.requires.python_version; return `== ${pythonVersion}.*`; } - if (pipfileLock?._meta?.requires?.python_full_version) { - const pythonFullVersion: string = - pipfileLock._meta.requires.python_full_version; + if (result.data._meta?.requires?.python_full_version) { + const pythonFullVersion = result.data._meta.requires.python_full_version; return `== ${pythonFullVersion}`; } } catch (err) { @@ -56,14 +63,19 @@ function getPipenvConstraint( return pipenv; } try { - const pipfileLock = JSON.parse(existingLockFileContent); - if (pipfileLock?.default?.pipenv?.version) { - const pipenvVersion: string = pipfileLock.default.pipenv.version; - return pipenvVersion; + const result = PipfileLockSchema.safeParse( + JSON.parse(existingLockFileContent) + ); + // istanbul ignore if: not easily testable + if (!result.success) { + logger.warn({ error: result.error }, 'Invalid Pipfile.lock'); + return ''; + } + if (result.data.default?.pipenv?.version) { + return result.data.default.pipenv.version; } - if (pipfileLock?.develop?.pipenv?.version) { - const pipenvVersion: string = pipfileLock.develop.pipenv.version; - return pipenvVersion; + if (result.data.develop?.pipenv?.version) { + return result.data.develop.pipenv.version; } } catch (err) { // Do nothing diff --git a/lib/modules/manager/pipenv/schema.ts b/lib/modules/manager/pipenv/schema.ts new file mode 100644 index 0000000000..a57cac2148 --- /dev/null +++ b/lib/modules/manager/pipenv/schema.ts @@ -0,0 +1,24 @@ +import { z } from 'zod'; + +const PipfileLockEntrySchema = z + .record( + z.string(), + z.object({ + version: z.string().optional(), + }) + ) + .optional(); +export const PipfileLockSchema = z.object({ + _meta: z + .object({ + requires: z + .object({ + python_version: z.string().optional(), + python_full_version: z.string().optional(), + }) + .optional(), + }) + .optional(), + default: PipfileLockEntrySchema, + develop: PipfileLockEntrySchema, +}); -- GitLab