From 9da6609255abd41e69c53ee8c39623c35d7a595e Mon Sep 17 00:00:00 2001 From: Celia Amador Date: Wed, 9 Sep 2026 09:18:28 +0200 Subject: [PATCH 1/2] Add script to fix ConditionType automatically Made-with: Cursor --- libs/types/package.json | 1 + libs/types/scripts/fix-condition-type.js | 94 ++++++++++++++++++++++++ libs/types/scripts/openapi-typescript.js | 5 ++ 3 files changed, 100 insertions(+) create mode 100644 libs/types/scripts/fix-condition-type.js diff --git a/libs/types/package.json b/libs/types/package.json index 8e1eb25c26..916cf022d2 100644 --- a/libs/types/package.json +++ b/libs/types/package.json @@ -32,6 +32,7 @@ "prebuild": "tsc --noEmit && rimraf dist", "build": "tsc --build", "gen-types": "node ./scripts/openapi-typescript.js && npm run build", + "fix-condition-type": "node ./scripts/fix-condition-type.js", "ts-node": "ts-node -O '{\"module\":\"commonjs\"}'" }, "devDependencies": { diff --git a/libs/types/scripts/fix-condition-type.js b/libs/types/scripts/fix-condition-type.js new file mode 100644 index 0000000000..8fae52fc58 --- /dev/null +++ b/libs/types/scripts/fix-condition-type.js @@ -0,0 +1,94 @@ +#!/usr/bin/env node +const fs = require('fs/promises'); +const path = require('path'); +const YAML = require('js-yaml'); + +const CONDITION_TYPE_PATH = path.resolve(__dirname, '../models/ConditionType.ts'); + +function buildConditionTypeSource({ description, varnames, enumValues }) { + if (varnames.length !== enumValues.length) { + throw new Error( + `ConditionType x-enum-varnames (${varnames.length}) and enum (${enumValues.length}) lengths differ`, + ); + } + + const members = varnames.map((name, index) => ` ${name} = '${enumValues[index]}',`).join('\n'); + + return `/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * ${description} + */ +export enum ConditionType { +${members} +} +`; +} + +function extractConditionType(openApiDocument) { + const schema = openApiDocument?.components?.schemas?.ConditionType; + if (!schema) { + throw new Error('ConditionType schema not found in OpenAPI document'); + } + + const varnames = schema['x-enum-varnames']; + const enumValues = schema.enum; + + if (!Array.isArray(varnames) || varnames.length === 0) { + throw new Error('ConditionType is missing x-enum-varnames'); + } + if (!Array.isArray(enumValues) || enumValues.length === 0) { + throw new Error('ConditionType is missing enum values'); + } + + return { + description: schema.description || 'Type of condition in CamelCase.', + varnames, + enumValues, + }; +} + +function parseOpenApiInput(input) { + if (typeof input === 'string') { + return YAML.load(input); + } + return input; +} + +async function fixConditionType(input, outputPath = CONDITION_TYPE_PATH) { + const conditionType = extractConditionType(parseOpenApiInput(input)); + const source = buildConditionTypeSource(conditionType); + await fs.writeFile(outputPath, source, 'utf8'); + return conditionType; +} + +module.exports = { + buildConditionTypeSource, + extractConditionType, + fixConditionType, +}; + +if (require.main === module) { + const fsSync = require('fs'); + + async function main() { + const inputPath = process.argv[2]; + const outputPath = process.argv[3] || CONDITION_TYPE_PATH; + + if (!inputPath) { + console.error('Usage: node fix-condition-type.js [output.ts]'); + process.exit(1); + } + + const input = fsSync.readFileSync(inputPath, 'utf8'); + const conditionType = await fixConditionType(input, outputPath); + console.log(`✅ Wrote ${conditionType.varnames.length} ConditionType entries to ${outputPath}`); + } + + main().catch((error) => { + console.error('❌ Error fixing ConditionType:', error.message); + process.exit(1); + }); +} diff --git a/libs/types/scripts/openapi-typescript.js b/libs/types/scripts/openapi-typescript.js index ac68552af7..e1f9d3cfdb 100644 --- a/libs/types/scripts/openapi-typescript.js +++ b/libs/types/scripts/openapi-typescript.js @@ -6,6 +6,7 @@ const OpenAPI = require('openapi-typescript-codegen'); const YAML = require('js-yaml'); const { rimraf, copyDir, fixCoreReferences } = require('./openapi-utils'); +const { fixConditionType } = require('./fix-condition-type'); const CORE_API = 'core'; const ALPHA_CORE_API = 'alphacore'; @@ -78,6 +79,10 @@ async function generateTypes(mode) { await rimraf(finalDir); await copyDir(output, path.resolve(__dirname, '..')); await rimraf(output); + + console.log('Fixing ConditionType enum (duplicate OpenAPI enum values)...'); + const conditionType = await fixConditionType(data); + console.log(`✅ ConditionType fixed (${conditionType.varnames.length} entries)`); } else { // Image builder and alpha types need to be fixed before they can be moved to their final location await rimraf(finalDir); From 20f26e75e0499ffcfbb033ade8eb5cddebf1aa93 Mon Sep 17 00:00:00 2001 From: Celia Amador Date: Wed, 9 Sep 2026 09:31:09 +0200 Subject: [PATCH 2/2] Add checks to prevent exploitability Made-with: Cursor --- libs/types/scripts/fix-condition-type.js | 47 ++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/libs/types/scripts/fix-condition-type.js b/libs/types/scripts/fix-condition-type.js index 8fae52fc58..efadfb8d2e 100644 --- a/libs/types/scripts/fix-condition-type.js +++ b/libs/types/scripts/fix-condition-type.js @@ -4,6 +4,41 @@ const path = require('path'); const YAML = require('js-yaml'); const CONDITION_TYPE_PATH = path.resolve(__dirname, '../models/ConditionType.ts'); +const DEFAULT_DESCRIPTION = 'Type of condition in CamelCase.'; +const TS_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/; + +function validateMemberName(name, index) { + if (typeof name !== 'string' || !TS_IDENTIFIER.test(name)) { + throw new Error(`Invalid x-enum-varnames entry at index ${index}: ${JSON.stringify(name)}`); + } +} + +function validateUniqueMemberNames(names) { + const seen = new Set(); + for (const name of names) { + if (seen.has(name)) { + throw new Error(`Duplicate x-enum-varnames entry: ${name}`); + } + seen.add(name); + } +} + +function validateEnumValue(value, index) { + if (typeof value !== 'string') { + throw new Error(`Invalid enum value at index ${index}: expected string, got ${typeof value}`); + } +} + +function sanitizeDescription(description) { + if (typeof description !== 'string' || description.trim() === '') { + return DEFAULT_DESCRIPTION; + } + + return description + .replace(/\*\//g, '* /') + .replace(/\/\*/g, '/ *') + .replace(/\r\n|\r|\n/g, ' '); +} function buildConditionTypeSource({ description, varnames, enumValues }) { if (varnames.length !== enumValues.length) { @@ -12,14 +47,20 @@ function buildConditionTypeSource({ description, varnames, enumValues }) { ); } - const members = varnames.map((name, index) => ` ${name} = '${enumValues[index]}',`).join('\n'); + varnames.forEach(validateMemberName); + validateUniqueMemberNames(varnames); + enumValues.forEach(validateEnumValue); + + const members = varnames + .map((name, index) => ` ${name} = ${JSON.stringify(enumValues[index])},`) + .join('\n'); return `/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ /** - * ${description} + * ${sanitizeDescription(description)} */ export enum ConditionType { ${members} @@ -44,7 +85,7 @@ function extractConditionType(openApiDocument) { } return { - description: schema.description || 'Type of condition in CamelCase.', + description: schema.description || DEFAULT_DESCRIPTION, varnames, enumValues, };