Skip to content

๐Ÿ›ก๏ธ Sentinel: [CRITICAL] ๊ฒฝ๋กœ ํƒ์ƒ‰ ์ทจ์•ฝ์  ์ˆ˜์ •#49

Open
seonghobae wants to merge 1 commit into
mainfrom
fix-path-traversal-gherkin-14294950985736443331
Open

๐Ÿ›ก๏ธ Sentinel: [CRITICAL] ๊ฒฝ๋กœ ํƒ์ƒ‰ ์ทจ์•ฝ์  ์ˆ˜์ •#49
seonghobae wants to merge 1 commit into
mainfrom
fix-path-traversal-gherkin-14294950985736443331

Conversation

@seonghobae

Copy link
Copy Markdown

๐Ÿšจ Severity: CRITICAL
๐Ÿ’ก Vulnerability: vspec export gherkin ๋ช…๋ น์—์„œ --output ์˜ต์…˜์˜ ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ๊ฒ€์ฆํ•˜์ง€ ์•Š์•„ path traversal ๊ณต๊ฒฉ์ด ๊ฐ€๋Šฅํ–ˆ์Šต๋‹ˆ๋‹ค.
๐ŸŽฏ Impact: ๊ณต๊ฒฉ์ž๊ฐ€ ../../../etc/passwd ์™€ ๊ฐ™์€ ์•…์„ฑ ๊ฒฝ๋กœ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ํ”„๋กœ์ ํŠธ ํด๋” ์™ธ๋ถ€์— ์œ„์น˜ํ•œ ์ž„์˜์˜ ๋ฏผ๊ฐํ•œ ํŒŒ์ผ์— ์ ‘๊ทผํ•˜๊ฑฐ๋‚˜ ๋ฎ์–ด์“ธ ์œ„ํ—˜์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
๐Ÿ”ง Fix: Node.js์˜ path.resolve ๋ฐ path.relative ๋ฅผ ์‚ฌ์šฉํ•ด ์ƒ์„ฑ๋œ ๊ฒฝ๋กœ๊ฐ€ ํ•ญ์ƒ ์„ค์ •๋œ config root ๋‚ด๋ถ€์— ์žˆ๋„๋ก ๊ฒ€์ฆํ•˜๋Š” ๋ฐฉ์–ด ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
โœ… Verification: npx vitest tests/gherkin.test.ts ์™€ ์ „์ฒด ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ pnpm test๊ฐ€ ํ†ต๊ณผํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.


PR created automatically by Jules for task 14294950985736443331 started by @seonghobae

exportGherkin ํ•จ์ˆ˜์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•œ --output ์˜ต์…˜ ๊ฒฝ๋กœ๊ฐ€ config.root ์™ธ๋ถ€์˜ ํŒŒ์ผ ์‹œ์Šคํ…œ ๊ฒฝ๋กœ๋ฅผ ๊ฐ€๋ฆฌํ‚ค์ง€ ๋ชปํ•˜๋„๋ก ๋ฐฉ์–ด ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
- path.resolve์™€ path.relative๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœ์ ํŠธ ์™ธ๋ถ€ ๊ฒฝ๋กœ์— ๋Œ€ํ•œ ์ฝ๊ธฐ/์“ฐ๊ธฐ๋ฅผ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค.
- ๊ฒฝ๋กœ ์ œํ•œ ์œ„๋ฐ˜ ์‹œ VspecError๋ฅผ ๋˜์ง‘๋‹ˆ๋‹ค.
- tests/gherkin.test.ts์— ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
@google-labs-jules

Copy link
Copy Markdown

๐Ÿ‘‹ Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a ๐Ÿ‘€ emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

๐Ÿ“ Walkthrough

Summary by CodeRabbit

๋ฆด๋ฆฌ์Šค ๋…ธํŠธ

  • ๋ฒ„๊ทธ ์ˆ˜์ •

    • Gherkin ๋‚ด๋ณด๋‚ด๊ธฐ ๊ธฐ๋Šฅ์—์„œ ์ถœ๋ ฅ ๊ฒฝ๋กœ ๊ฒ€์ฆ์„ ๊ฐ•ํ™”ํ•˜์—ฌ ํ”„๋กœ์ ํŠธ ๋ฒ”์œ„ ์™ธ๋ถ€๋กœ์˜ ํŒŒ์ผ ์“ฐ๊ธฐ ์‹œ๋„๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ํ…Œ์ŠคํŠธ

    • ๊ฒฝ๋กœ ๊ฒ€์ฆ ๋ณด์•ˆ ๊ฒ€์‚ฌ๋ฅผ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Walkthrough

exportGherkin ํ•จ์ˆ˜์˜ --output ์ธ์ž๊ฐ€ ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋Š” ๊ฒฝ๋กœ ์„ค์ •์„ ํ—ˆ์šฉํ•˜๋Š” ๊ฒฝ๋กœ ์ˆœํšŒ ์ทจ์•ฝ์ ์„ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ฒฝ๋กœ ๊ฒ€์ฆ ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๊ณ , ๊ด€๋ จ ํ…Œ์ŠคํŠธ์™€ ๋ณด์•ˆ ๋ฌธ์„œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

Changes

๊ฒฝ๋กœ ์ˆœํšŒ ์ทจ์•ฝ์  ์ˆ˜์ •

Layer / File(s) Summary
๊ฒฝ๋กœ ์ˆœํšŒ ์ทจ์•ฝ์  ๋ฌธ์„œํ™”
.jules/sentinel.md
exportGherkin์˜ --output ๊ฒฝ๋กœ ๊ฒ€์ฆ ๋ˆ„๋ฝ์œผ๋กœ ์ธํ•œ ์ทจ์•ฝ์ ์„ ์„ค๋ช…ํ•˜๊ณ , path.resolve๋ฅผ ์ด์šฉํ•œ ๊ฒฝ๊ณ„ ๊ฒ€์ฆ ๋ฐฉ์‹์„ ๋ฌธ์„œํ™”ํ•จ.
๊ฒฝ๋กœ ๊ฒ€์ฆ ๋กœ์ง ๊ตฌํ˜„ ๋ฐ import ํ™•์žฅ
src/export/gherkin.ts
node:path import์— resolve, relative, isAbsolute๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , outputPath๋ฅผ resolve(config.root, output)๋กœ ๊ณ„์‚ฐํ•œ ๋’ค ์ƒ๋Œ€ ๊ฒฝ๋กœ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋ฒ”์œ„๋ฅผ ๊ฒ€์ฆ. ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฉด VspecError("INVALID_ARGUMENT", ...)๋ฅผ ๋˜์ง.
๊ฒฝ๋กœ ์ˆœํšŒ ๋ฐฉ์ง€ ํ…Œ์ŠคํŠธ
tests/gherkin.test.ts
exportGherkin๊ณผ VspecError๋ฅผ importํ•˜๊ณ , ์ƒ์œ„ ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ฒฝ๋กœ ์ž…๋ ฅ ์‹œ INVALID_ARGUMENT ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”์ง€ ๊ฒ€์ฆํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ถ”๊ฐ€.

๐ŸŽฏ 2 (Simple) | โฑ๏ธ ~12๋ถ„

๐Ÿฐ ๊ฒฝ๋กœ๋ฅผ ์ง€ํ‚ค๋Š” ๋ฐฉ์–ด๋ง‰์ด ์„ธ์›Œ์กŒ๋„ค,
../../../ ์•…์˜๋ฅผ ๋ง‰์•„๋‚ด๊ณ ,
ํ”„๋กœ์ ํŠธ ๊ฒฝ๊ณ„ ์•ˆ์—์„œ๋งŒ ๋…ธ๋‹ˆ๋Š”
์•ˆ์ „ํ•œ ํŒŒ์ผ๋“ค์˜ ์ถค์ด ํŽผ์ณ์ง„๋‹ค!
resolve์™€ relative์˜ ์†์žก์Œ์œผ๋กœ โœจ


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

โŒ Failed checks (1 error)

Check name Status Explanation Resolution
Docstring Coverage โŒ Error Docstring coverage is 0.00% which is insufficient. The required threshold is 100.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
โœ… Passed checks (4 passed)
Check name Status Explanation
Title check โœ… Passed Pull request ์ œ๋ชฉ์ด ๊ฒฝ๋กœ ํƒ์ƒ‰ ์ทจ์•ฝ์  ์ˆ˜์ •์ด๋ผ๋Š” ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ๋ณ€๊ฒฝ ๋‚ด์šฉ๊ณผ ๊ด€๋ จ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค.
Description check โœ… Passed Pull request ์„ค๋ช…์ด ๋ฌธ์ œ์ , ์˜ํ–ฅ, ์ˆ˜์ • ๋‚ด์šฉ, ๊ฒ€์ฆ ๊ฒฐ๊ณผ๋ฅผ ์ƒ์„ธํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ๋ณ€๊ฒฝ ์‚ฌํ•ญ๊ณผ ์™„์ „ํžˆ ๊ด€๋ จ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค.
Linked Issues check โœ… Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check โœ… Passed Check skipped because no linked issues were found for this pull request.
โœจ Finishing Touches
๐Ÿ“ Generate docstrings
  • Create stacked PR
  • Commit on current branch
๐Ÿงช Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-path-traversal-gherkin-14294950985736443331
โœจ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix-path-traversal-gherkin-14294950985736443331

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

๐Ÿงน Nitpick comments (1)
tests/gherkin.test.ts (1)

15-22: โšก Quick win

ํ…Œ์ŠคํŠธ๊ฐ€ ์ž˜ ์ž‘์„ฑ๋˜์—ˆ์œผ๋‚˜, ์ถ”๊ฐ€ ๊ณต๊ฒฉ ๋ฒกํ„ฐ์— ๋Œ€ํ•œ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ๋ณด๊ฐ•ํ•˜์„ธ์š”.

ํ˜„์žฌ ํ…Œ์ŠคํŠธ๋Š” .. traversal ์ผ€์ด์Šค๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ๋” ํฌ๊ด„์ ์ธ ๋ณด์•ˆ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ๋‹ค์Œ ์ผ€์ด์Šค๋“ค์„ ์ถ”๊ฐ€๋กœ ๊ณ ๋ คํ•˜์„ธ์š”:

  • ์ ˆ๋Œ€ ๊ฒฝ๋กœ ์‹œ๋„ (์˜ˆ: /tmp/hacked.feature)
  • Windows ์ ˆ๋Œ€ ๊ฒฝ๋กœ (์˜ˆ: C:\\tmp\\hacked.feature)
  • ๋ฃจํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ ์ž์ฒด์— ์“ฐ๊ธฐ (์˜ˆ: ๋นˆ ๋ฌธ์ž์—ด ๋˜๋Š” .)
๐Ÿงช ์ถ”๊ฐ€ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ œ์•ˆ
   it("prevents path traversal vulnerabilities", () => {
     // VSPEC-001 is present in the mock project root (tests/fixtures/doctor/clean) so it would succeed
     // if not for the traversal in the --output path.
     const cwd = join(import.meta.dirname, "fixtures/doctor/clean");
     expect(() =>
       exportGherkin({ key: "VSPEC-001", output: "../../../tmp/hacked.feature", cwd })
     ).toThrowError(new VspecError("INVALID_ARGUMENT", "Output path must be within the project root."));
+
+    // Absolute path attempt
+    expect(() =>
+      exportGherkin({ key: "VSPEC-001", output: "/tmp/hacked.feature", cwd })
+    ).toThrowError(new VspecError("INVALID_ARGUMENT", "Output path must be within the project root."));
+
+    // Windows absolute path attempt
+    expect(() =>
+      exportGherkin({ key: "VSPEC-001", output: "C:\\tmp\\hacked.feature", cwd })
+    ).toThrowError(new VspecError("INVALID_ARGUMENT", "Output path must be within the project root."));
   });
