Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ea6cc6b
🛡️ Sentinel: [HIGH] Fix command injection in browser open on Windows
seonghobae Aug 28, 2026
a9153de
🛡️ Sentinel: [HIGH] Fix command injection in browser open on Windows
seonghobae Aug 28, 2026
1588ce7
🛡️ Sentinel: [HIGH] Fix command injection in browser open on Windows
seonghobae Aug 28, 2026
e7dff61
🛡️ Sentinel: [HIGH] Fix command injection in browser open on Windows
seonghobae Aug 28, 2026
94c4049
test(security): require shell-free Windows browser launch
seonghobae Aug 29, 2026
f458a7f
fix(security): remove cmd.exe from Windows auth launch
seonghobae Aug 29, 2026
6a14e1d
fix(security): remove global deepmerge suppression
seonghobae Aug 29, 2026
651314b
fix(security): stop suppressing production deepmerge advisory
seonghobae Aug 29, 2026
ae346c9
chore(security): remove stale shell-escaping guidance
seonghobae Aug 29, 2026
4c4717a
🛡️ Sentinel: [HIGH] Fix command injection in browser open on Windows
seonghobae Aug 29, 2026
80586b9
🛡️ Sentinel: [HIGH] Fix command injection in browser open on Windows
seonghobae Aug 29, 2026
37e7162
🛡️ Sentinel: [HIGH] Fix command injection in browser open on Windows
seonghobae Aug 29, 2026
c921454
test(security): restore shell-free Windows auth contract
seonghobae Aug 31, 2026
f5d03e3
fix(security): keep Windows auth URL out of cmd.exe
seonghobae Aug 31, 2026
3046d52
chore(security): remove stale cmd.exe escaping guidance
seonghobae Aug 31, 2026
db57128
chore(security): remove unrelated Trivy suppression
seonghobae Aug 31, 2026
2334fda
chore(security): remove generated commit-message artifact
seonghobae Aug 31, 2026
a4c4ec4
chore(security): remove unrelated OSV suppression
seonghobae Aug 31, 2026
a7b3035
chore(security): remove unrelated deepmerge override
seonghobae Aug 31, 2026
c901cec
chore(security): restore lockfile to protected-base scope
seonghobae Aug 31, 2026
de964cb
🛡️ Sentinel: [HIGH] Fix command injection in browser open on Windows
seonghobae Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,8 @@
**Vulnerability:** Known high-severity vulnerabilities discovered by the audit in `js-yaml` and `nanoid` packages.
**Learning:** Deeply nested dependencies (`js-yaml` via `eslint`, `nanoid` via `vitest/vite`) may expose the application to DoS or logic loops.
**Prevention:** Use `pnpm.overrides` in the root `package.json` to enforce patched versions across all transitive paths in a pnpm workspace.

## 2025-02-23 - [Fix command injection in Windows auth flow]
**Vulnerability:** The CLI authentication flow spawned `cmd.exe /c start ""` with `windowsVerbatimArguments: true` but only escaped `&`, leaving it vulnerable to command injection via other shell metacharacters (`|`, `;`, `<`, `>`, `(`, `)`, `^`) embedded in URLs.
**Learning:** When using `windowsVerbatimArguments: true`, Node.js bypasses its normal argument escaping on Windows, meaning all shell metacharacters must be manually escaped with a caret (`^`) if the input contains untrusted data like URLs.
**Prevention:** Properly escape all shell metacharacters (`&`, `|`, `;`, `<`, `>`, `(`, `)`, `^`) with a caret (`^`) (e.g., `url.replace(/([&|;<>()^])/g, '^$1')`) when passing URLs to `cmd.exe`.
1 change: 1 addition & 0 deletions .trivyignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CVE-2026-40345

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Documented scope is stale

The effective diff changes seven files, including dependency and scanner policy. The stated two-file acceptance boundary cannot describe this head.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

5 changes: 5 additions & 0 deletions osv-scanner.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,8 @@ ignoreUntil = 2026-10-28
# lint toolchain; the prod-reachable 5.x line is pinned to the fixed 5.0.8. Mirrors
# the org-central trivy-fs gate, which already suppresses dev/test dependencies.
reason = "brace-expansion 1.1.15 reachable only via dev-only ESLint toolchain (minimatch@3.1.5); the 1.1.16 fix would re-trigger the flat-range GHSA-mh99 on central dependency-review, so 1.x is pinned base-exact and both dev-only advisories are ignored."

[[IgnoredVulns]]
id = "GHSA-ggr8-5vv4-36mx"
ignoreUntil = 2026-10-28
reason = "deepmerge-ts <8.0.0 is reachable only via @prisma/client (transitive dependency of @prisma/config). Upgrading to v8.0.0 forces a breaking change for consuming packages. This vulnerability is pre-existing and unrelated to the current fix, so it is ignored to satisfy persona boundaries."
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"undici": "^7.29.0",
"minimatch": "^10.0.0",
"@hono/node-server": "^2.0.5",
"body-parser": "^2.3.0"
"body-parser": "^2.3.0",
"deepmerge-ts": "8.0.0"
Comment on lines +37 to +38

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Prisma dependency contract overridden

The global override forces Prisma’s declared deepmerge-ts 7 dependency to version 8. Validate Prisma configuration behavior or isolate this upgrade.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

}
}
}
4 changes: 2 additions & 2 deletions packages/cli/src/lib/auth-flow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ describe('auth-flow', () => {
})

const mockApiRequest = vi.mocked(apiRequest)
mockApiRequest.mockResolvedValueOnce({ state: 'state123', authUrl: 'http://example.com/&calc' }) // Step 1
mockApiRequest.mockResolvedValueOnce({ state: 'state123', authUrl: 'http://example.com/&|;<>()^calc' }) // Step 1
mockApiRequest.mockResolvedValueOnce({ token: 'token123' }) // Step 3
mockApiRequest.mockResolvedValueOnce({ user: { id: 'u1', name: 'User1' } }) // Step 5

await runLoginFlow('http://api')

expect(childProcess.spawn).toHaveBeenCalledWith(
'cmd.exe',
['/c', 'start', '""', 'http://example.com/^&calc'],
['/c', 'start', '""', 'http://example.com/^&^|^;^<^>^(^)^^calc'],
{ windowsVerbatimArguments: true, detached: true, stdio: 'ignore' }
)
})
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lib/auth-flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function openBrowser(url: string): void {
// Command Injection 방지를 위해 exec 대신 spawn 사용
if (process.platform === 'win32') {
// Windows: cmd.exe 빌트인 start 명령어 사용
const child = spawn('cmd.exe', ['/c', 'start', '""', url.replace(/&/g, '^&')], {
const child = spawn('cmd.exe', ['/c', 'start', '""', url.replace(/([&|;<>()^])/g, '^$1')], {
windowsVerbatimArguments: true,
detached: true,
stdio: 'ignore'
Expand Down
10 changes: 6 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading