Skip to content

[Hackathon] fix(input): plan, steps, code and password files saved by Windows PowerShell 5.1 are UTF-16 and get rejected or garbled #463

Open

Description

@freddy-tama

Verified against (commit SHA or released version)

commit 988c035 (main, v0.14.0), built and run on Windows 11, Windows PowerShell 5.1.26100, Node 24.16.

Discord Username / User ID

gradzzz

What does this improvement do?

The docs create plan files with shell redirection: testsprite test scaffold > first-test.plan.json (DOCUMENTATION.md L348) and testsprite test create --plan-template > plan.json (L384). In Windows PowerShell 5.1 (powershell.exe, the default shell on Windows 10 and 11), > writes UTF-16LE with a BOM. The CLI decodes every input file as UTF-8, so its own template fails its own linter:

PS> testsprite test create --plan-template > plan.json
PS> Format-Hex plan.json | Select-Object -First 1
00000000   FF FE 7B 00 0D 00 0A 00 20 00 20 00 22 00 24 00  .þ{..... . .".$.
PS> testsprite test lint --plan-from plan.json
plan.json: plan-from: not valid JSON: Unexpected token '�', "��{
   "... is not valid JSON
0/1 valid, 1 problem(s)
Error: lint: 1 problem(s) across 1 file(s)
PS> $LASTEXITCODE
5

test scaffold > first-test.plan.json fails the same way. The same happens to any file a user writes on PS 5.1 with >, Out-File or echo ... >:

  • --password-file: no error, but the secret is garbled. The CLI's own reader returns "��h\u0000u\u0000n\u0000t\u0000e\u0000r\u00002\u0000\r\u0000\n\u0000" for a file made with "hunter2" > pw.txt (it returns "hunter2" for the UTF-8 version), and that value is what the project's login gets.
  • --code-file: the code is uploaded with a NUL byte after every character.

Only a UTF-8 BOM is handled today (stripBom, from #34, for Set-Content -Encoding utf8).

Readers involved, all at 988c035:

  • readCappedFileGuarded, test.ts#L2151: --plan-from, --steps, create-batch --plans, and test lint --plan-from/--steps
  • readCodeFile, test.ts#L1781: --code-file
  • the plan-directory reader, test.ts#L5103: create-batch --plan-from-dir
  • test lint --plans, test.ts#L6010: this one reads the file with its own readFileSync(..., 'utf8') instead of the shared reader, so a UTF-16LE JSONL file reports every line as invalid JSON
  • readSecretFileGuarded, secret-file.ts#L101: --password-file on project create/update, project env, project sign-in

Details / implementation notes

Proposal: one small decoder for user-supplied text files, used by the five readers above so the encoding rule lives in one place:

  • UTF-8 BOM (EF BB BF): strip it and decode as UTF-8 (today's behaviour)
  • UTF-16LE BOM (FF FE): decode as UTF-16LE
  • UTF-16BE BOM (FE FF): decode as UTF-16BE
  • anything else: UTF-8, exactly as today (no guessing for BOM-less input, no new dependency)

Size caps keep counting raw bytes, as now. UTF-8 input behaves exactly as before.

Tests: Buffer fixtures for each BOM in the decoder's unit test; test lint --plan-from and test create --plan-from --dry-run with a UTF-16LE fixture; --password-file with a UTF-16LE fixture asserting the decoded value; the existing UTF-8 BOM tests unchanged.

Out of scope: the raw credential, client-secret and refresh-token file reads in project.ts, which #282 / #283 move onto readSecretFileGuarded. They get this fix once that lands.

Confirmations

  • I have searched existing issues and this is not a duplicate.
  • I have provided my Discord identity above for reward coordination.

Activity

  1. fyZhang66 commented on Oct 20, 2026

    @fyZhang66
    Contributor

    Confirmed from the code, thanks @freddy-tama (not tested on PowerShell 5.1 here). Every input reader decodes utf8 and stripBom only handles U+FEFF after decoding, so a UTF-16LE file written by > is garbled. Accepted, scope:

    • One shared reader that sniffs a BOM (UTF-16LE/BE, UTF-8) and decodes accordingly, used by the plan/steps/code/password readers; a DOCUMENTATION.md note recommending Out-File -Encoding utf8 or pwsh.
    • Tests (must fail before the fix): the same plan JSON saved as UTF-16LE, UTF-16BE and UTF-8-with-BOM all lint and create; a BOM-less file is unchanged.

    No new dependency. Comment /assign to claim it.

  2. added
    acceptedTriaged and accepted — a PR for this will be reviewed
    on Oct 20, 2026
  3. OkeyAmy commented on Oct 20, 2026

    @OkeyAmy

    /assign

  4. testsprite-hob commented on Oct 20, 2026

    @testsprite-hob

    Assigned to @OkeyAmy. Thanks for taking this on. (0 more slots available.)

    Your open assigned issues (3/3):

  5. OkeyAmy commented on Oct 21, 2026

    @OkeyAmy

    /unassign

  6. testsprite-hob commented on Oct 21, 2026

    @testsprite-hob

    Unassigned @OkeyAmy. Thanks for the update — freeing this up for someone else.

  7. Herbicide7665 commented on Oct 21, 2026

    @Herbicide7665

    /assign

    Discord: weili_yang33

  8. testsprite-hob commented on Oct 21, 2026

    @testsprite-hob

    Thanks @Herbicide7665! You're at the 3-issue limit, so I haven't assigned this one. Please finish or /unassign one first, then comment again here.

    Your open assigned issues (3/3):

  9. Herbicide7665 commented on Oct 21, 2026

    @Herbicide7665

    /assign

    Discord: weili_yang33

  10. testsprite-hob commented on Oct 21, 2026

    @testsprite-hob

    Assigned to @Herbicide7665. Thanks for taking this on. (0 more slots available.)

    Your open assigned issues (3/3):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

acceptedTriaged and accepted — a PR for this will be reviewedin-progressAssigned and actively being worked on

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions