Skip to content

Commit c333af8

Browse files
committed
fix(config): mention the config-file fallback in project-id guidance, dedupe dangerous-key checks, prettier pass
1 parent 4c96c72 commit c333af8

2 files changed

Lines changed: 11 additions & 9 deletions

File tree

src/commands/test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9176,10 +9176,14 @@ export function createTestCommand(deps: TestDeps = {}): Command {
91769176

91779177
if (isAll) {
91789178
// --all path: wave-ordered fresh batch run.
9179-
const projectId = resolveProjectId(cmdOpts.project, deps, resolveCommonOptions(command).profile);
9179+
const projectId = resolveProjectId(
9180+
cmdOpts.project,
9181+
deps,
9182+
resolveCommonOptions(command).profile,
9183+
);
91809184
requireProjectId(
91819185
projectId,
9182-
'--all requires a project id - pass --project <id> or set TESTSPRITE_PROJECT_ID',
9186+
'--all requires a project id - pass --project <id>, set TESTSPRITE_PROJECT_ID, or set project_id in ~/.testsprite/config (or TESTSPRITE_CONFIG_FILE)',
91839187
);
91849188
// --target-url has no effect on the --all batch path: a BE test's base
91859189
// URL is baked into its code, and the unified engine resolves each
@@ -9826,7 +9830,7 @@ function resolveProjectId(
98269830
}
98279831
function requireProjectId(
98289832
projectId: string | undefined,
9829-
message = 'is required; pass --project <id>, set TESTSPRITE_PROJECT_ID, or set project_id in ~/.testsprite/config',
9833+
message = 'is required; pass --project <id>, set TESTSPRITE_PROJECT_ID, or set project_id in the config file (~/.testsprite/config or TESTSPRITE_CONFIG_FILE)',
98309834
): asserts projectId is string {
98319835
if (typeof projectId !== 'string' || projectId.length === 0) {
98329836
throw localValidationError('project', message);

src/lib/credentials.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ const CREDENTIALS_LOCK_RETRY_MS = 25;
100100
const CREDENTIALS_LOCK_WAIT_MS = 5_000;
101101
const CREDENTIALS_LOCK_STALE_MS = 30_000;
102102

103+
const DANGEROUS_INI_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
104+
103105
/**
104106
* Generic hardened INI walk shared by the credentials file and the settings
105107
* config file (`~/.testsprite/config`). Returns every `[section]`'s raw
@@ -120,11 +122,7 @@ export function parseIniFile(content: string): Record<string, Record<string, str
120122
const sectionName = sectionMatch[1]!.trim();
121123
// Defense in depth alongside the null-prototype accumulator: never treat a
122124
// prototype-polluting key as a profile section. Skip its key=value lines.
123-
if (
124-
sectionName === '__proto__' ||
125-
sectionName === 'constructor' ||
126-
sectionName === 'prototype'
127-
) {
125+
if (DANGEROUS_INI_KEYS.has(sectionName)) {
128126
currentEntry = null;
129127
continue;
130128
}
@@ -145,7 +143,7 @@ export function parseIniFile(content: string): Record<string, Record<string, str
145143
const rawValue = line.slice(eqIndex + 1).trim();
146144
// Same guard for keys: `__proto__ = x` must never become a property write
147145
// on a shared prototype when a caller copies the section into a plain map.
148-
if (rawKey === '__proto__' || rawKey === 'constructor' || rawKey === 'prototype') continue;
146+
if (DANGEROUS_INI_KEYS.has(rawKey)) continue;
149147
currentEntry[rawKey] = rawValue;
150148
}
151149
// Return plain objects so callers (and test matchers) see normal prototypes.

0 commit comments

Comments
 (0)