You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Four file-reading flags added in v0.4.0 on project credential and project auto-auth read their file with a raw readFileSync(path, 'utf8').trim() and no guard. When the file is missing (or is a directory), the CLI throws an unwrapped Node error instead of the structured validation error every other file flag produces:
exit 1 (generic) instead of 5 (validation)
a malformed --output json envelope — error is a bare string, not the { code, message, nextAction } object the rest of the CLI emits, so anything parsing --output json breaks
it leaks raw fs internals (absolute path + errno)
it also crashes under --dry-run, which is documented not to touch the filesystem
The contrast is the bug — the same class of user error (bad input to a file flag) gets two different contracts:
# Guarded flag (correct): structured envelope, exit 5
$ testsprite test create --project x --type backend --name n --code-file /nope.py --output json{ "error": { "code": "VALIDATION_ERROR", "message": "Invalid request.", "nextAction": "Flag `--code-file` is invalid: file does not exist: /nope.py.", "requestId": "local", "details": { "field": "code-file", ... } }}
# exit 5
# Unguarded flag (bug): bare-string envelope, exit 1, leaks fs internals
$ testsprite project credential <project-id> --type "API key" --credential-file /nope --output json{ "error": "ENOENT: no such file or directory, open '/nope'"}
# exit 1
--dry-run does not help — it reads the file (and crashes) before the dry-run banner:
$ testsprite project credential <project-id> --type "API key" --credential-file /nope --dry-runError: ENOENT: no such file or directory, open '/nope'
# exit 1
A directory argument surfaces a raw EISDIR the same way.
Why this is a gap and not already covered
src/commands/test.ts already guards all of its file flags (--code-file, --plan-from, --plans, --steps) — each returns a clean VALIDATION_ERROR / exit 5 with a field detail. That is the reference behavior.
Summary
Four file-reading flags added in v0.4.0 on
project credentialandproject auto-authread their file with a rawreadFileSync(path, 'utf8').trim()and no guard. When the file is missing (or is a directory), the CLI throws an unwrapped Node error instead of the structured validation error every other file flag produces:1(generic) instead of5(validation)--output jsonenvelope —erroris a bare string, not the{ code, message, nextAction }object the rest of the CLI emits, so anything parsing--output jsonbreaks--dry-run, which is documented not to touch the filesystemAffected flags
project credential--credential-filesrc/commands/project.ts:470project auto-auth--password-filesrc/commands/project.ts:579project auto-auth--client-secret-filesrc/commands/project.ts:583project auto-auth--refresh-token-filesrc/commands/project.ts:588All six use the same unguarded pattern:
Reproduction
The contrast is the bug — the same class of user error (bad input to a file flag) gets two different contracts:
--dry-rundoes not help — it reads the file (and crashes) before the dry-run banner:A directory argument surfaces a raw
EISDIRthe same way.Why this is a gap and not already covered
src/commands/test.tsalready guards all of its file flags (--code-file,--plan-from,--plans,--steps) — each returns a cleanVALIDATION_ERROR/ exit 5 with afielddetail. That is the reference behavior.readPasswordFileGuarded(), but it is hardcoded to--password-fileand only wired intoproject create/project update(project.ts:213,:329). The four flags above are not covered —credential/auto-authdid not exist when that fix was written, so they survive its merge.readFileSyncsweep confirms these four are the only remaining unguarded user-flag file reads.Suggested fix
Generalize the guard into a small helper and apply it to all six project.ts file-read sites (the two
--password-filesites plus the four new ones):Happy to sequence this however you prefer — fold it into #248, or land it right after so the helper covers
--password-filetoo.Environment: reproduced on current
main(v0.4.0) against the production API, Node 20.