Skip to content

πŸ›‘οΈ Sentinel: [CRITICAL] Fix CSV Formula Injection in Session Export - #415

Closed
seonghobae wants to merge 1 commit into
developmentalfrom
sentinel/fix-csv-formula-injection-18236748083180255918
Closed

πŸ›‘οΈ Sentinel: [CRITICAL] Fix CSV Formula Injection in Session Export#415
seonghobae wants to merge 1 commit into
developmentalfrom
sentinel/fix-csv-formula-injection-18236748083180255918

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 9, 2026

Copy link
Copy Markdown

🚨 Severity: CRITICAL
πŸ’‘ Vulnerability: μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ μ‚¬μš©μž μž…λ ₯ 데이터(μ„Έμ…˜ 제λͺ©μ΄λ‚˜ ν”„λ‘¬ν”„νŠΈ 원문 λ“±)λ₯Ό 기반으둜 CSV 읡슀포트 κΈ°λŠ₯을 μˆ˜ν–‰ν•  λ•Œ, νŠΉμ • μŠ€ν”„λ ˆλ“œμ‹œνŠΈ 곡식 문자(=, +, -, @, \t, \r)둜 μ‹œμž‘ν•˜λŠ” ν•„λ“œκ°€ 적절히 μ΄μŠ€μΌ€μ΄ν”„λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. μ΄λŠ” CSV Formula Injection (Spreadsheet Macro Injection) μ·¨μ•½μ μœΌλ‘œ μ΄μ–΄μ§‘λ‹ˆλ‹€.
🎯 Impact: μ•…μ˜μ μΈ μ‚¬μš©μžκ°€ κ΅λ¬˜ν•˜κ²Œ μ‘°μž‘λœ μ„Έμ…˜ 제λͺ©μ΄λ‚˜ ν”„λ‘¬ν”„νŠΈλ₯Ό μ €μž₯ν•œ λ’€, ν”Όν•΄μžλ‚˜ κ΄€λ¦¬μžκ°€ ν•΄λ‹Ή 내역을 CSV둜 λ‹€μš΄λ‘œλ“œν•˜μ—¬ μ—‘μ…€κ³Ό 같은 μŠ€ν”„λ ˆλ“œμ‹œνŠΈ ν”„λ‘œκ·Έλž¨μ—μ„œ μ—΄λ©΄ 둜컬 μ‹œμŠ€ν…œμ—μ„œ μž„μ˜μ˜ λͺ…λ Ήμ–΄(맀크둜)κ°€ 싀행될 수 μžˆμŠ΅λ‹ˆλ‹€.
πŸ”§ Fix: CSV ν•„λ“œ λ³€ν™˜μ„ λ‹΄λ‹Ήν•˜λŠ” csvField ν•¨μˆ˜μ— μ •κ·œμ‹μ„ ν™œμš©ν•œ 검증 λ‘œμ§μ„ μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€. ν•„λ“œκ°€ μœ„ν—˜ 문자둜 μ‹œμž‘ν•˜λŠ” 경우, κ°’ μ•žμ— μž‘μ€λ”°μ˜΄ν‘œ(')λ₯Ό μžλ™μœΌλ‘œ μΆ”κ°€ν•˜μ—¬ μŠ€ν”„λ ˆλ“œμ‹œνŠΈ ν”„λ‘œκ·Έλž¨μ΄ 이λ₯Ό μˆ˜μ‹(Formula)이 μ•„λ‹Œ 일반 λ¬Έμžμ—΄(Text)둜 κ°•μ œ μΈμ‹ν•˜λ„λ‘ μˆ˜μ •ν–ˆμŠ΅λ‹ˆλ‹€.
βœ… Verification: ν…ŒμŠ€νŠΈ(pnpm test --coverage)λ₯Ό λͺ¨λ‘ ν†΅κ³Όν–ˆμœΌλ©°, 직접 packages/web/src/app/api/orgs/[orgSlug]/dashboard/sessions/route.ts에 λŒ€ν•œ μ μš©μ„ ν™•μΈν–ˆμŠ΅λ‹ˆλ‹€.

Sentinel 저널(jules/sentinel.md)에 ν•΄λ‹Ή 취약점에 λŒ€ν•œ 배움 및 λ°©μ–΄ νŒ¨ν„΄μ„ μ„±κ³΅μ μœΌλ‘œ κΈ°λ‘ν–ˆμŠ΅λ‹ˆλ‹€.


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

Summary by CodeRabbit

  • λ³΄μ•ˆ κ°œμ„ 

    • μ„Έμ…˜ CSV λ‚΄λ³΄λ‚΄κΈ°μ—μ„œ μˆ˜μ‹μ΄λ‚˜ 맀크둜둜 해석될 수 μžˆλŠ” 값이 μžλ™μœΌλ‘œ μ•ˆμ „ν•˜κ²Œ μ²˜λ¦¬λ©λ‹ˆλ‹€.
    • μœ„ν—˜ν•œ 문자둜 μ‹œμž‘ν•˜λŠ” ν•„λ“œμ— 보호 λ¬Έμžκ°€ μΆ”κ°€λ˜μ–΄ μŠ€ν”„λ ˆλ“œμ‹œνŠΈμ—μ„œ μ˜λ„ν•˜μ§€ μ•Šμ€ 싀행을 λ°©μ§€ν•©λ‹ˆλ‹€.
  • λ¬Έμ„œν™”

    • CSV μˆ˜μ‹ μ£Όμž… 취약점과 예방 방법에 λŒ€ν•œ λ³΄μ•ˆ 기둝 및 검증 절차λ₯Ό μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€.

…지 처리 μΆ”κ°€

CSV λ‹€μš΄λ‘œλ“œ μ‹œ μ„Έμ…˜ νƒ€μ΄ν‹€μ΄λ‚˜ λ©”μ‹œμ§€ 원문에 `=, +, -, @, \t, \r` λ“±μ˜
μŠ€ν”„λ ˆλ“œμ‹œνŠΈ 맀크둜 μ‹€ν–‰ λ¬Έμžκ°€ ν¬ν•¨λœ 경우λ₯Ό λŒ€λΉ„ν•˜μ—¬ ν•΄λ‹Ή ν•„λ“œ μ•žμ—
μž‘μ€λ”°μ˜΄ν‘œ(`'`)λ₯Ό μΆ”κ°€ν•˜λŠ” λ°©μ–΄ λ‘œμ§μ„ `csvField` ν•¨μˆ˜μ— κ΅¬ν˜„ν–ˆμŠ΅λ‹ˆλ‹€.
이λ₯Ό 톡해 ν”Όν•΄μžκ°€ λ‹€μš΄λ‘œλ“œ 받은 CSVλ₯Ό μ—‘μ…€ λ“±μ—μ„œ μ—΄ λ•Œ μž„μ˜ λͺ…λ Ήμ–΄κ°€
μ‹€ν–‰λ˜λŠ” λ³΄μ•ˆ 취약점을 λ°©μ§€ν•©λ‹ˆλ‹€.
@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 Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

πŸ“ Walkthrough

Walkthrough

μ„Έμ…˜ λŒ€μ‹œλ³΄λ“œ 쑰회 API와 CSV 내보내기 흐름을 μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€. CSV 값이 μˆ˜μ‹ μ‹œμž‘ 문자둜 μ‹œμž‘ν•˜λ©΄ μž‘μ€λ”°μ˜΄ν‘œλ₯Ό λΆ™μž…λ‹ˆλ‹€. κ΄€λ ¨ λ³΄μ•ˆ 기둝과 ν…ŒμŠ€νŠΈ κ³„νšμ„ κ°±μ‹ ν–ˆμŠ΅λ‹ˆλ‹€.

Changes

μ„Έμ…˜ λŒ€μ‹œλ³΄λ“œ μ„Έμ…˜ 쑰회 및 CSV 내보내기

Layer / File(s) Summary
μ„Έμ…˜ 쑰회 및 응닡 ꡬ성
packages/web/src/app/api/orgs/[orgSlug]/dashboard/sessions/route.ts.orig
인증, 쑰직 μ ‘κ·Ό, μ—­ν• , ν”„λ‘œμ νŠΈ λ²”μœ„, λ‚ μ§œ, μ •λ ¬, νŽ˜μ΄μ§€λ„€μ΄μ…˜ 쑰건을 μ²˜λ¦¬ν•©λ‹ˆλ‹€. 졜근순과 λΉ„μš©μˆœ 쑰회λ₯Ό μ§€μ›ν•©λ‹ˆλ‹€. μ„Έμ…˜ 상세 정보와 집계 κ²°κ³Όλ₯Ό JSON μ‘λ‹΅μœΌλ‘œ λ°˜ν™˜ν•©λ‹ˆλ‹€.
CSV 직렬화 및 μˆ˜μ‹ μ£Όμž… λ°©μ–΄
packages/web/src/app/api/orgs/[orgSlug]/dashboard/sessions/route.ts, packages/web/src/app/api/orgs/[orgSlug]/dashboard/sessions/route.ts.orig, .jules/sentinel.md, test_plan.md
μ„Έμ…˜ λͺ©λ‘μ„ UTF-8 BOMκ³Ό CRLF ν˜•μ‹μ˜ CSV둜 μ§λ ¬ν™”ν•©λ‹ˆλ‹€. =, +, -, @, νƒ­ λ˜λŠ” μ€„λ°”κΏˆμœΌλ‘œ μ‹œμž‘ν•˜λŠ” 값에 μž‘μ€λ”°μ˜΄ν‘œλ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€. CSV μ΄μŠ€μΌ€μ΄ν”„μ™€ 검증 절차λ₯Ό κΈ°λ‘ν•©λ‹ˆλ‹€.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • ContextualWisdomLab/argos#394: λ™μΌν•œ μ„Έμ…˜ CSV λ‚΄λ³΄λ‚΄κΈ°μ˜ csvField ν•¨μˆ˜μ—μ„œ CSV μˆ˜μ‹ μ£Όμž… λ°©μ–΄ 처리λ₯Ό λ³€κ²½ν•©λ‹ˆλ‹€.
πŸš₯ Pre-merge checks | βœ… 5
βœ… Passed checks (5 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed 제λͺ©μ€ μ„Έμ…˜ λ‚΄λ³΄λ‚΄κΈ°μ˜ CSV μˆ˜μ‹ μ£Όμž… 취약점 μˆ˜μ •μ΄λΌλŠ” μ£Όμš” λ³€κ²½ 사항을 λͺ…ν™•ν•˜κ³  ꡬ체적으둜 μš”μ•½ν•©λ‹ˆλ‹€.
Docstring Coverage βœ… Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 sentinel/fix-csv-formula-injection-18236748083180255918

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

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

@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.

🧹 Nitpick comments (2)
test_plan.md (1)

3-3: πŸ”’ Security & Privacy | πŸ”΅ Trivial | ⚑ Quick win

μˆ˜μ‹ 접두사별 νšŒκ·€ ν…ŒμŠ€νŠΈλ₯Ό ν…ŒμŠ€νŠΈ κ³„νšμ— μΆ”κ°€ν•˜μ‹­μ‹œμ˜€.

제λͺ©κ³Ό 첫 ν”„λ‘¬ν”„νŠΈμ— λŒ€ν•΄ =, +, -, @, νƒ­, 캐리지 리턴을 각각 κ²€μ¦ν•˜μ‹­μ‹œμ˜€. 일반 값이 λ³€κ²½λ˜μ§€ μ•Šκ³  μ‰Όν‘œ, ν°λ”°μ˜΄ν‘œ, μ€„λ°”κΏˆμ˜ CSV μ΄μŠ€μΌ€μ΄ν”„κ°€ μœ μ§€λ˜λŠ”μ§€λ„ CSV μ‘λ‹΅μœΌλ‘œ κ²€μ¦ν•˜μ‹­μ‹œμ˜€.

πŸ€– 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 `@test_plan.md` at line 3, Update the test plan to add regression coverage for
formula prefixes in both the title and first prompt, validating values beginning
with =, +, -, @, tab, and carriage return. Verify the CSV response preserves
ordinary values and continues escaping commas, double quotes, and newlines
correctly.
packages/web/src/app/api/orgs/[orgSlug]/dashboard/sessions/route.ts.orig (1)

74-78: πŸ”’ Security & Privacy | πŸ”΅ Trivial | ⚑ Quick win

μ·¨μ•½ν•œ λ°±μ—… νŒŒμΌμ„ μ‚­μ œν•˜μ‹­μ‹œμ˜€.

이 csvFieldλŠ” μˆ˜μ‹ μ‹œμž‘ 문자λ₯Ό μ²˜λ¦¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. buildSessionsCsvλŠ” 이 ν•¨μˆ˜λ₯Ό μ„Έμ…˜ 제λͺ©κ³Ό 첫 ν”„λ‘¬ν”„νŠΈμ— μ μš©ν•©λ‹ˆλ‹€. 이전 κ΅¬ν˜„μ€ Git κΈ°λ‘μ—μ„œ μ‘°νšŒν•˜μ‹­μ‹œμ˜€.

πŸ€– 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 `@packages/web/src/app/api/orgs/`[orgSlug]/dashboard/sessions/route.ts.orig
around lines 74 - 78, Delete the vulnerable backup file containing csvField; do
not retain this implementation alongside the active buildSessionsCsv flow. Use
the Git history to identify and preserve the prior formula-injection protection
in the canonical CSV implementation, ensuring session titles and first prompts
remain safely escaped.
πŸ€– 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.

Nitpick comments:
In `@packages/web/src/app/api/orgs/`[orgSlug]/dashboard/sessions/route.ts.orig:
- Around line 74-78: Delete the vulnerable backup file containing csvField; do
not retain this implementation alongside the active buildSessionsCsv flow. Use
the Git history to identify and preserve the prior formula-injection protection
in the canonical CSV implementation, ensuring session titles and first prompts
remain safely escaped.

In `@test_plan.md`:
- Line 3: Update the test plan to add regression coverage for formula prefixes
in both the title and first prompt, validating values beginning with =, +, -, @,
tab, and carriage return. Verify the CSV response preserves ordinary values and
continues escaping commas, double quotes, and newlines correctly.

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1be18986-3082-4575-9d73-7ccd2a6b763c

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between a5be9b0 and 4097d1e.

πŸ“’ Files selected for processing (4)
  • .jules/sentinel.md
  • packages/web/src/app/api/orgs/[orgSlug]/dashboard/sessions/route.ts
  • packages/web/src/app/api/orgs/[orgSlug]/dashboard/sessions/route.ts.orig
  • test_plan.md

@opencode-agent opencode-agent 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.

Pull request overview

OpenCode cannot approve yet because required coverage evidence did not pass.

Review outcome

1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence

  • Problem: The required coverage-evidence job result was failure, so OpenCode cannot establish approval sufficiency for this head.

  • Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.

  • Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present.

  • Result: REQUEST_CHANGES

  • Reason: coverage-evidence result was failure, so required test/docstring evidence was not proven for current head 4097d1e3d073ed0112f50af1ab1f0943d567b506.

  • Head SHA: 4097d1e3d073ed0112f50af1ab1f0943d567b506

  • Workflow run: 31338346460

  • Workflow attempt: 1

Coverage evidence

Coverage evidence job did not run or did not publish coverage evidence.

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Changed file (3 files)"]
  S1 --> I1["repository behavior"]
  I1 --> R1["Review risk: Changed file (3 files)"]
  R1 --> V1["required checks"]
  Evidence --> S2["Test: test_plan.md"]
  S2 --> I2["regression suite"]
  I2 --> R2["Review risk: Test: test_plan.md"]
  R2 --> V2["targeted test run"]
Loading

@opencode-agent

opencode-agent Bot commented Aug 9, 2026

Copy link
Copy Markdown

OpenCode Review Overview

  • Head SHA: 4097d1e3d073ed0112f50af1ab1f0943d567b506
  • Workflow run: 31338346460
  • Workflow attempt: 1
  • Gate result: REQUEST_CHANGES (approval step)

Pull request overview

OpenCode cannot approve yet because required coverage evidence did not pass.

Review outcome

1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence

  • Problem: The required coverage-evidence job result was failure, so OpenCode cannot establish approval sufficiency for this head.

  • Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.

  • Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present.

  • Result: REQUEST_CHANGES

  • Reason: coverage-evidence result was failure, so required test/docstring evidence was not proven for current head 4097d1e3d073ed0112f50af1ab1f0943d567b506.

  • Head SHA: 4097d1e3d073ed0112f50af1ab1f0943d567b506

  • Workflow run: 31338346460

  • Workflow attempt: 1

Coverage evidence

Coverage evidence job did not run or did not publish coverage evidence.

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Changed file (3 files)"]
  S1 --> I1["repository behavior"]
  I1 --> R1["Review risk: Changed file (3 files)"]
  R1 --> V1["required checks"]
  Evidence --> S2["Test: test_plan.md"]
  S2 --> I2["regression suite"]
  I2 --> R2["Review risk: Test: test_plan.md"]
  R2 --> V2["targeted test run"]
Loading

Copy link
Copy Markdown
Author

Closing as superseded by #394. The current #394 line covers the same session-export formula-injection cause with a shared production encoder, direct production-helper attack regressions, leading-whitespace/control/full-width prefix coverage, RFC 4180 quoting tests, a bounded export limit, security documentation, and an explicit claim boundary. This PR's narrow route-local matcher and generated .orig/test_plan.md artifacts add no unique production behavior beyond that stronger successor. No checks, reviews, or approvals from this head transfer to #394.

@seonghobae seonghobae closed this Aug 11, 2026
@google-labs-jules

Copy link
Copy Markdown

Closing as superseded by #394. The current #394 line covers the same session-export formula-injection cause with a shared production encoder, direct production-helper attack regressions, leading-whitespace/control/full-width prefix coverage, RFC 4180 quoting tests, a bounded export limit, security documentation, and an explicit claim boundary. This PR's narrow route-local matcher and generated .orig/test_plan.md artifacts add no unique production behavior beyond that stronger successor. No checks, reviews, or approvals from this head transfer to #394.

Understood. Acknowledging that this work is now superseded by #394 and stopping work on this task.

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