Skip to content

feat(amber): guard cloneWorkflow with a read-access check - #7605

Merged
aglinxinyuan merged 1 commit into
apache:mainfrom
aglinxinyuan:fix/clone-workflow-read-access
Aug 13, 2026
Merged

feat(amber): guard cloneWorkflow with a read-access check#7605
aglinxinyuan merged 1 commit into
apache:mainfrom
aglinxinyuan:fix/clone-workflow-read-access

Conversation

@aglinxinyuan

@aglinxinyuan aglinxinyuan commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

cloneWorkflow fetched the source workflow by wid and copied its content into a workflow owned by the caller, with no access check on the way in. Any authenticated REGULAR user could POST /workflow/clone/<wid> for a wid they hold no privilege on and receive a full copy of a private workflow's content — operator configurations, file paths and all.

Root cause. Every sibling on this path guards; this one endpoint did not.

Endpoint Guard
retrieveWorkflow hasReadAccess directly
duplicateWorkflow hasReadAccess directly
cloneVersion (/version/clone/{vid}) inherits it via retrieveWorkflowVersion
cloneWorkflow none

That reads as an oversight rather than a decision. SECURITY.md states that REGULAR users "cannot access other users' private resources without granted permissions", so the endpoint contradicted the project's own declared model.

Before → after

 caller with no privilege on wid
   |
   v                                   v
 POST /workflow/clone/{wid}          POST /workflow/clone/{wid}
   |                                   |
   |  (no check)                       +-- hasReadAccess(wid, uid)? --> no --> 403
   v                                   |
 fetchOneByWid(wid)                    v  yes (owner / READ grant / public)
   |                                 fetchOneByWid(wid)
   v                                   |
 full content copied to caller         v
                                     full content copied to caller

The fix adds the same three lines the siblings use. hasReadAccess already returns true for public workflows, so the hub's clone button — the only caller, and always acting on a published workflow — is unaffected. A caller holding an explicit READ grant can still clone.

Any related issues, documentation, discussions?

Found while reviewing the clone-endpoint test in #7592, now merged; the new cases here build on the spec helpers that landed with it. Not filed as an issue, because SECURITY.md asks that security bugs not be reported through public issues.

The release/v1.2 backport preflight comes back grey: the guard itself applies, but the two new test cases depend on helpers that arrived with #7592, which was not backported. The backport needs those cases rewritten self-contained, so it will have to be resolved by hand rather than pushed straight through.

How was this PR tested?

Two cases added to the existing WorkflowResourceSpec, and the pre-existing success case in #7592 now publishes its source first so it exercises the public path.

Test Pins
clone a private workflow the caller has been granted read access to the guard does not over-block a legitimate READ grant
reject a caller with no access to the source workflow 403, no copy reaches the caller, and no WORKFLOW_USER_CLONES row is written for the rejected attempt

Red before the guard, green after — with the guard reverted the rejection case fails and the other 69 pass, so it is the guard the test is pinning and not a fixture.

sbt "WorkflowExecutionService/testOnly org.apache.texera.web.resource.dashboard.file.WorkflowResourceSpec"
[info] Total number of tests run: 70
[info] Tests: succeeded 70, failed 0, canceled 0, ignored 0, pending 0

scalafmtCheck and scalafix --check pass for both Compile and Test.

Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Opus 5)

Copilot AI lite review requested due to automatic review settings August 12, 2026 23:58

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Yicong-Huang Yicong-Huang added the release/v1.2 back porting to release/v1.2 label Aug 12, 2026
@github-actions
github-actions Bot requested a review from xuang7 August 12, 2026 23:58
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Backport auto-label report