๐Ÿค– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/gherkin.test.ts` around lines 15 - 22, Add additional security unit
tests for exportGherkin that cover absolute path attack vectors: assert that
calling exportGherkin with output set to a Unix absolute path (e.g.,
"/tmp/hacked.feature"), a Windows absolute path (e.g.,
"C:\\tmp\\hacked.feature"), and root/empty outputs (e.g., "" and ".") each
throws the same VspecError("INVALID_ARGUMENT", "Output path must be within the
project root."); place these assertions alongside the existing traversal test in
the gherkin.test.ts file so exportGherkinโ€™s validation logic is exercised for
../ traversal, absolute paths, Windows-style absolute paths, and empty/root
output values.
๐Ÿค– Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/export/gherkin.ts`:
- Around line 43-47: The current check uses resolve/relative and isAbsolute on
outputPath but does not resolve symlinks, so an attacker can escape the project
root via symlinked directories; update the validation around outputPath (the
computed outputPath and rel variables used before throwing VspecError) to
compare real filesystem paths: obtain real paths for config.root and the
output's nearest existing ancestor (using fs.realpathSync or async fs.realpath)
and ensure the real output path starts with the real root, or alternatively walk
path segments of outputPath and lstatSync each segment to reject any symlinked
component before calling mkdirSync(dirname(outputPath)) or
writeFileSync(outputPath,...). Ensure the same checks are applied wherever
outputPath is created/used.

---

