Skip to content

fix(security): constrain artifact-link purposes on current main - #415

Open
seonghobae wants to merge 7 commits into
mainfrom
fix/artifact-link-purpose-main-20260812-r2
Open

fix(security): constrain artifact-link purposes on current main#415
seonghobae wants to merge 7 commits into
mainfrom
fix/artifact-link-purpose-main-20260812-r2

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Objective

Constrain signed artifact-link purposes to the documented allowlist before any signing path consumes the request.

Exact current-head evidence

  • protected base: 961f44a19c4ced87f8062b3ab9842a9a08d05235;
  • test-only RED head: e7e9b82f1782ed689f748eb09659d0550affaffe;
  • exact GREEN head: 00814b6ea46c9b4e8783fd2e04924007c3fdd677;
  • CI, Security Scan, SAST Semgrep, and fuzz: terminal-success;
  • mergeable: true;
  • all review threads: resolved.

Contract

viewer-preview, download, and integration are accepted; absent or blank purpose retains documented default behavior; legacy viewer maps only to viewer-preview; unknown purpose fails before signing. NUL-corrupted input is rejected rather than normalized into an allowed purpose.

Scope boundary

Request-boundary validation only. Token signatures, TTLs, tenant authority, ledger persistence, delivery, and canonical documentation are unchanged.

Merge gate

Keep this exact head unchanged. Auto-merge may act only after live checks remain successful, zero valid unresolved findings remain, and a qualifying independent non-author approval is attached to this exact head. Automated evidence and predecessor reviews do not transfer.

Summary by CodeRabbit

  • 버그 수정
    • 아티팩트 링크 생성 시 목적 값의 앞뒤 공백과 잘못된 문자를 자동으로 정리합니다.
    • 목적 값이 비어 있으면 기본 설정이 적용됩니다.
    • 기존 viewer 목적 값은 viewer-preview로 자동 변환되어 호환성을 유지합니다.
    • 지원되지 않거나 잘못된 목적 값은 명확한 오류로 안내됩니다.
    • 기본 뷰어 미리보기 링크의 TTL 및 세션 설정이 안정적으로 적용됩니다.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review available on request

  • 🔍 Trigger review

Reviews should be triggered manually for repositories with fewer than 10 stars. Select Trigger review above or comment @coderabbitai review to review the latest changes. For a full review, comment @coderabbitai full review.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c906a830-4f4c-4f2d-a4f3-aaee69aa0b3e

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 10d69e53-1cf0-448b-9475-f79116d1de06

📥 Commits

Reviewing files that changed from the base of the PR and between fbdeec1 and 00814b6.

📒 Files selected for processing (1)
  • src/test/java/com/clearfolio/viewer/artifact/ArtifactLinkServiceTest.java

📝 Walkthrough

Walkthrough

ArtifactLinkRequest가 목적 값을 정규화하고 검증합니다. 레거시 "viewer""viewer-preview"로 변환됩니다. 지원되지 않는 목적과 NUL 문자는 예외로 처리됩니다. 관련 JUnit 테스트가 추가되고 기존 서비스 테스트 입력이 조정되었습니다.

Changes

Artifact 목적 검증

Layer / File(s) Summary
목적 정규화와 검증
src/main/java/com/clearfolio/viewer/api/ArtifactLinkRequest.java, src/test/java/com/clearfolio/viewer/api/ArtifactLinkRequestTest.java, src/test/java/com/clearfolio/viewer/artifact/ArtifactLinkServiceTest.java
지원 목적 상수와 목록을 추가했습니다. NUL 문자를 거부하고 앞뒤 공백을 제거합니다. 빈 목적은 null로 처리합니다. 레거시 "viewer""viewer-preview"로 변환합니다. 지원되지 않는 목적은 IllegalArgumentException으로 거부합니다. 기본 뷰어 요청과 관련 오류 동작을 테스트합니다.

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

Merge Risk: ⚪ Minimal · up to 00814

The PR constrains artifact-link purposes at the request boundary while preserving documented defaults and legacy mapping; invalid purposes are rejected before signing. No actionable merge-blocking risk remains after normal checks and review.

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 artifact-link 목적 값 제한이라는 변경의 핵심을 정확하고 간결하게 설명합니다.
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
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/artifact-link-purpose-main-20260812-r2

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.

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

