Skip to content

fix(security): enforce session revocation on URL-token endpoints - #397

Closed
seonghobae wants to merge 23 commits into
developfrom
sentinel-fix-calendar-stream-revocation-11666540507854451077
Closed

fix(security): enforce session revocation on URL-token endpoints#397
seonghobae wants to merge 23 commits into
developfrom
sentinel-fix-calendar-stream-revocation-11666540507854451077

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Close a HIGH-severity session-revocation gap in every project endpoint that accepts a session JWT outside the normal bearer middleware:

  • /api/projects/:id/calendar.ics?token=...
  • /api/projects/:id/stream?token=...
  • /api/projects/:id/attachments/:attachment_id/view?token=...

A shared verifySessionJwt boundary verifies the JWT signature, loads the referenced user, and compares the token tv claim with the current database token_version. Missing users, malformed tokens, expired tokens, and stale sessions fail closed with 401 Unauthorized. PAT header paths remain available where supported.

Security impact

Before this change, a query-transport session token could remain usable until expiration after password change or logout all devices incremented token_version. A leaked token could therefore continue reading project data through URL-token endpoints.

This PR establishes consistent revocation semantics on the existing authentication model. It is a prerequisite for issue #413, which will replace full session JWTs in URLs with narrowly scoped ephemeral grants and separately revocable calendar subscription secrets.

Regression coverage

tests/api/session-revocation.test.mjs creates two concurrent device sessions and proves:

  1. calendar, SSE, and attachment-view authentication accept both live tokens;
  2. both stale tokens fail with 401 on all three URL-token transports after logout-all;
  3. the replacement token continues through authentication on all three transports;
  4. bearer middleware uses the same verifySessionJwt security boundary.

The attachment test uses a deliberately missing attachment: a valid session reaches tenant-scoped lookup and receives 404, while a stale session is rejected earlier with 401.

Included baseline synchronization

The branch carries the linear MS Project XML parser and current dependency/runtime lock synchronization required for clean security and CI evidence against the present develop base. Overlapping changes collapse from the final diff after PR #386 merges.

Verification

Current head 5ed7fa125bcf63df4bb548d8bc244ac4ddf0054c completed successfully:

  • Server Tests
  • Security Scan
  • SAST Semgrep
  • Dependency Review
  • OSV Scanner
  • Fuzz
  • CodeRabbit status
  • npm run test:unit
  • npm run test:api
  • npm run coverage
  • all inline review threads resolved

The completed one-shot repair workflow was removed before merge so no self-modifying write-token workflow can enter develop. The security verification note now covers calendar, SSE, and attachment-view authentication, including valid-session 404 versus revoked-session 401 ordering.

CHANGELOG.md records both the centralized session-revocation invariant and the cross-device URL-transport regression evidence.

Merge policy

Auto-merge is enabled. Merge only after exact-head checks remain successful and a non-author approval satisfies branch protection.

The `/api/projects/:id/calendar.ics` and `/api/projects/:id/stream`
endpoints accept auth tokens via query parameters. While they verified
the JWT signature, they failed to query the database to ensure the
user's `token_version` matched the token's payload.

This allowed revoked tokens (e.g. after a user logged out everywhere,
changed passwords, or deleted their account) to continue accessing
project data via these specialized endpoints.

This commit updates both endpoints to perform the same database
verification against `users.token_version` as the primary middleware.
@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 2, 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: 1 minute

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: 6fbf015a-0fd0-4df3-8eda-022060660bf3

📥 Commits

Reviewing files that changed from the base of the PR and between 7194681 and 5ed7fa1.

📒 Files selected for processing (4)
  • .jules/verification-session-revocation.md
  • CHANGELOG.md
  • server/app.mjs
  • tests/api/session-revocation.test.mjs
📝 Walkthrough

Walkthrough

Changes

세션 폐기 인증

Layer / File(s) Summary
쿼리 토큰 세션 폐기 검증
server/app.mjs, tests/api/session-revocation.test.mjs, .jules/*, package.json
iCalendar 및 SSE 인증이 JWT의 tv와 사용자 token_version을 비교합니다. logout-all 후 기존 토큰은 401을 반환하고 새 토큰은 200을 반환합니다. 관련 보안 기록과 테스트 실행 설정을 추가했습니다.

MS Project XML 파서

Layer / File(s) Summary
선형 XML 및 선행 작업 추출
cloud-sync.js, tests/unit/msproject.test.mjs
Task와 PredecessorLink 블록 추출을 indexOfslice 기반으로 변경했습니다. 불완전한 태그, 정규식 특수문자 작업명, 대량의 미완성 블록을 테스트합니다.

Node.js 및 패키지 설정

Layer / File(s) Summary
실행 환경 및 CI 고정
.github/workflows/*, package.json
CI 작업의 Node.js 버전을 22.13.0으로 고정했습니다. packageManager, engines, Hono 패키지 버전, c8 설정을 갱신했습니다.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant CalendarSSE
  participant JWTVerifier
  participant UsersDatabase
  Client->>CalendarSSE: 쿼리 JWT 요청
  CalendarSSE->>JWTVerifier: JWT 서명 검증
  JWTVerifier-->>CalendarSSE: 사용자 ID와 tv 반환
  CalendarSSE->>UsersDatabase: token_version 조회
  UsersDatabase-->>CalendarSSE: 현재 token_version 반환
  CalendarSSE-->>Client: 일치하면 200, 불일치하면 401
Loading

Possibly related issues

  • ContextualWisdomLab/scopeweave 이슈 413: 현재 PR은 calendar 및 SSE 쿼리 토큰에 token_version 폐기 검사를 추가하며, 해당 이슈의 쿼리 토큰 인증 강화 목표와 연결됩니다.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 URL 토큰 엔드포인트에 세션 폐기 검사를 적용한 주요 변경 사항을 정확하고 간결하게 설명합니다.
✨ Finishing Touches 💡 1
📝 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-calendar-stream-revocation-11666540507854451077

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

…ent ReDoS

The `parseMsProjectXml` function in `cloud-sync.js` previously constructed a
dynamic regular expression using `new RegExp()` to extract XML tag values.
If the `name` parameter were somehow derived from unvalidated input, this
could expose the application to Regular Expression Denial of Service (ReDoS)
attacks by executing complex, potentially backtracking expressions on the
main thread.

This commit replaces the non-literal `RegExp` with safe, exact string matching
using `indexOf` and `slice`. This implementation avoids regular expressions
entirely for this parsing step, mitigating the ReDoS vulnerability and resolving
the related SAST scan warning.
This commit addresses two separate security findings:

1. Upgrades `@hono/node-server` to version 2.0.12 (via `pnpm update`) to
resolve a moderate severity vulnerability (GHSA-frvp-7c67-39w9) flagged by
trivy-fs and `pnpm audit`.

2. Refactors `parseMsProjectXml` in `cloud-sync.js` to avoid using a dynamic
non-literal `RegExp` constructor. A SAST scan flagged this as a potential
Regular Expression Denial of Service (ReDoS) vulnerability. We replace the
regex with exact string matching using `indexOf` and `slice` to safely
extract XML tag values without the risk of backtracking.
@seonghobae
seonghobae enabled auto-merge (squash) August 3, 2026 00:35
@seonghobae
seonghobae changed the base branch from develop to fix/security-hono-csv-hardening August 3, 2026 01:07
@seonghobae
seonghobae changed the base branch from fix/security-hono-csv-hardening to develop August 3, 2026 01:09

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@seonghobae
seonghobae changed the base branch from develop to fix/security-hono-csv-hardening August 3, 2026 01:13
@seonghobae
seonghobae changed the base branch from fix/security-hono-csv-hardening to develop August 3, 2026 01:19

Copy link
Copy Markdown
Contributor Author

@jules Please also clear the current Semgrep gate by porting the exact linear parseMsProjectXml collection/tag extraction fix and its regression test from PR #386. Preserve this PR's calendar/SSE token-version fix and session-revocation test, and do not add a Semgrep suppression or weaken the gate.

@seonghobae seonghobae changed the title 🛡️ Sentinel: [HIGH] 세션 해지 우회 취약점 수정 (calendar 및 stream 엔드포인트) fix(security): enforce session revocation on calendar and stream tokens Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@seonghobae
seonghobae marked this pull request as draft August 3, 2026 02:55
auto-merge was automatically disabled August 3, 2026 02:55

Pull request was converted to draft

@seonghobae
seonghobae marked this pull request as ready for review August 3, 2026 02:55
@seonghobae
seonghobae enabled auto-merge (squash) August 3, 2026 02:55

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@seonghobae
seonghobae marked this pull request as draft August 3, 2026 13:14
auto-merge was automatically disabled August 3, 2026 13:14

Pull request was converted to draft

@seonghobae
seonghobae force-pushed the sentinel-fix-calendar-stream-revocation-11666540507854451077 branch from 476cc9c to f8ad361 Compare August 3, 2026 13:14
@seonghobae
seonghobae marked this pull request as ready for review August 3, 2026 13:27

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@seonghobae
seonghobae enabled auto-merge (squash) August 3, 2026 13:31

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@seonghobae
seonghobae marked this pull request as draft August 3, 2026 13:36
auto-merge was automatically disabled August 3, 2026 13:36

Pull request was converted to draft

@seonghobae
seonghobae marked this pull request as ready for review August 3, 2026 13:37
@seonghobae
seonghobae enabled auto-merge (squash) August 3, 2026 13:37
@seonghobae
seonghobae force-pushed the sentinel-fix-calendar-stream-revocation-11666540507854451077 branch from eed71f5 to 5ed7fa1 Compare August 3, 2026 14:02

Copy link
Copy Markdown
Contributor Author

Superseded by #414. This bot-owned branch was overwritten again after successful verification: commit eed71f5d0ce9f87530c0ae94d81709e369b60311 removed the session-revocation implementation, deleted its regression test and verification note, rewrote lock/runtime evidence, and introduced unrelated UI changes. The branch was restored to the last verified implementation commit 5ed7fa125bcf63df4bb548d8bc244ac4ddf0054c, copied to the stable maintainer-owned branch used by #414, and is being closed to prevent another asynchronous agent overwrite from invalidating exact-head evidence.

@seonghobae seonghobae closed this Aug 3, 2026
auto-merge was automatically disabled August 3, 2026 14:03

Pull request was closed

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.

2 participants