@runa-ai/runa-cli 0.5.31 → 0.5.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"db-rollback.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/commands/db-rollback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AASH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoNpC;;GAEG;AACH,eAAO,MAAM,eAAe,SAoBxB,CAAC"}
1
+ {"version":3,"file":"db-rollback.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/commands/db-rollback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AASH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsNpC;;GAEG;AACH,eAAO,MAAM,eAAe,SAoBxB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"db-seed-verify.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/commands/db-seed-verify.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8EpC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,SA6C3B,CAAC"}
1
+ {"version":3,"file":"db-seed-verify.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/commands/db-seed-verify.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgFpC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,SA6C3B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"db-target.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/utils/db-target.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,YAAY,CAAC;AAyBpE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAoDjE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,CAUhF"}
1
+ {"version":3,"file":"db-target.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/utils/db-target.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,YAAY,CAAC;AAyBpE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAqDjE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,CAUhF"}
@@ -23,8 +23,12 @@ export declare function detectAppSchemas(schemasDir: string, verbose: boolean):
23
23
  *
24
24
  * @param schemas Array of schema names
25
25
  * @returns Formatted string for SQL: "'public', 'accounts', 'repos'"
26
+ * @throws Error if any schema name fails validation
27
+ *
28
+ * SECURITY (Issue #535): Defense-in-depth for SQL injection prevention:
29
+ * 1. Validates each schema is a safe PostgreSQL identifier
30
+ * 2. Escapes single quotes (even though valid identifiers won't have them)
26
31
  *
27
- * Note: Escapes single quotes for SQL injection defense.
28
32
  * While schema names from detectAppSchemas() are already safe (regex \w+),
29
33
  * this provides defense-in-depth for future code paths.
30
34
  */
@@ -1 +1 @@
1
- {"version":3,"file":"schema-detector.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/utils/schema-detector.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH;;GAEG;AACH,eAAO,MAAM,gBAAgB,aAsB3B,CAAC;AAEH;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAoC/E;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAE7D"}
1
+ {"version":3,"file":"schema-detector.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/utils/schema-detector.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH;;GAEG;AACH,eAAO,MAAM,gBAAgB,aAsB3B,CAAC;AAEH;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAoC/E;AAQD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAW7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"schema-sync.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/utils/schema-sync.ts"],"names":[],"mappings":"AA6FA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC3C,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAErC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,mBAAmB,EAAE,KAAK,CAAC;QACzB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,YAAY,EAAE,MAAM,EAAE,CAAC;QACvB,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC,CAAC;CACJ;AAMD;;;;;;;;;;;GAWG;AACH,wBAAsB,2BAA2B,CAC/C,aAAa,EAAE,MAAM,EACrB,WAAW,GAAE,MAAsB,GAClC,OAAO,CAAC;IACT,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC5C,CAAC,CAoCD;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;CACpC,GACA,OAAO,CAAC;IACT,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACtC,CAAC,CAmDD;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE;IACjC,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC3C,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACtC,GAAG,QAAQ,CAuCX"}
1
+ {"version":3,"file":"schema-sync.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/utils/schema-sync.ts"],"names":[],"mappings":"AA0KA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC3C,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAErC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,mBAAmB,EAAE,KAAK,CAAC;QACzB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,YAAY,EAAE,MAAM,EAAE,CAAC;QACvB,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC,CAAC;CACJ;AAMD;;;;;;;;;;;GAWG;AACH,wBAAsB,2BAA2B,CAC/C,aAAa,EAAE,MAAM,EACrB,WAAW,GAAE,MAAsB,GAClC,OAAO,CAAC;IACT,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC5C,CAAC,CAoCD;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;CACpC,GACA,OAAO,CAAC;IACT,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACtC,CAAC,CAoDD;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE;IACjC,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC3C,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACtC,GAAG,QAAQ,CAuCX"}
@@ -1 +1 @@
1
- {"version":3,"file":"table-registry.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/utils/table-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,KAAK,EAMV,UAAU,EACV,cAAc,EAEf,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EAAyC,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAiBlG;;;;;;;;;;;GAWG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,UAAU,EAAE,CAAC,CAmDvB;AA4PD;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,UAAU,EAAE,EACvB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC;IACT,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC,CAYD;AAMD;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,UAAU,EAAE,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CA0HvB;AAMD,MAAM,WAAW,uBAAuB;IACtC,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,6DAA6D;IAC7D,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAuCD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,cAAc,CAAC,CA6GzB;AA+FD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAa7E;AAMD,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"table-registry.d.ts","sourceRoot":"","sources":["../../../../src/commands/db/utils/table-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,KAAK,EAMV,UAAU,EACV,cAAc,EAEf,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EAAyC,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AA4ElG;;;;;;;;;;;GAWG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,UAAU,EAAE,CAAC,CAmDvB;AA4PD;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,UAAU,EAAE,EACvB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC;IACT,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC,CAYD;AAMD;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,UAAU,EAAE,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CA2HvB;AAMD,MAAM,WAAW,uBAAuB;IACtC,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,6DAA6D;IAC7D,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAuCD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,cAAc,CAAC,CA6GzB;AA+FD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAa7E;AAMD,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"env-encrypt.d.ts","sourceRoot":"","sources":["../../../../src/commands/env/commands/env-encrypt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8MpC,eAAO,MAAM,cAAc,SAqBvB,CAAC"}
1
+ {"version":3,"file":"env-encrypt.d.ts","sourceRoot":"","sources":["../../../../src/commands/env/commands/env-encrypt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiOpC,eAAO,MAAM,cAAc,SAqBvB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"env-pull.d.ts","sourceRoot":"","sources":["../../../../src/commands/env/commands/env-pull.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAYH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAq5BpC,eAAO,MAAM,WAAW,SAkCpB,CAAC"}
1
+ {"version":3,"file":"env-pull.d.ts","sourceRoot":"","sources":["../../../../src/commands/env/commands/env-pull.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAaH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA88BpC,eAAO,MAAM,WAAW,SA+BpB,CAAC"}
package/dist/index.js CHANGED
@@ -5,10 +5,10 @@ import path10__default, { join, dirname, resolve, relative, basename, sep, isAbs
5
5
  import { fileURLToPath } from 'url';
6
6
  import { execSync, spawnSync, execFileSync, exec, spawn } from 'child_process';
7
7
  import * as fs6 from 'fs';
8
- import fs6__default, { existsSync, readFileSync, readdirSync, mkdtempSync, writeFileSync, mkdirSync, copyFileSync, createWriteStream, statSync, rmSync, realpathSync, promises, lstatSync, chmodSync, accessSync, constants, unlinkSync } from 'fs';
8
+ import fs6__default, { existsSync, readFileSync, readdirSync, mkdtempSync, writeFileSync, mkdirSync, copyFileSync, createWriteStream, statSync, rmSync, realpathSync, promises, lstatSync, accessSync, constants, chmodSync, unlinkSync } from 'fs';
9
9
  import { createCLILogger, cacheClear, CacheClearOutputSchema, CLIError, cachePrune, CachePruneOutputSchema, cacheStats, CacheStatsOutputSchema, cacheList, CacheListOutputSchema, cacheInvalidate, CacheInvalidateOutputSchema, syncFromProduction, dbGenerateDiagram, DbDiagramGenerateOutputSchema, createDbSnapshot, syncDatabase, emitDbPushFailureCapsule, emitDbAnnotations, writeDbPushStepSummary, exportDbReportJson, DbSyncOutputSchema, databasePaths, detectRequiredServices, formatDetectionResults, dbStart, DbLifecycleStartOutputSchema, dbStop, DbLifecycleStopOutputSchema, dbReset, DbLifecycleResetOutputSchema, dbValidateSchemas, DbSchemaValidateOutputSchema, DbSchemaRisksOutputSchema, dbDetectSchemaRisks, dbApplySchemas, DbSchemaApplyOutputSchema, dbGenerateTypes, DbSchemaGenerateOutputSchema, extractSchemaFilter, dbSeedInit, DbSeedInitOutputSchema, dbSeedValidate, DbSeedValidateOutputSchema, dbSeedGenerate, DbSeedGenerateOutputSchema, dbVerifySeeds, DbSeedVerifyOutputSchema, DbSnapshotCreateOutputSchema, restoreDbSnapshot, DbSnapshotRestoreOutputSchema, listDbSnapshots, DbSnapshotListOutputSchema, dbGeneratePgTapTests, DbTestGenOutputSchema, dbUpdateGoldenRecord, DbTestUpdateGoldenOutputSchema, repairRunaConfig, detectExistingInitConfig, initProject, validateInitResult, linkCliGlobally, LinkCliOutputSchema, unlinkCliGlobally, UnlinkCliOutputSchema, checkRepoStatus, CheckRepoStatusOutputSchema, enableTelemetry, disableTelemetry, getTelemetryStatus, uploadTelemetry, TelemetryUploadOutputSchema, runTest, TestRunOutputSchema, runTestService, TestServiceOutputSchema, runTestIntegration, TestIntegrationOutputSchema, runTestStatic, TestStaticOutputSchema, generateOwaspTop10Tests, TestOwaspGenerateOutputSchema, updateGoldenRecord, generateE2ETests, generateSecurityTests, generateUnitTests, generateApiTests, generateComponentTests, generateE2EScaffold, validateConfig, ValidateConfigOutputSchema, deploySchemaToProduction, WorkflowNotifyOutputSchema, devopsSync, workflowSync, validateInfrastructure, emitWorkflowValidateFailureCapsule, emitWorkflowAnnotations, writeWorkflowValidateStepSummary, exportWorkflowReportJson, WorkflowValidateInfrastructureOutputSchema, createSuccessEnvelopeSchema, CLI_CONTRACT_VERSION, runChecks, RunCheckOutputSchema, formatDuration as formatDuration$1, GITHUB_API, loadRunaConfig, getClassificationForProfile, recordSchemaAudit, RecordSchemaAuditOutputSchema, createBackup, CreateBackupOutputSchema, listBackups, ListBackupsOutputSchema, getBackupMetadata, restoreBackup, RestoreBackupOutputSchema, deleteBackup, DeleteBackupOutputSchema, detectSchemaNames, SUPABASE_SYSTEM_SCHEMAS, loadRunaConfigOrThrow, dbSeedApply, writeDbSeedStepSummary, DbSeedApplyOutputSchema, emitDbSeedFailureCapsule, syncEnvironment, EnvSyncOutputSchema, detectDatabasePackage, findProjectRoot as findProjectRoot$1, TelemetryEnableOutputSchema, TelemetryDisableOutputSchema, TelemetryStatusOutputSchema, workflowNotify, DevOpsSyncOutputSchema, WorkflowSyncOutputSchema, formatCLIError, DATABASE_PACKAGE_CANDIDATES, getStatusIcon as getStatusIcon$1, findWorkspaceRoot as findWorkspaceRoot$1, checkExtensionConfig, UpgradeTransaction, readRunaVersion, syncTemplates, SyncOutputSchema, ErrorEnvelopeSchema, preCheckSync, findConflictFiles, TestUnitGenOutputSchema, TestE2EGenerateOutputSchema, TestSecurityGenOutputSchema, TestApiGenOutputSchema, TestComponentGenOutputSchema } from '@runa-ai/runa';
10
10
  import { z } from 'zod';
11
- import fs10, { mkdir, writeFile, appendFile, readFile, rm, stat, realpath, cp, readdir } from 'fs/promises';
11
+ import fs10, { mkdir, writeFile, appendFile, readFile, rm, stat, realpath, cp, readdir, lstat } from 'fs/promises';
12
12
  import { promisify } from 'util';
13
13
  import { glob } from 'glob';
14
14
  import { Project, Node, SyntaxKind } from 'ts-morph';
@@ -925,7 +925,7 @@ var CLI_VERSION, HAS_ADMIN_COMMAND;
925
925
  var init_version = __esm({
926
926
  "src/version.ts"() {
927
927
  init_esm_shims();
928
- CLI_VERSION = "0.5.31";
928
+ CLI_VERSION = "0.5.32";
929
929
  HAS_ADMIN_COMMAND = false;
930
930
  }
931
931
  });
@@ -3030,8 +3030,50 @@ var init_typescript_analyzer = __esm({
3030
3030
  };
3031
3031
  }
3032
3032
  });
3033
+ function containsPathTraversal4(inputPath) {
3034
+ const normalized = path10__default.normalize(inputPath);
3035
+ if (normalized.includes("..")) return true;
3036
+ if (inputPath.includes("\0")) return true;
3037
+ return false;
3038
+ }
3039
+ function isPathWithinBoundary(filePath, boundaryDir) {
3040
+ try {
3041
+ const resolvedFile = path10__default.resolve(filePath);
3042
+ const resolvedBoundary = path10__default.resolve(boundaryDir);
3043
+ const relative8 = path10__default.relative(resolvedBoundary, resolvedFile);
3044
+ return !relative8.startsWith("..") && !path10__default.isAbsolute(relative8);
3045
+ } catch {
3046
+ return false;
3047
+ }
3048
+ }
3049
+ function resolvePathWithinBoundary(inputPath, boundaryDir) {
3050
+ if (containsPathTraversal4(inputPath)) {
3051
+ throw new Error(`Path contains traversal patterns: ${inputPath}`);
3052
+ }
3053
+ const resolvedPath = path10__default.isAbsolute(inputPath) ? inputPath : path10__default.resolve(boundaryDir, inputPath);
3054
+ if (!isPathWithinBoundary(resolvedPath, boundaryDir)) {
3055
+ throw new Error(`Path is outside allowed boundary: ${inputPath}`);
3056
+ }
3057
+ return resolvedPath;
3058
+ }
3059
+ function filterPathsWithinBoundary(files, boundaryDir) {
3060
+ const resolvedBoundary = path10__default.resolve(boundaryDir);
3061
+ return files.filter((file) => isPathWithinBoundary(file, resolvedBoundary));
3062
+ }
3063
+ function validateGlobPatterns(patterns) {
3064
+ for (const pattern of patterns) {
3065
+ if (containsPathTraversal4(pattern)) {
3066
+ throw new Error(`Glob pattern contains path traversal: ${pattern}`);
3067
+ }
3068
+ }
3069
+ }
3070
+ var init_path_validation = __esm({
3071
+ "src/internal/vuln-checker/security/path-validation.ts"() {
3072
+ init_esm_shims();
3073
+ }
3074
+ });
3033
3075
  async function loadConfig(configPath, rootDir) {
3034
- const fullPath = path10__default.isAbsolute(configPath) ? configPath : path10__default.join(rootDir, configPath);
3076
+ const fullPath = resolvePathWithinBoundary(configPath, rootDir);
3035
3077
  try {
3036
3078
  const content = await fs10.readFile(fullPath, "utf-8");
3037
3079
  if (fullPath.endsWith(".yml") || fullPath.endsWith(".yaml")) {
@@ -3046,7 +3088,7 @@ async function loadConfig(configPath, rootDir) {
3046
3088
  }
3047
3089
  }
3048
3090
  async function loadIgnores(ignorePath, rootDir) {
3049
- const fullPath = path10__default.isAbsolute(ignorePath) ? ignorePath : path10__default.join(rootDir, ignorePath);
3091
+ const fullPath = resolvePathWithinBoundary(ignorePath, rootDir);
3050
3092
  try {
3051
3093
  const content = await fs10.readFile(fullPath, "utf-8");
3052
3094
  let parsed;
@@ -3072,6 +3114,7 @@ async function loadIgnores(ignorePath, rootDir) {
3072
3114
  var init_loader = __esm({
3073
3115
  "src/internal/vuln-checker/config/loader.ts"() {
3074
3116
  init_esm_shims();
3117
+ init_path_validation();
3075
3118
  }
3076
3119
  });
3077
3120
  function normalizePath(filePath) {
@@ -3661,6 +3704,7 @@ var init_vuln_checker = __esm({
3661
3704
  init_json_reporter();
3662
3705
  init_markdown_reporter();
3663
3706
  init_sarif_reporter();
3707
+ init_path_validation();
3664
3708
  init_types2();
3665
3709
  SEVERITY_ORDER2 = {
3666
3710
  critical: 5,
@@ -3748,11 +3792,16 @@ var init_vuln_checker = __esm({
3748
3792
  return result;
3749
3793
  }
3750
3794
  async getFiles() {
3751
- return glob(this.options.include, {
3795
+ validateGlobPatterns(this.options.include);
3796
+ if (this.options.exclude) {
3797
+ validateGlobPatterns(this.options.exclude);
3798
+ }
3799
+ const files = await glob(this.options.include, {
3752
3800
  cwd: this.options.rootDir,
3753
3801
  ignore: this.options.exclude,
3754
3802
  absolute: true
3755
3803
  });
3804
+ return filterPathsWithinBoundary(files, this.options.rootDir);
3756
3805
  }
3757
3806
  async runAnalyzers(inlineAnnotations) {
3758
3807
  const findings = [];
@@ -5343,6 +5392,26 @@ function isExecutable(filePath) {
5343
5392
  return false;
5344
5393
  }
5345
5394
  }
5395
+ function resolveAndValidateSymlink(filePath) {
5396
+ try {
5397
+ const lstats = lstatSync(filePath);
5398
+ if (lstats.isSymbolicLink()) {
5399
+ const realPath = realpathSync(filePath);
5400
+ const realDir = dirname(realPath);
5401
+ if (!isTrustedDirectory(realDir)) {
5402
+ return null;
5403
+ }
5404
+ const realStats = statSync(realPath);
5405
+ if (!realStats.isFile()) {
5406
+ return null;
5407
+ }
5408
+ return realPath;
5409
+ }
5410
+ return filePath;
5411
+ } catch {
5412
+ return null;
5413
+ }
5414
+ }
5346
5415
  function isTrustedDirectory(dir) {
5347
5416
  const normalizedDir = normalize(dir).replace(/[/\\]$/, "");
5348
5417
  if (process.platform === "win32") {
@@ -5350,7 +5419,7 @@ function isTrustedDirectory(dir) {
5350
5419
  }
5351
5420
  for (const trusted of TRUSTED_DIRECTORIES_UNIX) {
5352
5421
  const normalizedTrusted = normalize(trusted).replace(/[/\\]$/, "");
5353
- if (normalizedDir === normalizedTrusted || normalizedDir.startsWith(normalizedTrusted + "/")) {
5422
+ if (normalizedDir === normalizedTrusted || normalizedDir.startsWith(`${normalizedTrusted}/`)) {
5354
5423
  return true;
5355
5424
  }
5356
5425
  }
@@ -5396,8 +5465,9 @@ function resolveBinaryPath(binaryName) {
5396
5465
  const isExpired = now - cached.resolvedAt >= CACHE_TTL_MS;
5397
5466
  const pathChanged = cached.pathFingerprint !== currentPathFingerprint;
5398
5467
  if (!isExpired && !pathChanged) {
5399
- if (isExecutable(cached.path)) {
5400
- return cached.path;
5468
+ const validatedPath = resolveAndValidateSymlink(cached.path);
5469
+ if (validatedPath && isExecutable(validatedPath)) {
5470
+ return validatedPath;
5401
5471
  }
5402
5472
  binaryPathCache.delete(binaryName);
5403
5473
  } else {
@@ -5410,19 +5480,31 @@ function resolveBinaryPath(binaryName) {
5410
5480
  `Binary '${binaryName}' not found in trusted PATH directories. Ensure ${binaryName} is installed in a system directory.`
5411
5481
  );
5412
5482
  }
5413
- if (!isExecutable(resolvedPath)) {
5414
- throw new Error(`Binary '${binaryName}' at '${resolvedPath}' is not executable.`);
5483
+ const realPath = resolveAndValidateSymlink(resolvedPath);
5484
+ if (!realPath) {
5485
+ throw new Error(
5486
+ `Security: Binary '${binaryName}' at '${resolvedPath}' is a symlink pointing outside trusted directories. This is not allowed for security reasons.`
5487
+ );
5488
+ }
5489
+ if (!isExecutable(realPath)) {
5490
+ throw new Error(`Binary '${binaryName}' at '${realPath}' is not executable.`);
5415
5491
  }
5416
5492
  binaryPathCache.set(binaryName, {
5417
- path: resolvedPath,
5493
+ path: realPath,
5418
5494
  resolvedAt: now,
5419
5495
  pathFingerprint: currentPathFingerprint
5420
5496
  });
5421
- return resolvedPath;
5497
+ return realPath;
5422
5498
  }
5423
5499
  function secureExeca(binaryName, args = [], options) {
5424
5500
  const resolvedPath = resolveBinaryPath(binaryName);
5425
- return execa(resolvedPath, args, { ...options, shell: false });
5501
+ const finalPath = resolveAndValidateSymlink(resolvedPath);
5502
+ if (!finalPath || !isExecutable(finalPath)) {
5503
+ throw new Error(
5504
+ `Security: Binary '${binaryName}' validation failed just before execution. The file may have been modified.`
5505
+ );
5506
+ }
5507
+ return execa(finalPath, args, { ...options, shell: false });
5426
5508
  }
5427
5509
  function securePnpm(args = [], options) {
5428
5510
  return secureExeca("pnpm", args, options);
@@ -7115,9 +7197,9 @@ function printSummary(logger15, output3) {
7115
7197
  }
7116
7198
  function findRepoRoot(startDir) {
7117
7199
  const { existsSync: existsSync49, readFileSync: readFileSync27 } = __require("fs");
7118
- const { join: join22, dirname: dirname4 } = __require("path");
7200
+ const { join: join22, dirname: dirname5 } = __require("path");
7119
7201
  let current = startDir;
7120
- while (current !== dirname4(current)) {
7202
+ while (current !== dirname5(current)) {
7121
7203
  if (existsSync49(join22(current, "turbo.json"))) {
7122
7204
  return current;
7123
7205
  }
@@ -7131,7 +7213,7 @@ function findRepoRoot(startDir) {
7131
7213
  } catch {
7132
7214
  }
7133
7215
  }
7134
- current = dirname4(current);
7216
+ current = dirname5(current);
7135
7217
  }
7136
7218
  return startDir;
7137
7219
  }
@@ -9279,8 +9361,14 @@ function detectAppSchemas(schemasDir, verbose) {
9279
9361
  }
9280
9362
  return result;
9281
9363
  }
9364
+ var VALID_PG_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_]{0,62}$/;
9282
9365
  function formatSchemasForSql(schemas) {
9283
- return schemas.map((s) => `'${s.replace(/'/g, "''")}'`).join(", ");
9366
+ return schemas.map((s) => {
9367
+ if (!VALID_PG_IDENTIFIER.test(s)) {
9368
+ throw new Error(`Invalid schema name "${s}": must be a valid PostgreSQL identifier`);
9369
+ }
9370
+ return `'${s.replace(/'/g, "''")}'`;
9371
+ }).join(", ");
9284
9372
  }
9285
9373
  async function detectStack(repoRoot, tmpDir, productionDbUrlAdmin) {
9286
9374
  const res = await runLogged({
@@ -16722,7 +16810,8 @@ function resolveDatabaseUrl(environment) {
16722
16810
  if (!isLocalDatabaseUrl(url)) {
16723
16811
  throw new CLIError("Local database URL appears to be a remote URL", "LOCAL_DB_URL_REMOTE", [
16724
16812
  "Local operations should use the local Supabase instance",
16725
- `Current URL: ${url.substring(0, 50)}...`,
16813
+ // SECURITY (Issue #513): Redact credentials from database URL in error message
16814
+ `Current URL: ${redactSecrets(url)}`,
16726
16815
  "If you need to connect to a remote DB, use --env preview or --env production"
16727
16816
  ]);
16728
16817
  }
@@ -19344,6 +19433,34 @@ init_config_loader();
19344
19433
 
19345
19434
  // src/commands/db/utils/schema-sync.ts
19346
19435
  init_esm_shims();
19436
+ var VALID_PG_IDENTIFIER_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]{0,62}$/;
19437
+ function validatePgIdentifier(name, context) {
19438
+ if (!name || typeof name !== "string") {
19439
+ throw new Error(`Invalid ${context}: empty or not a string`);
19440
+ }
19441
+ if (!VALID_PG_IDENTIFIER_PATTERN.test(name)) {
19442
+ throw new Error(
19443
+ `Invalid ${context} "${name}": must start with letter/underscore and contain only alphanumeric/underscore characters`
19444
+ );
19445
+ }
19446
+ }
19447
+ function escapePgStringLiteral(value) {
19448
+ if (typeof value !== "string") {
19449
+ throw new Error("Value must be a string");
19450
+ }
19451
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "''");
19452
+ }
19453
+ function buildSafeSchemaInClause(schemas) {
19454
+ if (schemas.length === 0) {
19455
+ throw new Error("No schemas provided for IN clause");
19456
+ }
19457
+ const safeSchemas = [];
19458
+ for (const schema of schemas) {
19459
+ validatePgIdentifier(schema, "schema name");
19460
+ safeSchemas.push(`'${escapePgStringLiteral(schema)}'`);
19461
+ }
19462
+ return safeSchemas.join(",");
19463
+ }
19347
19464
  var ERROR_MESSAGES2 = {
19348
19465
  PATH_TRAVERSAL: "Schema path validation failed",
19349
19466
  SCHEMA_NOT_FOUND: "Schema file not found"
@@ -19419,7 +19536,7 @@ async function fetchDbTablesAndEnums(databaseUrl, options) {
19419
19536
  ...options?.additionalSystemSchemas ?? []
19420
19537
  ]);
19421
19538
  const filteredManagedSchemas = managedSchemas.filter((s) => !systemSchemas.has(s));
19422
- const schemaList = filteredManagedSchemas.map((s) => `'${s}'`).join(",");
19539
+ const schemaList = buildSafeSchemaInClause(filteredManagedSchemas);
19423
19540
  const tablesSql = `
19424
19541
  SELECT schemaname || '.' || tablename
19425
19542
  FROM pg_tables
@@ -23091,7 +23208,7 @@ async function runRollbackAction(environment, options) {
23091
23208
  logger15.section("Schema Rollback");
23092
23209
  logger15.info(`Environment: ${environment}`);
23093
23210
  logger15.info(`Snapshot: ${snapshotFile}`);
23094
- logger15.info(`Database: ${databaseUrl.slice(0, 50)}...`);
23211
+ logger15.info(`Database: ${redactSecrets(databaseUrl)}`);
23095
23212
  if (options.dryRun) {
23096
23213
  logger15.info("\n[DRY RUN] Would restore from snapshot");
23097
23214
  logger15.info("Re-run without --dry-run to apply");
@@ -23825,7 +23942,7 @@ function renderSeedVerifyText(params) {
23825
23942
  const { logger: logger15, env: env2, dbUrl, result } = params;
23826
23943
  logger15.section("Seed Data Verification");
23827
23944
  logger15.info(`Environment: ${env2}`);
23828
- logger15.info(`Database: ${dbUrl.substring(0, 50)}...`);
23945
+ logger15.info(`Database: ${redactSecrets(dbUrl)}`);
23829
23946
  logger15.info("");
23830
23947
  logger15.step("Checking data counts", 1);
23831
23948
  for (const r of result.results) {
@@ -25069,7 +25186,12 @@ var VERCEL_ENVIRONMENTS = ["development", "preview", "production"];
25069
25186
  function isAlreadyEncrypted(filePath) {
25070
25187
  if (!existsSync(filePath)) return false;
25071
25188
  const content = readFileSync(filePath, "utf-8");
25072
- return content.includes("DOTENV_PUBLIC_KEY");
25189
+ const hasPublicKeyDeclaration = /^DOTENV_PUBLIC_KEY_\w+\s*=\s*["']?[A-Za-z0-9+/=]+/m.test(
25190
+ content
25191
+ );
25192
+ if (!hasPublicKeyDeclaration) return false;
25193
+ const hasEncryptedValue = /^[A-Z_][A-Z0-9_]*\s*=\s*["']?encrypted:/m.test(content);
25194
+ return hasEncryptedValue;
25073
25195
  }
25074
25196
  async function encryptFile(workDir, environment, logger15, options) {
25075
25197
  const fileName = `.env.${environment}`;
@@ -25423,6 +25545,31 @@ var ERROR_MESSAGES3 = {
25423
25545
  PATH_TRAVERSAL: "Working directory path validation failed",
25424
25546
  APP_NOT_FOUND: "App directory not found"
25425
25547
  };
25548
+ function sanitizeErrorMessage(message) {
25549
+ if (!message || typeof message !== "string") {
25550
+ return "Unknown error";
25551
+ }
25552
+ let sanitized = message;
25553
+ sanitized = sanitized.replace(
25554
+ /\b(vercel_|vc_|VERCEL_TOKEN[=:]\s*)[a-zA-Z0-9_-]{20,}/gi,
25555
+ "$1[REDACTED]"
25556
+ );
25557
+ sanitized = sanitized.replace(/\b(Bearer\s+)[a-zA-Z0-9._-]{20,}/gi, "$1[REDACTED]");
25558
+ sanitized = sanitized.replace(
25559
+ /\b(token[=:]\s*["']?)[a-zA-Z0-9._-]{20,}(["']?)/gi,
25560
+ "$1[REDACTED]$2"
25561
+ );
25562
+ sanitized = sanitized.replace(
25563
+ /\b(api[_-]?key[=:]\s*["']?)[a-zA-Z0-9._-]{20,}(["']?)/gi,
25564
+ "$1[REDACTED]$2"
25565
+ );
25566
+ sanitized = sanitized.replace(
25567
+ /\b(Authorization[=:]\s*["']?)[a-zA-Z0-9\s._-]{20,}(["']?)/gi,
25568
+ "$1[REDACTED]$2"
25569
+ );
25570
+ sanitized = sanitized.replace(/:\/\/[^@\s]+@/g, "://[CREDENTIALS]@");
25571
+ return sanitized;
25572
+ }
25426
25573
  function containsPathTraversal3(inputPath) {
25427
25574
  const normalized = path10__default.normalize(inputPath);
25428
25575
  return normalized.includes("..") || inputPath.includes("\0");
@@ -25606,6 +25753,7 @@ function extractAndSavePrivateKeys(workDir, envFiles, logger15) {
25606
25753
  ].join("\n");
25607
25754
  const keysFile = resolve(workDir, ".env.keys");
25608
25755
  writeFileSync(keysFile, keysContent, "utf-8");
25756
+ chmodSync(keysFile, 384);
25609
25757
  logger15.success(` \u{1F511} Extracted ${Object.keys(privateKeys).length} keys \u2192 .env.keys`);
25610
25758
  return true;
25611
25759
  }
@@ -25687,7 +25835,7 @@ async function encryptFile2(workDir, filePath, logger15) {
25687
25835
  return true;
25688
25836
  } catch (error) {
25689
25837
  const message = error instanceof Error ? error.message : "Unknown error";
25690
- logger15.warn(` \u26A0\uFE0F Encryption failed: ${message}`);
25838
+ logger15.warn(` \u26A0\uFE0F Encryption failed: ${sanitizeErrorMessage(message)}`);
25691
25839
  return false;
25692
25840
  }
25693
25841
  }
@@ -25701,6 +25849,7 @@ function buildVercelCmdEnv(auth) {
25701
25849
  return cmdEnv;
25702
25850
  }
25703
25851
  function mapVercelError(message, environment, outputPath, logger15) {
25852
+ const safeMessage = sanitizeErrorMessage(message);
25704
25853
  if (message.includes("not linked")) {
25705
25854
  logger15.error(` \u2717 ${environment}: Project not linked to Vercel`);
25706
25855
  return {
@@ -25728,8 +25877,8 @@ function mapVercelError(message, environment, outputPath, logger15) {
25728
25877
  error: "Invalid VERCEL_TOKEN. Check your token is valid."
25729
25878
  };
25730
25879
  }
25731
- logger15.error(` \u2717 ${environment}: ${message}`);
25732
- return { environment, outputPath, success: false, error: message };
25880
+ logger15.error(` \u2717 ${environment}: ${safeMessage}`);
25881
+ return { environment, outputPath, success: false, error: safeMessage };
25733
25882
  }
25734
25883
  async function pullEnvironment(workDir, environment, auth, logger15, options = {}) {
25735
25884
  const outputPath = getOutputPath(workDir, environment);
@@ -25942,16 +26091,12 @@ var pullCommand = new Command("pull").description("Pull all environments from Ve
25942
26091
  await runEnvPullAction(options);
25943
26092
  } catch (error) {
25944
26093
  if (error instanceof CLIError) throw error;
25945
- throw new CLIError(
25946
- error instanceof Error ? error.message : "Environment pull failed",
25947
- "ENV_PULL_FAILED",
25948
- [
25949
- "Check Vercel CLI is installed: vercel --version",
25950
- "Ensure project is linked: vercel link",
25951
- "Check network connectivity"
25952
- ],
25953
- error instanceof Error ? error : void 0
25954
- );
26094
+ const rawMessage = error instanceof Error ? error.message : "Environment pull failed";
26095
+ throw new CLIError(sanitizeErrorMessage(rawMessage), "ENV_PULL_FAILED", [
26096
+ "Check Vercel CLI is installed: vercel --version",
26097
+ "Ensure project is linked: vercel link",
26098
+ "Check network connectivity"
26099
+ ]);
25955
26100
  }
25956
26101
  });
25957
26102
 
@@ -29361,6 +29506,29 @@ async function installTemplates(tempDir, authToken, verbose) {
29361
29506
  ]);
29362
29507
  }
29363
29508
  }
29509
+ async function verifyNoSymlinks(dir, baseDir) {
29510
+ const entries = await readdir(dir, { withFileTypes: true });
29511
+ for (const entry of entries) {
29512
+ const fullPath = path10__default.join(dir, entry.name);
29513
+ const stats = await lstat(fullPath);
29514
+ if (stats.isSymbolicLink()) {
29515
+ const relativePath = path10__default.relative(baseDir, fullPath);
29516
+ throw new CLIError(
29517
+ "Security: Symlink detected in template package.",
29518
+ "SYMLINK_ATTACK_DETECTED",
29519
+ [
29520
+ `Symlink found: ${relativePath}`,
29521
+ "This could be an attempt to access files outside the template directory.",
29522
+ "The template package may be malicious or corrupted.",
29523
+ "Please report this issue if you believe this is a false positive."
29524
+ ]
29525
+ );
29526
+ }
29527
+ if (stats.isDirectory()) {
29528
+ await verifyNoSymlinks(fullPath, baseDir);
29529
+ }
29530
+ }
29531
+ }
29364
29532
  async function copyToCache(tempDir, cacheDir) {
29365
29533
  const sourceTemplates = path10__default.join(tempDir, "node_modules", TEMPLATES_PACKAGE_NAME, "templates");
29366
29534
  if (!fs6__default.existsSync(sourceTemplates)) {
@@ -29370,9 +29538,13 @@ async function copyToCache(tempDir, cacheDir) {
29370
29538
  "Try running with --fresh to re-download."
29371
29539
  ]);
29372
29540
  }
29541
+ await verifyNoSymlinks(sourceTemplates, sourceTemplates);
29373
29542
  await mkdir(cacheDir, { recursive: true });
29374
29543
  const targetTemplates = path10__default.join(cacheDir, "templates");
29375
- await cp(sourceTemplates, targetTemplates, { recursive: true });
29544
+ await cp(sourceTemplates, targetTemplates, {
29545
+ recursive: true,
29546
+ dereference: false
29547
+ });
29376
29548
  }
29377
29549
  async function fetchTemplates(options = {}) {
29378
29550
  const version = options.version ?? COMPATIBLE_TEMPLATES_VERSION;
@@ -33431,9 +33603,9 @@ function printActionsNeeded(logger15, actions) {
33431
33603
  }
33432
33604
  function findRepoRoot3(startDir) {
33433
33605
  const { existsSync: existsSync49, readFileSync: readFileSync27 } = __require("fs");
33434
- const { join: join22, dirname: dirname4 } = __require("path");
33606
+ const { join: join22, dirname: dirname5 } = __require("path");
33435
33607
  let current = startDir;
33436
- while (current !== dirname4(current)) {
33608
+ while (current !== dirname5(current)) {
33437
33609
  if (existsSync49(join22(current, "turbo.json"))) {
33438
33610
  return current;
33439
33611
  }
@@ -33447,7 +33619,7 @@ function findRepoRoot3(startDir) {
33447
33619
  } catch {
33448
33620
  }
33449
33621
  }
33450
- current = dirname4(current);
33622
+ current = dirname5(current);
33451
33623
  }
33452
33624
  return startDir;
33453
33625
  }
@@ -37146,8 +37318,8 @@ async function checkLicense() {
37146
37318
  }
37147
37319
  const result = await resolveGitHubOwner();
37148
37320
  if (!result?.owner) {
37149
- logger14.debug("[license] Could not determine owner, skipping check");
37150
- return { allowed: true, reason: "error" };
37321
+ logger14.warn("[license] Could not determine repository owner, denying access");
37322
+ return { allowed: false, reason: "owner-resolution-failed" };
37151
37323
  }
37152
37324
  const owner = result.owner;
37153
37325
  if (isTrustedOrg(owner)) {
@@ -3,14 +3,24 @@
3
3
  *
4
4
  * Purpose: Load and parse configuration files
5
5
  * Supports: YAML and JSON formats
6
+ *
7
+ * Security:
8
+ * - Path validation to prevent path traversal attacks (Issue #537)
9
+ * - Config/ignore files must be within rootDir boundary
6
10
  */
7
11
  import type { IgnoreRule, VulnCheckerConfig } from '../types.js';
8
12
  /**
9
13
  * Load configuration file
14
+ *
15
+ * SECURITY (Issue #537): Validates that configPath is within rootDir boundary
16
+ * to prevent path traversal attacks.
10
17
  */
11
18
  export declare function loadConfig(configPath: string, rootDir: string): Promise<VulnCheckerConfig>;
12
19
  /**
13
20
  * Load ignore file
21
+ *
22
+ * SECURITY (Issue #537): Validates that ignorePath is within rootDir boundary
23
+ * to prevent path traversal attacks.
14
24
  */
15
25
  export declare function loadIgnores(ignorePath: string, rootDir: string): Promise<IgnoreRule[]>;
16
26
  //# sourceMappingURL=loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../../src/internal/vuln-checker/config/loader.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEjE;;GAEG;AACH,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAmBhG;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CA8B5F"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../../src/internal/vuln-checker/config/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEjE;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAoBhG;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CA+B5F"}
@@ -3,6 +3,10 @@
3
3
  *
4
4
  * Purpose: Comprehensive security scanning for TypeScript/PostgreSQL projects
5
5
  * Philosophy: AST-based analysis + pattern matching + false positive management
6
+ *
7
+ * Security:
8
+ * - Path validation to prevent path traversal attacks (Issue #537)
9
+ * - Glob patterns and file lists are validated to stay within rootDir
6
10
  */
7
11
  import type { Category, ScanResult, Severity } from './types.js';
8
12
  export * from './types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/internal/vuln-checker/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,OAAO,KAAK,EAGV,QAAQ,EAKR,UAAU,EACV,QAAQ,EAET,MAAM,YAAY,CAAC;AAEpB,cAAc,YAAY,CAAC;AAE3B,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;IACnD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AA6ED,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,SAAS,CAAwB;IACzC,OAAO,CAAC,MAAM,CAAmD;gBAErD,OAAO,EAAE,kBAAkB;IA6CjC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IASrB,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;YA6BnB,QAAQ;YAQR,YAAY;IA2B1B,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,gBAAgB;IAQxB,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM;IAQ5B,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;IAK5B,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,GAAE,QAAiB,GAAG,MAAM;IAWlE,+CAA+C;IAC/C,SAAS,IAAI,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;CAGzD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/internal/vuln-checker/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAcH,OAAO,KAAK,EAGV,QAAQ,EAKR,UAAU,EACV,QAAQ,EAET,MAAM,YAAY,CAAC;AAEpB,cAAc,YAAY,CAAC;AAE3B,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;IACnD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AA6ED,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,SAAS,CAAwB;IACzC,OAAO,CAAC,MAAM,CAAmD;gBAErD,OAAO,EAAE,kBAAkB;IA6CjC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IASrB,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;YA6BnB,QAAQ;YAkBR,YAAY;IA2B1B,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,gBAAgB;IAQxB,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM;IAQ5B,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;IAK5B,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,GAAE,QAAiB,GAAG,MAAM;IAWlE,+CAA+C;IAC/C,SAAS,IAAI,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;CAGzD"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * AI HINT: Path Validation Security for Vulnerability Checker
3
+ *
4
+ * Purpose: Prevent path traversal attacks in vulnerability checker
5
+ * Features:
6
+ * - Validates paths are within allowed boundaries
7
+ * - Detects path traversal patterns (../, etc.)
8
+ * - Provides reusable validation functions
9
+ *
10
+ * @see Issue #537 - Path traversal in vulnerability checker
11
+ */
12
+ /**
13
+ * SECURITY (Issue #537): Validate that a path doesn't contain traversal patterns.
14
+ *
15
+ * Checks for patterns that could escape the intended directory:
16
+ * - .. (parent directory)
17
+ * - Null bytes (\0)
18
+ *
19
+ * @param inputPath - Path to validate
20
+ * @returns true if path contains traversal patterns
21
+ */
22
+ export declare function containsPathTraversal(inputPath: string): boolean;
23
+ /**
24
+ * SECURITY (Issue #537): Validate that a resolved path is within the allowed boundary.
25
+ *
26
+ * @param filePath - Path to validate (absolute or relative)
27
+ * @param boundaryDir - Boundary directory (e.g., rootDir)
28
+ * @returns true if the path is within the boundary
29
+ */
30
+ export declare function isPathWithinBoundary(filePath: string, boundaryDir: string): boolean;
31
+ /**
32
+ * SECURITY (Issue #537): Validate and resolve a path safely within boundary.
33
+ *
34
+ * @param inputPath - User-provided path (may be relative or absolute)
35
+ * @param boundaryDir - Boundary directory that path must be within
36
+ * @returns Resolved absolute path
37
+ * @throws Error if path is outside boundary or invalid
38
+ */
39
+ export declare function resolvePathWithinBoundary(inputPath: string, boundaryDir: string): string;
40
+ /**
41
+ * SECURITY (Issue #537): Validate and resolve a path with symlink resolution.
42
+ *
43
+ * This is more secure as it resolves symlinks to detect symlink-based escapes.
44
+ *
45
+ * @param inputPath - User-provided path
46
+ * @param boundaryDir - Boundary directory
47
+ * @returns Resolved real path
48
+ * @throws Error if path is outside boundary, invalid, or doesn't exist
49
+ */
50
+ export declare function resolveRealPathWithinBoundary(inputPath: string, boundaryDir: string): string;
51
+ /**
52
+ * SECURITY (Issue #537): Filter file list to only include paths within boundary.
53
+ *
54
+ * @param files - Array of file paths (from glob or similar)
55
+ * @param boundaryDir - Boundary directory
56
+ * @returns Array of files that are within the boundary
57
+ */
58
+ export declare function filterPathsWithinBoundary(files: string[], boundaryDir: string): string[];
59
+ /**
60
+ * SECURITY (Issue #537): Validate glob patterns don't contain traversal.
61
+ *
62
+ * @param patterns - Array of glob patterns
63
+ * @throws Error if any pattern contains traversal
64
+ */
65
+ export declare function validateGlobPatterns(patterns: string[]): void;
66
+ //# sourceMappingURL=path-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-validation.d.ts","sourceRoot":"","sources":["../../../../src/internal/vuln-checker/security/path-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAWhE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAgBnF;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAiBxF;AAED;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAsB5F;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAGxF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAM7D"}
@@ -7,15 +7,17 @@
7
7
  * Design decisions:
8
8
  * - r06-dev: Instant allow, NO API call, NO log (zero-impact)
9
9
  * - External org: API check via allowlist with last-known-good fallback
10
- * - Fail-open strategy:
10
+ * - Fail-closed strategy (Issue #542):
11
+ * - Owner resolution failure: Deny access (prevents bypass via manipulation)
11
12
  * - API available: Use live response
12
13
  * - API error + cached: Use last-known-good (24h window)
13
- * - API error + no cache: Fail closed (new orgs blocked during outage)
14
- * - Escape hatch: RUNA_SKIP_LICENSE_CHECK=1 bypasses all checks
14
+ * - API error + no cache: Deny access (new orgs blocked during outage)
15
+ * - Escape hatch: RUNA_SKIP_LICENSE_CHECK=1 bypasses all checks (local only)
15
16
  *
16
17
  * Security model:
18
+ * - Fail-closed: Any resolution/validation error denies access
17
19
  * - Known orgs protected during outages (last-known-good)
18
- * - Unknown orgs cannot bypass by causing outages (fail closed)
20
+ * - Unknown orgs cannot bypass by causing errors (fail closed)
19
21
  */
20
22
  import type { LicenseCheckResult } from './types.js';
21
23
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/license/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAOH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAerD;;;;GAIG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAoDhE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAMxD;AAGD,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/license/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAOH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAerD;;;;GAIG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAqDhE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAMxD;AAGD,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -37,6 +37,8 @@ export interface LicenseCheckResult {
37
37
  }
38
38
  /**
39
39
  * Reasons for license check decisions
40
+ *
41
+ * SECURITY (Issue #542): Fail-closed design - errors result in denial
40
42
  */
41
- export type LicenseCheckReason = 'not-ci' | 'trusted-org' | 'allowlist' | 'not-found' | 'error' | 'skip-flag';
43
+ export type LicenseCheckReason = 'not-ci' | 'trusted-org' | 'allowlist' | 'not-found' | 'error' | 'owner-resolution-failed' | 'skip-flag';
42
44
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/utils/license/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,wCAAwC;IACxC,IAAI,EAAE,OAAO,CAAC;IACd,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,MAAM,EAAE,YAAY,GAAG,YAAY,GAAG,YAAY,GAAG,cAAc,CAAC;CACrE;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,gCAAgC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,aAAa,GACb,WAAW,GACX,WAAW,GACX,OAAO,GACP,WAAW,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/utils/license/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,wCAAwC;IACxC,IAAI,EAAE,OAAO,CAAC;IACd,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,MAAM,EAAE,YAAY,GAAG,YAAY,GAAG,YAAY,GAAG,cAAc,CAAC;CACrE;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,gCAAgC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,aAAa,GACb,WAAW,GACX,WAAW,GACX,OAAO,GACP,yBAAyB,GACzB,WAAW,CAAC"}
@@ -40,6 +40,11 @@ export declare function clearBinaryPathCache(): void;
40
40
  * - Caches results with TTL and PATH fingerprint for invalidation
41
41
  * - Re-validates cached paths are still executable
42
42
  *
43
+ * SECURITY (Issue #541):
44
+ * - Resolves symlinks and validates target is in trusted directory
45
+ * - Returns the real path (after symlink resolution), not the symlink
46
+ * - Prevents TOCTOU attacks via symlink replacement
47
+ *
43
48
  * @throws Error if binary is not trusted or not found
44
49
  */
45
50
  export declare function resolveBinaryPath(binaryName: string): string;
@@ -56,6 +61,10 @@ export declare function isBinaryAvailable(binaryName: string): boolean;
56
61
  * 3. Validates the path is executable
57
62
  * 4. Passes the absolute path to execa with shell: false
58
63
  *
64
+ * SECURITY (Issue #541): This function also:
65
+ * 5. Resolves symlinks and validates target is in trusted directory
66
+ * 6. Performs final validation right before execution to minimize TOCTOU window
67
+ *
59
68
  * @param binaryName - Name of the trusted binary to execute
60
69
  * @param args - Arguments to pass to the binary
61
70
  * @param options - execa options
@@ -70,6 +79,10 @@ export declare function secureExeca(binaryName: TrustedBinary, args?: readonly s
70
79
  * - Only executes from the project's node_modules/.bin directory
71
80
  * - Validates the binary name to prevent path traversal
72
81
  * - Uses shell: false
82
+ *
83
+ * SECURITY (Issue #541):
84
+ * - Resolves symlinks and validates target is within project
85
+ * - Performs final validation before execution
73
86
  */
74
87
  export declare function secureExecaLocal(binaryName: string, args?: readonly string[], options?: ExecaOptions & {
75
88
  cwd?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"secure-exec.d.ts","sourceRoot":"","sources":["../../src/utils/secure-exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAE,KAAK,OAAO,IAAI,YAAY,EAAE,KAAK,aAAa,EAAS,MAAM,OAAO,CAAC;AAMhF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,gIAenB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AA8D9D;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AA0GD;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAyD5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAO7D;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,aAAa,EACzB,IAAI,GAAE,SAAS,MAAM,EAAO,EAC5B,OAAO,CAAC,EAAE,YAAY,GACrB,aAAa,CAIf;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,SAAS,MAAM,EAAO,EAC5B,OAAO,CAAC,EAAE,YAAY,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACxC,aAAa,CAsBf;AAMD;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,GAAE,SAAS,MAAM,EAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,aAAa,CAE9F;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,GAAE,SAAS,MAAM,EAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,aAAa,CAE7F;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,GAAE,SAAS,MAAM,EAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,aAAa,CAE5F;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,GAAE,SAAS,MAAM,EAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,aAAa,CAEhG;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,IAAI,GAAE,SAAS,MAAM,EAAO,EAC5B,OAAO,CAAC,EAAE,YAAY,GACrB,aAAa,CAEf;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,GAAE,SAAS,MAAM,EAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,aAAa,CAEhG;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,GAAE,SAAS,MAAM,EAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,aAAa,CAE9F"}
1
+ {"version":3,"file":"secure-exec.d.ts","sourceRoot":"","sources":["../../src/utils/secure-exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAE,KAAK,OAAO,IAAI,YAAY,EAAE,KAAK,aAAa,EAAS,MAAM,OAAO,CAAC;AAMhF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,gIAenB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AA8D9D;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAsJD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAoE5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAO7D;AAMD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,aAAa,EACzB,IAAI,GAAE,SAAS,MAAM,EAAO,EAC5B,OAAO,CAAC,EAAE,YAAY,GACrB,aAAa,CAef;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,SAAS,MAAM,EAAO,EAC5B,OAAO,CAAC,EAAE,YAAY,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACxC,aAAa,CAwDf;AAMD;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,GAAE,SAAS,MAAM,EAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,aAAa,CAE9F;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,GAAE,SAAS,MAAM,EAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,aAAa,CAE7F;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,GAAE,SAAS,MAAM,EAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,aAAa,CAE5F;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,GAAE,SAAS,MAAM,EAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,aAAa,CAEhG;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,IAAI,GAAE,SAAS,MAAM,EAAO,EAC5B,OAAO,CAAC,EAAE,YAAY,GACrB,aAAa,CAEf;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,GAAE,SAAS,MAAM,EAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,aAAa,CAEhG;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,GAAE,SAAS,MAAM,EAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,aAAa,CAE9F"}
@@ -1 +1 @@
1
- {"version":3,"file":"template-fetcher.d.ts","sourceRoot":"","sources":["../../src/utils/template-fetcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAkEH,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,MAAM,EAAE,OAAO,CAAC;CACjB;AA0OD;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAgF/B;AAmGD;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAKzD"}
1
+ {"version":3,"file":"template-fetcher.d.ts","sourceRoot":"","sources":["../../src/utils/template-fetcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAkEH,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,MAAM,EAAE,OAAO,CAAC;CACjB;AA+RD;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAgF/B;AAmGD;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAKzD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runa-ai/runa-cli",
3
- "version": "0.5.31",
3
+ "version": "0.5.32",
4
4
  "private": false,
5
5
  "description": "AI-powered DevOps CLI",
6
6
  "type": "module",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@dotenvx/dotenvx": "1.51.4",
29
- "@runa-ai/runa": "^0.5.31",
29
+ "@runa-ai/runa": "^0.5.32",
30
30
  "@runa-ai/runa-xstate-test-plugin": "^0.5.28",
31
31
  "@types/node": "22.19.3",
32
32
  "boxen": "7.1.1",