Skip to content

🛡️ Sentinel: [HIGH] Fix CSV Formula Injection (DDE) bypass via leading whitespace - #378

Closed
seonghobae wants to merge 3 commits into
developfrom
sentinel-fix-csv-formula-whitespace-bypass-18083188102511554276
Closed

🛡️ Sentinel: [HIGH] Fix CSV Formula Injection (DDE) bypass via leading whitespace#378
seonghobae wants to merge 3 commits into
developfrom
sentinel-fix-csv-formula-whitespace-bypass-18083188102511554276

Conversation

@seonghobae

@seonghobae seonghobae commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

🚨 Severity: HIGH
💡 Vulnerability: The backend CSV export for audit logs mitigated CSV injection by prefixing fields beginning with formula characters (=, +, -, @, |) with a single quote. However, the regex /^[=+\-@|]/ failed to account for leading whitespace.
🎯 Impact: Spreadsheet software like Excel and Google Sheets typically ignores leading spaces when executing DDE (Dynamic Data Exchange) formulas. An attacker could input a payload like =cmd|' /C calc'!A0, bypassing the filter entirely and causing arbitrary code execution on the machine of an admin downloading the audit log.
🔧 Fix: Updated the regex to /^\s*[=+\-@|]/ across server/app.mjs (matching the frontend logic in app.js) to ensure all payloads with malicious command prefixes are properly neutralized, regardless of preceding whitespace.
Verification: Verified via updated test assertion in tests/api/smoke.mjs. Tests passed.


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

Summary by CodeRabbit

  • 버그 수정

    • 감사 로그 CSV 내보내기에서 선행 공백 뒤에 수식 문자가 오는 값도 감지해 스프레드시트 수식 주입 위험을 완화했습니다.
    • 프로젝트 XML에서 태그 값을 더 안정적으로 추출하고, 불완전한 태그 처리와 값 정리를 일관되게 유지했습니다.
  • 테스트

    • 선행 공백과 =, +, -, @, | 문자를 포함한 CSV 보안 검증을 강화했습니다.
  • 문서

    • CSV 수식 중화 처리의 적용 기준과 우회 방지 지침을 보완했습니다.

…g whitespace

* Updated the sanitization regex in `server/app.mjs` for CSV exports from `/^[=+\-@|]/` to `/^\s*[=+\-@|]/`.
* This prevents attackers from bypassing formula injection neutralization (e.g. ` =cmd|' /C calc'!A0`) because spreadsheet software ignores leading whitespace when executing DDE payloads.
* Added a learning entry to `.jules/sentinel.md`.
* Updated `tests/api/smoke.mjs` to reflect the new strict regex assertion.
@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 Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@seonghobae, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 36 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 38cc919f-baec-4cd4-b28c-4d3135abea5c

📥 Commits

Reviewing files that changed from the base of the PR and between 3c5e6f9 and fd517a7.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (1)
  • package.json
📝 Walkthrough

Walkthrough

MS Project XML 태그 추출이 정규식 매칭에서 위치 기반 방식으로 변경되었습니다. 감사 로그 CSV는 선행 공백 뒤의 수식·DDE 접두를 탐지하도록 중화 규칙, 테스트, 보안 지침이 갱신되었습니다.

Changes

XML 태그 추출

Layer / File(s) Summary
태그 위치 기반 추출
cloud-sync.js
parseMsProjectXml의 태그 값 추출이 indexOfsubstring을 사용하도록 변경되었으며, 태그가 없으면 빈 문자열을 반환합니다.

CSV 수식 주입 탐지

Layer / File(s) Summary
CSV 중화 규칙과 검증
server/app.mjs, tests/api/smoke.mjs, .jules/sentinel.md
CSV 값 앞의 공백을 허용해 수식·DDE 접두를 탐지하고, 파이프 문자를 차단 목록에 포함하도록 구현·테스트·보안 지침을 갱신했습니다.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 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 수식/DDE 주입 우회 차단이라는 핵심 변경을 정확히 요약합니다.
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 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
📝 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-whitespace-bypass-18083188102511554276

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

…g whitespace

* Updated the sanitization regex in server/app.mjs for CSV exports from /^[=+\-@|]/ to /^\s*[=+\-@|]/.
* This prevents attackers from bypassing formula injection neutralization (e.g.  =cmd|' /C calc'!A0) because spreadsheet software ignores leading whitespace when executing DDE payloads.
* Added a learning entry to .jules/sentinel.md.
* Updated tests/api/smoke.mjs to reflect the new strict regex assertion.
* Fixed Semgrep SAST warning regarding RegExp construction in cloud-sync.js.

@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 (1)
tests/api/smoke.mjs (1)

500-500: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

중화된 셀 값을 직접 검증하도록 테스트를 보강해 주세요.

현재 검사는 두 번째 물리적 줄이 위험한 접두로 시작하지 않는지만 확인합니다. 선행 공백 payload가 실제로 존재하고 해당 셀이 '로 시작하도록 중화됐는지도 별도로 검증해야, 필드 누락이나 fixture 변경으로 테스트가 거짓 양성되는 것을 막을 수 있습니다.

🤖 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/api/smoke.mjs` at line 500, 보안 검증 테스트에서 auditCsv의 두 번째 물리적 줄 존재 여부와 선행
공백 payload가 포함된 대상 셀의 값을 직접 검증하도록 보강하세요. 해당 셀이 작은따옴표로 시작해 중화됐는지 별도 assertion으로
확인하고, 기존 위험 접두사 검사도 유지해 필드 누락이나 fixture 변경으로 인한 거짓 양성을 방지하세요.
🤖 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 `@tests/api/smoke.mjs`:
- Line 500: 보안 검증 테스트에서 auditCsv의 두 번째 물리적 줄 존재 여부와 선행 공백 payload가 포함된 대상 셀의 값을
직접 검증하도록 보강하세요. 해당 셀이 작은따옴표로 시작해 중화됐는지 별도 assertion으로 확인하고, 기존 위험 접두사 검사도 유지해 필드
누락이나 fixture 변경으로 인한 거짓 양성을 방지하세요.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f38280a-78e6-4ff6-b2fc-c46561146e9e

📥 Commits

Reviewing files that changed from the base of the PR and between a756b7e and 3c5e6f9.

📒 Files selected for processing (4)
  • .jules/sentinel.md
  • cloud-sync.js
  • server/app.mjs
  • tests/api/smoke.mjs

…g whitespace and ReDoS SAST warning

* Updated the sanitization regex in server/app.mjs for CSV exports from /^[=+\-@|]/ to /^\s*[=+\-@|]/.
* This prevents attackers from bypassing formula injection neutralization (e.g.  =cmd|' /C calc'!A0) because spreadsheet software ignores leading whitespace when executing DDE payloads.
* Added a learning entry to .jules/sentinel.md.
* Updated tests/api/smoke.mjs to reflect the new strict regex assertion.
* Fixed Semgrep SAST warning regarding RegExp construction in cloud-sync.js.
* Updated @hono/node-server to fix Trivy SAST warning.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

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

  • Head SHA: fd517a70bb309a99ff54fb0fed554dcdd6413bf8

  • Workflow run: 30514361201

  • 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 (6 files)"]
  S1 --> I1["repository behavior"]
  I1 --> R1["Review risk: Changed file (6 files)"]
  R1 --> V1["required checks"]
  Evidence --> S2["Test: smoke.mjs"]
  S2 --> I2["regression suite"]
  I2 --> R2["Review risk: Test: smoke.mjs"]
  R2 --> V2["targeted test run"]
Loading

@opencode-agent

Copy link
Copy Markdown
Contributor

OpenCode Review Overview

  • Head SHA: fd517a70bb309a99ff54fb0fed554dcdd6413bf8
  • Workflow run: 30514361201
  • 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 fd517a70bb309a99ff54fb0fed554dcdd6413bf8.

  • Head SHA: fd517a70bb309a99ff54fb0fed554dcdd6413bf8

  • Workflow run: 30514361201

  • 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 (6 files)"]
  S1 --> I1["repository behavior"]
  I1 --> R1["Review risk: Changed file (6 files)"]
  R1 --> V1["required checks"]
  Evidence --> S2["Test: smoke.mjs"]
  S2 --> I2["regression suite"]
  I2 --> R2["Review risk: Test: smoke.mjs"]
  R2 --> V2["targeted test run"]
Loading

@seonghobae

Copy link
Copy Markdown
Contributor Author

Superseded by #386 — clean combined fix: @hono/node-server ^2.0.12 (GHSA-frvp-7c67-39w9) + hono ^4.12.32 + server audit CSV whitespace formula guard with regression smoke. Closing to reduce PR queue noise.

@seonghobae seonghobae closed this Jul 31, 2026
@google-labs-jules

Copy link
Copy Markdown

Superseded by #386 — clean combined fix: @hono/node-server ^2.0.12 (GHSA-frvp-7c67-39w9) + hono ^4.12.32 + server audit CSV whitespace formula guard with regression smoke. Closing to reduce PR queue noise.

Understood. Acknowledging that this work is now obsolete 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