This fix: PR was checked against each actively-supported release branch. release/* labels drive the post-merge backport, so add or remove one to change where this fix lands.

Release branch Analysis
release/v1.2 Already labeled — this fix is queued to backport here.

Auto-label run.

@github-actions

Copy link
Copy Markdown
Contributor

Automated Reviewer Suggestions

Based on the git blame history of the changed files, we recommend the following reviewers:

  • Contributors with relevant context: @mengw15
    You can notify them by mentioning @mengw15 in a comment.

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

⚠️ Benchmark changes need a look

🟢 0 better · 🔴 10 worse · ⚪ 5 noise (<±5%) · 0 without baseline

Compared against main dd7d813 benchmarked on this same runner, so the delta is largely free of cross-runner hardware noise. The "7d avg" column still reflects the gh-pages dashboard. Treat <±5% as noise unless repeated.

Dashboard · Run

config throughput MB/s latency max Δ latest / 7d
🔴 bs=10 sw=10 sl=64 372 0.227 25,144/37,901/37,901 us 🔴 -10.3% / 🔴 +132.4%
🔴 bs=100 sw=10 sl=64 774 0.472 128,999/146,692/146,692 us 🔴 +12.4% / 🔴 +35.6%
bs=1000 sw=10 sl=64 915 0.558 1,088,222/1,179,927/1,179,927 us ⚪ within ±5% / 🔴 +13.2%
Baseline details

Latest main dd7d813 from same runner

config metric PR latest main 7d avg Δ latest Δ 7d
bs=10 sw=10 sl=64 throughput 372 tuples/sec 414 tuples/sec 775.33 tuples/sec -10.1% -52.0%
bs=10 sw=10 sl=64 MB/s 0.227 MB/s 0.253 MB/s 0.473 MB/s -10.3% -52.0%
bs=10 sw=10 sl=64 p50 25,144 us 23,129 us 12,743 us +8.7% +97.3%
bs=10 sw=10 sl=64 p95 37,901 us 34,520 us 16,310 us +9.8% +132.4%
bs=10 sw=10 sl=64 p99 37,901 us 34,520 us 18,926 us +9.8% +100.3%
bs=100 sw=10 sl=64 throughput 774 tuples/sec 841 tuples/sec 1,001 tuples/sec -8.0% -22.7%
bs=100 sw=10 sl=64 MB/s 0.472 MB/s 0.514 MB/s 0.611 MB/s -8.2% -22.7%
bs=100 sw=10 sl=64 p50 128,999 us 114,809 us 101,399 us +12.4% +27.2%
bs=100 sw=10 sl=64 p95 146,692 us 138,779 us 108,206 us +5.7% +35.6%
bs=100 sw=10 sl=64 p99 146,692 us 138,779 us 118,195 us +5.7% +24.1%
bs=1000 sw=10 sl=64 throughput 915 tuples/sec 918 tuples/sec 1,026 tuples/sec -0.3% -10.8%
bs=1000 sw=10 sl=64 MB/s 0.558 MB/s 0.56 MB/s 0.626 MB/s -0.4% -10.9%
bs=1000 sw=10 sl=64 p50 1,088,222 us 1,085,309 us 996,304 us +0.3% +9.2%
bs=1000 sw=10 sl=64 p95 1,179,927 us 1,165,966 us 1,042,531 us +1.2% +13.2%
bs=1000 sw=10 sl=64 p99 1,179,927 us 1,165,966 us 1,074,934 us +1.2% +9.8%
Raw CSV
config_idx,batch_size,schema_width,string_len,num_batches,total_ms,total_tuples,total_bytes,tuples_per_sec,mb_per_sec,lat_p50_us,lat_p95_us,lat_p99_us
0,10,10,64,20,537.86,200,128000,372,0.227,25143.98,37901.31,37901.31
1,100,10,64,20,2583.66,2000,1280000,774,0.472,128999.13,146691.94,146691.94
2,1000,10,64,20,21869.06,20000,12800000,915,0.558,1088222.06,1179926.63,1179926.63

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.40%. Comparing base (5021bc6) to head (cf8d403).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #7605      +/-   ##
============================================
- Coverage     88.63%   87.40%   -1.24%     
+ Complexity     4346     4342       -4     
============================================
  Files          1177     1177              
  Lines         46894    46782     -112     
  Branches       5229     5191      -38     
============================================
- Hits          41566    40890     -676     
- Misses         3568     4107     +539     
- Partials       1760     1785      +25     
Flag Coverage Δ *Carryforward flag
access-control-service 70.00% <ø> (ø) Carriedforward from c3c0bca
agent-service 86.87% <ø> (-11.75%) ⬇️ Carriedforward from c3c0bca
amber 84.93% <100.00%> (+0.21%) ⬆️
computing-unit-managing-service 50.24% <ø> (-10.15%) ⬇️ Carriedforward from c3c0bca
config-service 65.97% <ø> (-11.35%) ⬇️ Carriedforward from c3c0bca
file-service 69.05% <ø> (ø) Carriedforward from c3c0bca
frontend 89.07% <ø> (-0.92%) ⬇️ Carriedforward from c3c0bca
notebook-migration-service 78.89% <ø> (ø) Carriedforward from c3c0bca
pyamber 97.51% <ø> (-0.01%) ⬇️ Carriedforward from c3c0bca
workflow-compiling-service 26.31% <ø> (-31.58%) ⬇️ Carriedforward from c3c0bca

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aglinxinyuan
aglinxinyuan requested a review from mengw15 August 13, 2026 01:00
cloneWorkflow fetched the source by wid and copied its content into a workflow
owned by the caller, with no access check on the way in. Any authenticated
REGULAR user could POST /workflow/clone/<wid> for a wid they hold no privilege
on and receive a full copy of a private workflow's content.

Every sibling guards: retrieveWorkflow and duplicateWorkflow call hasReadAccess
directly, and the version endpoint cloneVersion inherits the check through
retrieveWorkflowVersion. cloneWorkflow was the one that did not, which reads as
an oversight rather than a decision -- SECURITY.md states that REGULAR users
cannot access other users' private resources without granted permissions.

Add the same guard the siblings use. hasReadAccess already returns true for
public workflows, so the hub's clone button -- the only caller, and always on a
published workflow -- is unaffected; a caller holding an explicit READ grant is
likewise still able to clone, which the new tests pin alongside the rejection.
@aglinxinyuan
aglinxinyuan force-pushed the fix/clone-workflow-read-access branch from cf8d403 to 522abef Compare August 13, 2026 03:58

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

LGTM

@mengw15
mengw15 requested review from mengw15 and a lite review from Copilot August 13, 2026 04:22
@mengw15
mengw15 removed their request for review August 13, 2026 04:23

Copilot AI 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

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (1)

amber/src/test/scala/org/apache/texera/web/resource/dashboard/file/WorkflowResourceSpec.scala:1242

  • The clone-rejection test asserts that no WORKFLOW_USER_CLONES row exists for the source wid at all, but the behavior being pinned is that the caller (user2) did not get a clone record. Filtering only by WID can make this test fail spuriously if another user ever clones the same workflow in the same DB lifecycle (or if fixtures change). Prefer matching the earlier clone test by filtering on both WID and UID.
    // no copy reached the caller, and the rejected attempt was not recorded as a clone
    assert(workflowNamesOf(sessionUser2).isEmpty)
    assert(getDSLContext.fetchCount(WORKFLOW_USER_CLONES, WORKFLOW_USER_CLONES.WID.eq(wid)) == 0)

@aglinxinyuan
aglinxinyuan added this pull request to the merge queue Aug 13, 2026
Merged via the queue into apache:main with commit 2891897 Aug 13, 2026
25 of 26 checks passed
@aglinxinyuan
aglinxinyuan deleted the fix/clone-workflow-read-access branch August 13, 2026 05:30
@github-actions

Copy link
Copy Markdown
Contributor

Backport PR opened: draft #7625 (#7625) to release/v1.2, assigned to @aglinxinyuan — needs manual work because the cherry-pick conflicts.

@aglinxinyuan aglinxinyuan changed the title fix(amber): guard cloneWorkflow with a read-access check feat(amber): guard cloneWorkflow with a read-access check Aug 13, 2026
@aglinxinyuan aglinxinyuan removed the release/v1.2 back porting to release/v1.2 label Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants