Skip to content

feat: require package maintainer permission when granting team package access#1027

Merged
elrrrrrrr merged 4 commits into3.xfrom
feat/team-grant-maintainer-check
Apr 10, 2026
Merged

feat: require package maintainer permission when granting team package access#1027
elrrrrrrr merged 4 commits into3.xfrom
feat/team-grant-maintainer-check

Conversation

@elrrrrrrr
Copy link
Copy Markdown
Member

Summary

  • Non-admin users must be a maintainer of the package before granting team package access
  • Admin users are exempt from this check and can grant any package
  • Updated docs (zh/en) with the new permission rule in core behavior, team package access, and permission summary sections

Test plan

  • Admin can grant any package to a team (bypass maintainer check)
  • Team owner can grant a package they maintain
  • Team owner gets 403 when granting a package they do not maintain

🤖 Generated with Claude Code

…e access

Non-admin users must be a maintainer of the package before they can
add it to a team. This prevents team owners from granting access to
packages they do not have permission over. Admin users are exempt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 10, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2d4a3bfd-8e64-408c-a99a-a44afe458a1f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/team-grant-maintainer-check

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 and usage tips.

@elrrrrrrr elrrrrrrr changed the base branch from master to 3.x April 10, 2026 06:09
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an Organization and Team management system, including support for team-based package access control and version-level package blocking. The review identified several critical security and code quality issues, including the use of an insecure RSA key length, missing access control checks for tarball downloads, and the need for stronger type safety for the 'role' property in TeamMember entities.

I am having trouble creating individual review comments. Click here to see my feedback.

app/common/CryptoUtil.ts (7)

security-critical critical

Using an RSA key with a modulus length of 512 bits is insecure and highly vulnerable to factorization attacks. It is strongly recommended to use a key length of at least 2048 bits for adequate security.

    modulusLength: 2048,

app/port/controller/package/DownloadPackageVersionTar.ts (54-59)

security-critical critical

The logic to check for blocked package versions appears to be missing. A user could potentially download the tarball of a blocked version. You should add checks for both package-level and version-level blocks before proceeding with the download. You can achieve this by calling this.getPackageEntity(scope, name) and this.getPackageVersionEntity(pkg, version).

    const [ scope, name ] = getScopeAndName(fullname);
    await this.userRoleManager.checkReadAccess(ctx, scope, name);
    const version = this.getAndCheckVersionFromFilename(ctx, fullname, filenameWithVersion);
    const pkg = await this.getPackageEntity(scope, name);
    await this.getPackageVersionEntity(pkg, version);
    const storeKey = "/packages/" + fullname + "/" + version + "/" + filenameWithVersion + ".tgz";
    const downloadUrl = await this.nfsAdapter.getDownloadUrl(storeKey);

app/core/entity/TeamMember.ts (8)

medium

The role property should be a specific union type 'owner' | 'member' instead of a generic string to ensure type safety and prevent invalid roles from being assigned. This aligns with the documentation and the implementation of OrgMember.

  role: 'owner' | 'member';

app/core/entity/TeamMember.ts (17)

medium

The role property should be a specific union type 'owner' | 'member' instead of a generic string to ensure type safety and prevent invalid roles from being assigned. This aligns with the documentation and the implementation of OrgMember.

  role: 'owner' | 'member';

app/core/entity/TeamMember.ts (24)

medium

With the role property now strongly typed as 'owner' | 'member', a valid role will always be provided upon TeamMember creation. The fallback to 'member' is no longer necessary and can be removed.

    this.role = data.role;

app/repository/model/TeamMember.ts (28)

medium

For better type safety and to align with the database schema comment which specifies 'owner or member', the type of role should be 'owner' | 'member' instead of a generic string.

  role: 'owner' | 'member';

@elrrrrrrr elrrrrrrr requested a review from fengmk2 April 10, 2026 06:54
@elrrrrrrr elrrrrrrr added the bug Something isn't working label Apr 10, 2026
@elrrrrrrr elrrrrrrr marked this pull request as ready for review April 10, 2026 06:54
@fengmk2
Copy link
Copy Markdown
Member

fengmk2 commented Apr 10, 2026

@codex review

@fengmk2
Copy link
Copy Markdown
Member

fengmk2 commented Apr 10, 2026

ci 不会跑了?

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