Nitpick comments:
In `@tests/gherkin.test.ts`:
- Around line 15-22: Add additional security unit tests for exportGherkin that
cover absolute path attack vectors: assert that calling exportGherkin with
output set to a Unix absolute path (e.g., "/tmp/hacked.feature"), a Windows
absolute path (e.g., "C:\\tmp\\hacked.feature"), and root/empty outputs (e.g.,
"" and ".") each throws the same VspecError("INVALID_ARGUMENT", "Output path
must be within the project root."); place these assertions alongside the
existing traversal test in the gherkin.test.ts file so exportGherkinโ€™s
validation logic is exercised for ../ traversal, absolute paths, Windows-style
absolute paths, and empty/root output values.
๐Ÿช„ Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

โ„น๏ธ Review info
โš™๏ธ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8ec24c9b-e7d0-422e-a848-c17691ba2195

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between f4223cb and c51abf7.

๐Ÿ“’ Files selected for processing (3)
  • .jules/sentinel.md
  • src/export/gherkin.ts
  • tests/gherkin.test.ts

Comment thread src/export/gherkin.ts
Comment on lines +43 to +47
const outputPath = resolve(config.root, output);
const rel = relative(config.root, outputPath);
if (rel.startsWith("..") || isAbsolute(rel)) {
throw new VspecError("INVALID_ARGUMENT", "Output path must be within the project root.");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue | ๐ŸŸ  Major

๐Ÿงฉ Analysis chain

๐Ÿ Script executed:

#!/bin/bash
# Description: Check for symlinks in the repository and assess security impact

# Find all symlinks in the repository
echo "=== Symlinks in repository ==="
fd -t l -H

# Check if the codebase has any symlink-related security documentation
echo -e "\n=== Symlink security documentation ==="
rg -i "symlink" --type md -C 2

Repository: Seongho-Bae/vooster-v2-mvp

Length of output: 141


๐Ÿ Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target file around the referenced lines
echo "=== src/export/gherkin.ts (lines 1-120) ==="
nl -ba src/export/gherkin.ts | sed -n '1,120p'

# Show where config.root and output come from (best-effort)
echo -e "\n=== Search for config.root usage in src/export/gherkin.ts ==="
rg -n "config\.root" src/export/gherkin.ts

echo -e "\n=== Search for output variable derivation in src/export/gherkin.ts ==="
rg -n "output" src/export/gherkin.ts

# Check for existing realpath / realpathSync usage across the repo
echo -e "\n=== Search for realpath(realpathSync) usage in src ==="
rg -n "realpath(Sync)?\(" src

# Search for other path boundary validation patterns in src
echo -e "\n=== Search for resolve+relative boundary checks patterns in src ==="
rg -n "relative\\(.*config\\.root|relative\\(.*root|INVALID_ARGUMENT.*Output path|startsWith\\(\"\\.\\.\"\\).*isAbsolute" src || true

Repository: Seongho-Bae/vooster-v2-mvp

Length of output: 175


๐Ÿ Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target file around the referenced lines
echo "=== src/export/gherkin.ts (lines 1-200) ==="
awk '{printf "%d:%s\n", NR, $0}' src/export/gherkin.ts | sed -n '1,200p'

# Show where config.root and output come from (best-effort)
echo -e "\n=== Search for config.root usage in src/export/gherkin.ts ==="
rg -n "config\.root" src/export/gherkin.ts || true

echo -e "\n=== Search for output variable derivation in src/export/gherkin.ts ==="
rg -n "\boutput\b" src/export/gherkin.ts || true

# Check for existing realpath / realpathSync usage across the repo
echo -e "\n=== Search for realpath(realpathSync) usage in src ==="
rg -n "realpath(Sync)?\(" src || true

# Search for other path boundary validation patterns in src
echo -e "\n=== Search for resolve+relative boundary checks patterns in src ==="
rg -n "relative\(|resolve\(|INVALID_ARGUMENT|Output path must be within|startsWith\(\"\\.\\.\"\\)|isAbsolute\(" src || true

Repository: Seongho-Bae/vooster-v2-mvp

Length of output: 5884


๐Ÿ Script executed:

#!/bin/bash
set -euo pipefail

echo "=== src/files.ts (head/tail + key parts) ==="
wc -l src/files.ts
sed -n '1,120p' src/files.ts
sed -n '120,260p' src/files.ts

echo -e "\n=== src/validate/doctor.ts (root/target ์ฒ˜๋ฆฌ) ==="
wc -l src/validate/doctor.ts
sed -n '1,160p' src/validate/doctor.ts

echo -e "\n=== src/project.ts (root resolve ์—ฌ๋ถ€) ==="
wc -l src/project.ts
sed -n '1,120p' src/project.ts

Repository: Seongho-Bae/vooster-v2-mvp

Length of output: 12646


๐Ÿ Script executed:

#!/bin/bash
set -euo pipefail

echo -e "\n=== exportGherkin ํ˜ธ์ถœ ์ง€์  ๊ฒ€์ƒ‰ ==="
rg -n "exportGherkin\\(" src || true

echo -e "\n=== args\\.output ๋˜๋Š” --output ๊ด€๋ จ CLI ์˜ต์…˜ ๊ฒ€์ƒ‰ ==="
rg -n "--output|args\\.output|output\\?:" src || true

echo -e "\n=== src/usecase-commands.ts ๊ด€๋ จ ์ถœ๋ ฅ ๊ฒฝ๋กœ ์ƒ์„ฑ/๊ฒ€์ฆ ==="
rg -n "output" src/usecase-commands.ts || true

echo -e "\n=== src/cli.ts ์˜ต์…˜/์ปค๋งจ๋“œ์—์„œ output ์ฒ˜๋ฆฌ ๊ฒ€์ƒ‰ ==="
rg -n "output" src/cli.ts || true

echo -e "\n=== src/output.ts / ๊ธฐํƒ€ ํŒŒ์ผ์—์„œ ๊ฒฝ๋กœ ๊ฒ€์ฆ ๋กœ์ง ๊ฒ€์ƒ‰ ==="
rg -n "INVALID_ARGUMENT.*Output|Output path|realpath|symlink|lstat|lstatSync|realpathSync|resolve\\(.*root" src || true

Repository: Seongho-Bae/vooster-v2-mvp

Length of output: 2038


๐Ÿ Script executed:

#!/bin/bash
set -euo pipefail

# Show relevant CLI output option handling around the reported line numbers
wc -l src/cli.ts
sed -n '240,320p' src/cli.ts

# Inspect where outputSuccess/outputError wrapping happens (might contain validation)
sed -n '1,120p' src/output.ts
wc -l src/output.ts
sed -n '1,140p' src/output.ts

# Double-check no other boundary validation exists for output paths
rg -n "realpath(Sync)?\\(|lstat(Sync)?\\(|readlink(Sync)?\\(|symlink" src || true
rg -n "Output path must be within|INVALID_ARGUMENT\".*,.*Output|--output|options\\.output|exportGherkin\\(\\{ key, output" src/cli.ts src/export/gherkin.ts || true

Repository: Seongho-Bae/vooster-v2-mvp

Length of output: 11940


[๊ฒฝ๋กœ ๊ฒ€์ฆ ์šฐํšŒ ๊ฐ€๋Šฅ์„ฑ ๋ณด๊ฐ• ํ•„์š”] src/export/gherkin.ts (43-47) ์˜ resolve + relative + (..)/isAbsolute ๊ฒ€์ฆ์€ ๋ฌธ์ž์—ด ๊ฒฝ๋กœ ๊ฒฝ๊ณ„๋งŒ ํ™•์ธํ•˜๋ฏ€๋กœ, config.root ๋‚ด๋ถ€์— ์žˆ๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ/๊ฒฝ๋กœ ๊ตฌ์„ฑ์š”์†Œ๊ฐ€ symlink์œผ๋กœ ์™ธ๋ถ€๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฒฝ์šฐ mkdirSync(dirname(outputPath)) ๋ฐ writeFileSync(outputPath, ...)๊ฐ€ ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ ๋ฐ–์— ํŒŒ์ผ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ฝ”๋“œ๋ฒ ์ด์Šค์—์„œ ์ด ๊ฒฝ๋กœ ๊ฒ€์ฆ์„ realpathSync/realpath์ฒ˜๋Ÿผ ์‹ค์ œ ๊ฒฝ๋กœ( symlink ํ•ด์„ ) ๊ธฐ์ค€์œผ๋กœ ๋‹ค์‹œ ํ™•์ธํ•˜๋Š” ์ฒ˜๋ฆฌ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. fs.realpathSync()(๋˜๋Š” ์กด์žฌํ•˜๋Š” ์ƒ์œ„ ๊ฒฝ๋กœ ๊ธฐ์ค€์œผ๋กœ realpath ํ›„)๋กœ ์‹ค์ œ ๊ฒฝ๋กœ๋ฅผ ๋น„๊ตํ•˜๊ฑฐ๋‚˜, ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ๋ณ„๋กœ lstatSync๋กœ symlink์„ ์ฐจ๋‹จํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ณด์™„ํ•˜์„ธ์š”.

๐Ÿค– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/export/gherkin.ts` around lines 43 - 47, The current check uses
resolve/relative and isAbsolute on outputPath but does not resolve symlinks, so
an attacker can escape the project root via symlinked directories; update the
validation around outputPath (the computed outputPath and rel variables used
before throwing VspecError) to compare real filesystem paths: obtain real paths
for config.root and the output's nearest existing ancestor (using
fs.realpathSync or async fs.realpath) and ensure the real output path starts
with the real root, or alternatively walk path segments of outputPath and
lstatSync each segment to reject any symlinked component before calling
mkdirSync(dirname(outputPath)) or writeFileSync(outputPath,...). Ensure the same
checks are applied wherever outputPath is created/used.

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