Copy link
Copy Markdown
Collaborator Author

@opencode-agent review

Please review the unchanged exact current head a9ebd74d03be2a87aa014446136745ab8daab307 against live protected main and repository gates. Treat checks/model output as evidence only and submit a formal review only for this exact head.

@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

🤖 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/main/java/com/clearfolio/viewer/api/ArtifactLinkRequest.java`:
- Around line 53-58: Update cleanPurpose in ArtifactLinkRequest to reject values
containing a NUL character before applying strip(), throwing an exception with
the message "artifact link purpose contains NUL" instead of removing the
character. Preserve the existing null handling and empty-value conversion.
🪄 Autofix

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 Plus

Run ID: 913b1d64-58a6-47d1-8743-dc34fc7faa6b

📥 Commits

Reviewing files that changed from the base of the PR and between b66b692 and 30d762f.

📒 Files selected for processing (2)
  • src/main/java/com/clearfolio/viewer/api/ArtifactLinkRequest.java
  • src/test/java/com/clearfolio/viewer/api/ArtifactLinkRequestTest.java

Comment thread src/main/java/com/clearfolio/viewer/api/ArtifactLinkRequest.java
@opencode-agent
opencode-agent Bot disabled auto-merge August 12, 2026 22:33
@seonghobae
seonghobae enabled auto-merge (squash) August 12, 2026 23:16

Copy link
Copy Markdown
Collaborator Author

Exact-current-head read-only review request for 00814b6ea46c9b4e8783fd2e04924007c3fdd677 on protected main 961f44a19c4ced87f8062b3ab9842a9a08d05235. CI, Security Scan, SAST Semgrep, and fuzz are terminal-success; the three-file request-boundary delta is mergeable and the prior major thread is resolved. Verify the documented purpose allowlist/default/legacy mapping and fail-closed NUL rejection before signing. Do not mutate the branch or bypass independent approval.

@opencode-agent review
@cwl-noema-review review
@strix review
@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

target='00814b6ea46c9b4e8783fd2e04924007c3fdd677'
base='961f44a19c4ced87f8062b3ab9842a9a08d05235'
repo='ContextualWisdomLab/clearfolio'

echo '== revision identity and ancestry =='
git rev-parse HEAD
git cat-file -t "$target"
git cat-file -t "$base"
git merge-base --is-ancestor "$base" "$target" && echo 'base_is_ancestor=yes'
git diff --name-status "$base" "$target"
git diff --check "$base" "$target"

echo '== patch =='
git diff --find-renames --find-copies "$base" "$target" -- \
  src/main/java/com/clearfolio/viewer/api/ArtifactLinkRequest.java \
  src/test/java/com/clearfolio/viewer/api/ArtifactLinkRequestTest.java

echo '== ArtifactLinkRequest outline =='
ast-grep outline src/main/java/com/clearfolio/viewer/api/ArtifactLinkRequest.java --items all --view expanded

echo '== ArtifactLinkRequest implementation =='
cat -n src/main/java/com/clearfolio/viewer/api/ArtifactLinkRequest.java

echo '== ArtifactLinkRequest tests =='
cat -n src/test/java/com/clearfolio/viewer/api/ArtifactLinkRequestTest.java

echo '== signing-related call sites =='
rg -n -C 5 --glob '*.java' \
  '(ArtifactLinkRequest|purpose\(\)|getPurpose\(\)|sign[A-Za-z]*\(|SignedArtifact|artifact[-A-Za-z]*link)' \
  src/main/java src/test/java

echo '== GitHub PR metadata and checks =='
gh pr view 415 --repo "$repo" --json number,headRefOid,baseRefName,baseRefOid,mergeCommit,statusCheckRollup

Length of output: 143601


🏁 Script executed:

#!/bin/bash
set -euo pipefail

target='00814b6ea46c9b4e8783fd2e04924007c3fdd677'
base='961f44a19c4ced87f8062b3ab9842a9a08d05235'
repo='ContextualWisdomLab/clearfolio'

echo '== remaining changed test delta =='
git diff --find-renames "$base" "$target" -- \
  src/test/java/com/clearfolio/viewer/artifact/ArtifactLinkServiceTest.java

echo '== createLink, purpose fallback, and signing order =='
sed -n '120,185p' src/main/java/com/clearfolio/viewer/artifact/ArtifactLinkService.java
rg -n -C 8 --glob '*.java' 'String purposeOf\s*\(' src/main/java

echo '== controller link endpoint =='
sed -n '65,90p' src/main/java/com/clearfolio/viewer/controller/ArtifactController.java

echo '== controller tests for invalid request bodies =='
rg -n -C 8 --glob '*Test.java' \
  '(admin-export|\\\\u0000|NUL|unsupported|artifact link purpose)' src/test/java

echo '== purpose contract in repository documentation and API schemas =='
rg -n -i -C 4 \
  '(viewer-preview|admin-export|artifact.{0,12}purpose|purpose.{0,12}artifact|integration|download)' \
  README.md docs src/main/resources 2>/dev/null || true

echo '== live main and protection metadata =='
git ls-remote origin refs/heads/main
gh api "repos/$repo/branches/main" --jq '{name,commit:.commit.sha,protected,protection_url}' 
gh api "repos/$repo/branches/main/protection" \
  --jq '{required_status_checks,required_pull_request_reviews,enforce_admins,restrictions}' 2>&1 || true

echo '== PR state, review decision, and review threads =='
gh pr view 415 --repo "$repo" --json state,isDraft,reviewDecision,reviews,comments

Length of output: 127473


@seonghobae Formal read-only review completed for 00814b6ea46c9b4e8783fd2e04924007c3fdd677.

No actionable findings were identified.

Verified:

  • Live protected main is 961f44a19c4ced87f8062b3ab9842a9a08d05235.
  • The requested base is an ancestor of the reviewed head.
  • The changed three-file delta has no whitespace errors.
  • The allowlist is viewer-preview, download, and integration.
  • null and blank purposes preserve default behavior.
  • Legacy viewer maps to viewer-preview.
  • NUL-containing purposes throw before normalization, service processing, token signing, or ledger persistence.
  • The canonical signed-artifact design documents the same allowlist.
  • Exact-head Maven, merge compatibility, security, Semgrep, CodeQL, and fuzz checks are successful.

The detailed branch-protection configuration could not be read because the GitHub integration returned HTTP 403. GitHub confirms that main is protected. The PR still requires independent review. I did not approve or modify the branch.

⚠️ Action not completed

Review rate limited.

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
Collaborator Author

@opencode-agent review
@coderabbitai review
@cwl-noema-review review
@strix review

Review exact head 00814b6ea46c9b4e8783fd2e04924007c3fdd677 only. Exact-head CI, Security Scan, SAST Semgrep, and fuzz are terminal-success; the NUL-input finding is resolved. Validate the closed purpose allowlist, legacy viewer normalization, default behavior for absent/blank purpose, and rejection before signing for unknown or NUL-corrupted input.

Review read-only; preserve independent reviewer identity. Do not mutate the branch, synthesize author approval, weaken protection, or merge.

@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

target='00814b6ea46c9b4e8783fd2e04924007c3fdd677'
repo='ContextualWisdomLab/clearfolio'

echo '== exact revision =='
echo -n 'HEAD='; git rev-parse HEAD
echo -n 'target='; git rev-parse "$target"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$target"

echo '== PR and checks =='
gh pr view 415 --repo "$repo" --json number,headRefOid,baseRefName,baseRefOid,state,isDraft,reviewDecision,statusCheckRollup
gh api "repos/$repo/commits/$target/check-runs?per_page=100" --paginate \
  --jq '.check_runs[] | [.name,.status,.conclusion,.details_url] | `@tsv`'

base="$(git show -s --format=%P "$target" | awk '{print $1}')"
echo "== parent base: $base =="
git merge-base --is-ancestor "$base" "$target"
git diff --check "$base" "$target"
git diff --name-status "$base" "$target"
git diff --find-renames "$base" "$target" -- \
  src/main/java/com/clearfolio/viewer/api/ArtifactLinkRequest.java \
  src/main/java/com/clearfolio/viewer/artifact/ArtifactLinkService.java \
  src/test/java/com/clearfolio/viewer/api/ArtifactLinkRequestTest.java \
  src/test/java/com/clearfolio/viewer/artifact/ArtifactLinkServiceTest.java

echo '== request type outline and source =='
ast-grep outline src/main/java/com/clearfolio/viewer/api/ArtifactLinkRequest.java --items all --view expanded
cat -n src/main/java/com/clearfolio/viewer/api/ArtifactLinkRequest.java

echo '== request tests =='
cat -n src/test/java/com/clearfolio/viewer/api/ArtifactLinkRequestTest.java

echo '== production request-to-signing paths =='
ast-grep outline src/main/java/com/clearfolio/viewer/artifact/ArtifactLinkService.java --items all --view expanded
rg -n -C 8 --glob '*.java' \
  '(new ArtifactLinkRequest\s*\(|ArtifactLinkRequest\s*\(|\.purpose\(\)|getPurpose\(\)|purposeOf\s*\(|sign[A-Za-z]*\s*\()' \
  src/main/java

echo '== relevant test coverage =='
rg -n -C 6 --glob '*Test.java' \
  '(admin-export|viewer-preview|integration|download|viewer|u0000|NUL|unsupported|purpose)' \
  src/test/java

Length of output: 202532


🏁 Script executed:

#!/bin/bash
set -euo pipefail

log='/tmp/coderabbit-shell-logs/shell-output-p97u2F'
target='00814b6ea46c9b4e8783fd2e04924007c3fdd677'

echo '== captured revision, PR, checks, and diff summary =='
sed -n '1,260p' "$log"

echo '== selected exact-head source =='
cat -n src/main/java/com/clearfolio/viewer/api/ArtifactLinkRequest.java
printf '\n== ArtifactLinkService createLink and purpose extraction ==\n'
rg -n -A100 -B10 --glob '*.java' \
  'ArtifactLinkResponse\s+createLink\s*\(|String\s+purposeOf\s*\(' \
  src/main/java/com/clearfolio/viewer/artifact/ArtifactLinkService.java
printf '\n== exact request tests ==\n'
cat -n src/test/java/com/clearfolio/viewer/api/ArtifactLinkRequestTest.java

printf '\n== direct service tests for rejected purposes ==\n'
rg -n -A35 -B10 --glob '*Test.java' \
  '(admin-export|unsupported artifact-link purpose|NUL|\\u0000)' \
  src/test/java/com/clearfolio/viewer/artifact/ArtifactLinkServiceTest.java

echo '== all direct production construction sites =='
rg -n -C 3 --glob '*.java' \
  'new\s+ArtifactLinkRequest\s*\(|ArtifactLinkRequest\.viewerPreview\s*\(' \
  src/main/java

Length of output: 45530


@seonghobae Formal read-only review completed for exact commit 00814b6ea46c9b4e8783fd2e04924007c3fdd677.

No actionable findings were identified.

Verified:

  • The checked-out HEAD matches the requested commit.
  • PR #415 targets main at 961f44a19c4ced87f8062b3ab9842a9a08d05235.
  • The allowlist accepts only viewer-preview, download, and integration.
  • Legacy viewer normalizes to viewer-preview.
  • null, blank purpose, and an absent request preserve the default viewer-preview behavior.
  • NUL-containing input throws before stripping or normalization.
  • Unsupported nonblank input throws with "artifact link purpose is unsupported".
  • Request construction completes before ArtifactLinkService.createLink can sign a token or persist a ledger record.
  • Exact-head Maven, merge compatibility, CodeQL, Semgrep, OSV, Trivy, Scorecard, Strix, and fuzz checks completed successfully.

I did not approve the PR, modify the branch, alter protection, or merge.

⚠️ Action not completed

Review rate limited.

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
Collaborator Author

Exact-current-head independent review request for 00814b6ea46c9b4e8783fd2e04924007c3fdd677.

The three-file artifact-link purpose boundary is mergeable; CI, Security Scan, SAST Semgrep, and fuzz are terminal-success; the only inline finding is resolved on this head. Only viewer-preview, download, and integration may be signed; the legacy viewer alias canonicalizes to viewer-preview; NUL-corrupted and unsupported values fail closed. Protected main has advanced since construction, so any base update must regenerate all exact-head evidence.

@opencode-agent review
@coderabbitai review
@cwl-noema-review review
@strix review

Review this exact head read-only. Do not mutate the branch, synthesize author approval, weaken protection, or merge.

@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo='ContextualWisdomLab/clearfolio'
target='00814b6ea46c9b4e8783fd2e04924007c3fdd677'

echo '== requested commit identity =='
git cat-file -t "$target"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$target"
parent="$(git show -s --format=%P "$target" | awk '{print $1}')"
printf 'first_parent=%s\n' "$parent"

echo '== live main and PR state =='
git ls-remote origin refs/heads/main
gh pr view 415 --repo "$repo" \
  --json number,state,isDraft,headRefOid,baseRefName,baseRefOid,reviewDecision,statusCheckRollup
gh api "repos/$repo/branches/main" --jq '{name,protected,sha:.commit.sha}' || true
gh api "repos/$repo/commits/$target/check-runs?per_page=100" --paginate \
  --jq '.check_runs[] | [.name,.status,.conclusion] | `@tsv`'

echo '== ancestry and exact delta =='
git merge-base --is-ancestor "$parent" "$target" && echo 'parent_is_ancestor=yes'
git diff --check "$parent" "$target"
git diff --name-status "$parent" "$target"
git diff --find-renames "$parent" "$target" -- \
  src/main/java/com/clearfolio/viewer/api/ArtifactLinkRequest.java \
  src/main/java/com/clearfolio/viewer/artifact/ArtifactLinkService.java \
  src/test/java/com/clearfolio/viewer/api/ArtifactLinkRequestTest.java \
  src/test/java/com/clearfolio/viewer/artifact/ArtifactLinkServiceTest.java

echo '== request boundary map and source =='
ast-grep outline src/main/java/com/clearfolio/viewer/api/ArtifactLinkRequest.java --items all --view expanded
cat -n src/main/java/com/clearfolio/viewer/api/ArtifactLinkRequest.java

echo '== request to signing path =='
rg -n -A100 -B10 --glob '*.java' \
  'ArtifactLinkResponse\s+createLink\s*\(|String\s+purposeOf\s*\(' \
  src/main/java/com/clearfolio/viewer/artifact/ArtifactLinkService.java
rg -n -C 5 --glob '*.java' \
  'new\s+ArtifactLinkRequest\s*\(|ArtifactLinkRequest\.viewerPreview\s*\(|\.purpose\(\)|getPurpose\(\)|sign[A-Za-z]*\s*\(' \
  src/main/java

echo '== focused tests =='
cat -n src/test/java/com/clearfolio/viewer/api/ArtifactLinkRequestTest.java
rg -n -A35 -B10 --glob '*Test.java' \
  '(admin-export|viewer-preview|integration|download|unsupported artifact-link purpose|NUL|\\u0000)' \
  src/test/java/com/clearfolio/viewer/artifact/ArtifactLinkServiceTest.java

Length of output: 46635


@seonghobae Formal read-only review completed for exact commit 00814b6ea46c9b4e8783fd2e04924007c3fdd677.

I found no new actionable source finding.

Verified:

  • The commit is the current head of PR #415.
  • ArtifactLinkRequest accepts only viewer-preview, download, and integration.
  • Legacy viewer canonicalizes to viewer-preview.
  • null and blank purpose values preserve the default purpose.
  • NUL-containing values throw "artifact link purpose contains NUL" before normalization.
  • Unsupported values throw "artifact link purpose is unsupported".
  • Request construction occurs before ArtifactLinkService.createLink signs a token or records a ledger entry.
  • The exact-head Maven, security, SAST, fuzz, and related checks completed successfully.

Live protected main is eb0a32e87e7470469c8fa0f8c67d9583654bef57. PR #415 still records base 961f44a19c4ced87f8062b3ab9842a9a08d05235. Therefore, the recorded merge-compatibility check does not verify compatibility with the current live main. Update the branch and regenerate exact-head evidence before merge evaluation.

I did not modify the branch, approve the PR, change protection, or merge.

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

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