elrrrrrrr and others added 2 commits April 10, 2026 15:44
…API (#1026)

`EdgedriverBinary.fetch('/<version>/')` has been broken since around
**2026-04-07** — the legacy Azure Blob container listing API now returns
**HTTP 409 `PublicAccessNotPermitted`** after Microsoft disabled public
access on the storage account:

```
https://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver?prefix=<version>/&delimiter=/&comp=list
→ HTTP 409 PublicAccessNotPermitted
```

This made `test/common/adapter/binary/EdgedriverBinary.test.ts > fetch()
> should work` fail on the `items.length >= 3` assertion, blocking
`@eggjs/egg` monorepo's E2E pipeline and any project depending on
EdgedriverBinary.

Microsoft has migrated Edge WebDriver hosting to
`https://msedgedriver.microsoft.com/`. Per-version files are still
reachable at the same path pattern:

```
https://msedgedriver.microsoft.com/<version>/edgedriver_<platform>.zip
```

The new host **also** serves a `listing.json` dump (used by Microsoft's
own catalog page at https://msedgedriver.microsoft.com/catalog/), but it
has two drawbacks:

1. **It's a single static file containing every version since
`112.0.1722.39`** — ~9000 entries, ~1.2MB. I verified empirically that
**none of `?top` / `?limit` / `?max` / `?pageSize` / `?prefix` /
`?latest` query parameters do anything** — the response is
byte-identical regardless of query string. There is no server-side
filtering.
2. Even with caching, that's 1.2MB extra per sync.

So instead this PR mirrors the approach already used by
[`FirefoxBinary`](https://github.com/cnpm/cnpmcore/blob/master/app/common/adapter/binary/FirefoxBinary.ts)
and
[`ChromeForTestingBinary`](https://github.com/cnpm/cnpmcore/blob/master/app/common/adapter/binary/ChromeForTestingBinary.ts):
**don't call any listing API for sub-dirs at all.** Instead, enumerate
the per-version download URLs from a static list of known platform
filenames:

```ts
const EDGEDRIVER_PLATFORM_FILES = [
  'edgedriver_arm64.zip',
  'edgedriver_linux64.zip',
  'edgedriver_mac64.zip',
  'edgedriver_mac64_m1.zip',
  'edgedriver_win32.zip',
  'edgedriver_win64.zip',
] as const;
```

Each emitted `BinaryItem` carries `ignoreDownloadStatuses: [404]`, so
any version that doesn't ship every platform (e.g. older builds without
`mac64_m1`) is skipped cleanly by cnpmcore's sync pipeline rather than
failing the task.

The root-dir listing path (`#syncDirItems` →
`https://edgeupdates.microsoft.com/api/products`) is **completely
unchanged** — that JSON API still works.

| | Use `listing.json` | Generate URLs (this PR) |
|---|---|---|
| Network per sub-dir fetch | ~1.2MB JSON dump | **0 bytes** |
| Per-sync caching needed | yes (~1.2MB cached + reset logic) | no |
| Failure modes | listing endpoint down → all sub-dir fetches fail |
per-platform 404 handled cleanly |
| Code surface | listing parse + cache + error path | one `.map()` over
a const array |
| Singleton state safety | needs careful reset in `initFetch` | none,
stateless |
| Platform discovery | from server response | static const (loses any
net-new platform until updated) |

The trade-off is that if Microsoft adds a new Edge WebDriver platform
(e.g. `linux_arm64`), this adapter won't pick it up until the const list
is updated. That's a maintenance cost but a small one — the platform
list has been stable at six entries throughout the entire history of the
new CDN.

- **Root-dir listing (`fetch('/')`)**: still uses
`https://edgeupdates.microsoft.com/api/products`. Microsoft only exposes
the very latest release of each channel via this API, so the result
today is ~5 versions total (one per channel: Stable / Beta / Dev /
Canary). Historical version coverage was — and still is — governed by
what cnpmcore had previously synced, not by what edgeupdates returns
now.
- **Version ordering in the root listing**: items come out in the order
Microsoft's API returns them, which is grouped by channel (Stable → Beta
→ Dev → Canary), newest within each channel. This is *not* a strict
latest-first sort, but it matches the pre-PR behavior. If a strict
ordering is desired, that's a separate change.

Microsoft's per-version download URL works for **versions from
`112.0.1722.39` onwards**. Versions older than 112 are no longer
downloadable from any public Microsoft endpoint:

| Version | Old Blob URL | New URL |
|---|---|---|
| 80.0.361.111 | 409 PublicAccessNotPermitted | 404 |
| 100.0.1185.27 | 409 PublicAccessNotPermitted | 404 |
| 110.0.1587.69 | 409 PublicAccessNotPermitted | 404 |
| **112.0.1722.39** | 409 PublicAccessNotPermitted | **200 ✓** |
| 120.0.2210.133 | 409 PublicAccessNotPermitted | 200 ✓ |
| 148.0.3966.0 | 409 PublicAccessNotPermitted | 200 ✓ |

This is a Microsoft-side cutoff, not something cnpmcore can work around.
**Existing mirrored copies of pre-112 Edge WebDriver binaries in
cnpmcore's own storage are unaffected** — this PR only changes
*discovery* of new files from upstream.

Also adds `await binary.initFetch()` in the test's `beforeEach`.
`EdgedriverBinary` is a `@SingletonProto`, so
`app.getEggObject(EdgedriverBinary)` returns the same instance across
tests, which means `dirItems` populated by one test would leak into the
next. This was a pre-existing latent issue masked by the old test only
having one `it` block, but it surfaces immediately when adding more
cases.

There is no formal Microsoft announcement for this specific storage
lockdown, but the migration off `azureedge.net` and the gradual lockdown
of the legacy XML listing have been documented across multiple community
projects. Timeline:

- **2024-05** —
[MicrosoftEdge/EdgeWebDriver#146](MicrosoftEdge/EdgeWebDriver#146):
Azure Blob listing stopped updating for versions ≥ 125.0.
- **2025-01** —
[MicrosoftEdge/EdgeWebDriver#183](MicrosoftEdge/EdgeWebDriver#183):
`azureedge.net` CDN sunset (Edgio bankruptcy); Microsoft initially said
it would continue under a different CDN provider.
- **2025-07** —
[seleniumbase/SeleniumBase#3888](seleniumbase/SeleniumBase#3888),
[MicrosoftEdge/EdgeWebDriver#201](MicrosoftEdge/EdgeWebDriver#201),
[#203](MicrosoftEdge/EdgeWebDriver#203):
`msedgedriver.azureedge.net` DNS stopped resolving; new CDN is
`msedgedriver.microsoft.com`. SeleniumBase fixed this in 4.40.6.
- **2026-04-07** — (this PR): The Azure Blob container backing the old
XML listing also had public access disabled; only
`msedgedriver.microsoft.com` works now.

- [x] `EdgedriverBinary.test.ts > should list recent stable versions
from edgeupdates.microsoft.com` — green
- [x] `EdgedriverBinary.test.ts > should generate all known platform
driver URLs for a version` — green (asserts the exact 6 generated items
with `ignoreDownloadStatuses: [404]` on each)
- [x] **All 6 cnpmcore CI matrices** (`mysql node@22/24 × jsonBuilder
true/false`, `postgresql node@22/24`) green
- [x] `test-deployment`, `typecheck` (lint + fmtcheck + tsc),
`codecov/patch`, `codecov/project` green
- [ ] Manual smoke (suggested for reviewer): a fresh sync of a recent
Edge WebDriver version (e.g. 148.0.3966.0) enumerates all 6 platform
binaries and downloads all of them successfully, without 404s
- [ ] Manual smoke (suggested for reviewer): a sync of an older version
that doesn't ship every platform (e.g. 112.0.1722.39 has no `mac64_m1`)
— the missing platform should be skipped via `ignoreDownloadStatuses:
[404]` and the rest should complete

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

* **Bug Fixes**
* Improved handling of unavailable EdgeDriver versions for specific
platforms—missing combinations are now skipped instead of causing
failures.

* **Improvements**
* Enhanced EdgeDriver binary delivery infrastructure for greater
reliability and consistency.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@elrrrrrrr
Copy link
Copy Markdown
Member Author

#1026

需要同步一下

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 10, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.19%. Comparing base (3445251) to head (6e2eeda).
⚠️ Report is 3 commits behind head on 3.x.

Additional details and impacted files
@@            Coverage Diff             @@
##              3.x    #1027      +/-   ##
==========================================
- Coverage   96.22%   96.19%   -0.03%     
==========================================
  Files         208      208              
  Lines       21018    20932      -86     
  Branches     2851     2848       -3     
==========================================
- Hits        20224    20135      -89     
- Misses        794      797       +3     

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@elrrrrrrr elrrrrrrr merged commit 33784a7 into 3.x Apr 10, 2026
12 of 13 checks passed
@elrrrrrrr elrrrrrrr deleted the feat/team-grant-maintainer-check branch April 10, 2026 08:34
fengmk2 pushed a commit that referenced this pull request Apr 10, 2026
[skip ci]

## 3.82.0 (2026-04-10)

* feat: require package maintainer permission when granting team package access (#1027) ([33784a7](33784a7)), closes [#1027](#1027)
@github-actions
Copy link
Copy Markdown

🎉 This PR is included in version 3.82.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working released on @3.x

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants