Skip to content

Revive/2026 modernization - #1026

Closed
rshu wants to merge 4 commits into
LeetCode-OpenSource:masterfrom
rshu:revive/2026-modernization
Closed

rshu wants to merge 4 commits into
LeetCode-OpenSource:masterfrom
rshu:revive/2026-modernization

Conversation

@rshu

@rshu rshu commented Sep 24, 2026

Copy link
Copy Markdown

No description provided.

rshu and others added 4 commits September 24, 2026 12:01
The project had been unmaintained since 2024-09. The build was fine, but
several runtime defects made it appear dead. Each fix below was reproduced
before it was written.

Sign-in (the main reason it looked broken):
- An invalid or expired cookie sent setCookieToCli into an unbounded prompt
  loop. LeetCode answers HTTP 200 with an empty username for a bad cookie,
  that empty name was fed to the CLI's required `login:` prompt, and the
  rejection went to stderr where nothing was listening. The progress
  notification spun forever and no error was ever shown.
- The promise could only settle from a stdout match, so a CLI crash left it
  pending forever. Added a settled latch, a close handler, a 30s timeout,
  accumulated buffering, and stderr failure detection.
- The cookie is now validated against the GraphQL user query before it is
  handed to the CLI, so a bad login is never persisted.
- Both sign-in catch blocks discarded the cause and showed one generic
  string; they now surface the real error.
- getLoginStatus treated a corrupt CLI record as a signed-in user literally
  named "undefined", which wedged the extension permanently.

Data plane:
- cpUtils rejected whenever stdout contained the substring "ERROR" anywhere,
  turning ordinary judge output and problem text into fake failures. It now
  matches the CLI's "[ERROR]" tag at line start.
- stdout/stderr are decoded with setEncoding("utf8"); decoding each raw chunk
  corrupted multi-byte characters that straddled a chunk boundary.
- Child processes are forced to a colourless environment, and the parsers
  strip ANSI. A user with FORCE_COLOR set previously got a silently empty tree.
- Show Problem created the file before fetching, so a failure left a 0-byte
  file that the exists-check then refused to ever regenerate.
- Submission failures rendered as the literal "## undefined", discarding the
  real error.
- toggleFavorite ignored leetcode.nodePath.

leetcode.cn only:
- getLeetCodeEndpoint and getUrl are pinned to leetcode.cn; the endpoint
  toggle command and the leetcode.endpoint setting are removed.
- The list parser accepts non-numeric front-end ids (LCP/LCR/LCS). The old
  regex silently dropped 279 of leetcode.cn's 3921 problems. Problem ids are
  now quoted at the CLI boundary since they can contain a space.

Manifest and build:
- All 16 contributed commands now have an activation event, and onUri is
  declared so browser sign-in can activate the extension.
- package-lock pinned four packages to r2.cnpmjs.org, whose certificate has
  expired; `npm ci` failed outright on any machine with a cold cache.
- CI now runs on Node 20 across three platforms and actually runs the tests.

Testing:
- Parsers extracted into src/utils/parseUtils.ts with no vscode import, and
  covered by 21 unit tests. Each regression test asserts the old behaviour
  was wrong rather than only asserting the new behaviour.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Security: every LeetCode CLI invocation spawned through a shell with its
arguments interpolated into the command line, so a problem id, file path or
test case containing shell metacharacters was executed. Quoting the values
did not help, because a quote in the value escapes the quoting.

Removed `shell: true` at every spawn site and stripped the manual quoting
that existed only to compensate for it:
- cpUtils.executeCommand / executeCommandWithProgress
- leetCodeExecutor.executeCommandEx / executeCommandWithProgressEx
- leetCodeManager.setCookieToCli, both the WSL and non-WSL branches
- getLeetCodeBinaryPath and the leetcode.nodePath wrapping
- the show, submit, test and star argument lists
- wslUtils.toWslPath / toWinPath
- show.showSolution's fsPath
- test.parseTestString, which now passes the test case through untouched;
  its escaping also corrupted any test input containing an apostrophe

Proven against the real CLI binary: a `"; touch <marker>; echo "` payload
creates the marker under the old shell path and is inert under the new one.
This also fixes problem 432 "All O`one Data Structure", whose backtick used
to abort submit and test, and paths containing spaces.

Safety: meetRequirements deleted ~/.lc outright on first activation in a
profile, silently destroying the leetcode-cli session shared with other
tools. It now moves the directory aside to ~/.lc.vscode-leetcode-backup.

Testing:
- 25 unit tests, including four that pin the shell-less spawn contract. One
  deliberately reproduces the original vulnerability so the safe test cannot
  quietly stop being meaningful.
- 10 integration tests in a real VS Code instance via @vscode/test-cli,
  covering activation, command registration, the manifest contract and
  configuration defaults.
- The integration harness redirects HOME to a throwaway directory, because a
  fresh profile would otherwise trigger the ~/.lc wipe against the
  developer's real session. A test asserts that redirect is in effect.
- `--profile-temp` had to be dropped from launchArgs; it prevents the test
  runner from ever starting mocha.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
With the endpoint pinned to leetcode.cn, every leetcode.com branch was
unreachable. Removed the .com URL table, PREMIUM_URL_GLOBAL, the endpoint
switch in getAuthLoginUrl, the .com tag links, and the endpoint branch in
the CLI plugin toggle, which is now enableCnEndpoint().

Sign-in errors from the CLI are summarised to the useful line instead of
dumping the tail of a stack trace at the user.

README: replaced the stale "cannot log in to leetcode.com" workaround notice,
dropped the Switch Endpoint section and the leetcode.endpoint setting row,
and corrected the stated VS Code and Node requirements.

Verified after the change:
- 25 unit tests and 10 integration tests pass
- all resolved URLs point at leetcode.cn
- all 3921 live leetcode.cn problems parse; the old regex dropped 279

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Verified against a live leetcode.cn account. Manage Sessions fails because
leetcode.cn now rejects the CLI's request to /session/ with HTTP 405. That is
an upstream change, not a regression here, but the extension reported it as
"Failed to list sessions. Please open the output channel for details." with
no cause, and the underlying error read "failed with exit code 0".

- cpUtils now puts the CLI's own error line into the thrown Error, so the
  message reads: The LeetCode CLI failed (exit code 0). [ERROR] http error
  [code=405]
- summariseCliOutput moved into parseUtils so both the process bridge and the
  sign-in path share it, and so it is unit testable. It prefers the CLI's
  [ERROR] line, then a thrown error, and never shows stack frames.
- Added promptForFailure in uiUtils and routed all ten generic failure
  dialogs through it, so every command reports why it failed.

29 unit tests and 10 integration tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rshu rshu closed this Sep 24, 2026
@rshu
rshu deleted the revive/2026-modernization branch September 24, 2026 06:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant