feat(labeler): reclassify undeclared-access as a warning, not a block#2097
Conversation
The severity-amended calibration sweep flagged glm-5.2 blocking a plugin that used undeclared storage — a correct finding, but the runtime already enforces declared access (the sandbox throws 'Storage collection not declared', and the allowedHosts/capability gates reject undeclared network/content/media/users), so undeclared access can never execute. It is a manifest/behavior mismatch, not an executable threat; genuine over-reach is caught by the specific malicious categories (data-exfiltration, credential-harvesting, ...) which block on their own. Hard-blocking a merely-inaccurate manifest is over-enforcement. - moderation-policy: undeclared-access category automated-block->warning, officialEffect block->warn; policyVersion + effectiveAt bumped to 2026-07-17. The label value is unchanged, so queryLabels consumers are unaffected; only the effect softens. Resolver/adapter/console are all policy-driven — no code hardcodes the classification. - fixture: clean-with-images declares its gallery storage so it is a genuine clean baseline (its code used ctx.storage.gallery without declaring it). - tests: undeclared-access now resolves to warned at any source/severity; the deterministic-blocks-at-any-severity test moved to credential-harvesting; version-string pins updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: bf350b0 The changes in this PR will be included in the next version bump. This PR includes changesets to release 20 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-playground | bf350b0 | Jul 17 2026, 05:12 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-demo-do | bf350b0 | Jul 17 2026, 05:13 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-demo-cache | bf350b0 | Jul 17 2026, 05:12 PM |
There was a problem hiding this comment.
The reclassification itself is sound: the threat-model reasoning in the PR description is correct, the labeler policy fixture is updated consistently, the clean-with-images fixture now honestly declares the storage it uses, and the labeler-side tests cover the new warning behavior. I checked the policy fixture, policy-resolver.ts, policy.ts, the code-AI adapter, the console label menus, registry-client, and the consumer-side @emdash-cms/registry-moderation evaluator.
The headline issue is that the PR does not complete the reclassification for downstream consumers. packages/registry-moderation/src/index.ts hardcodes undeclared-access in AUTOMATED_BLOCKS and omits it from WARNINGS. Because RELEASE_BLOCK_VALUES is built from AUTOMATED_BLOCKS, the admin UI and any other registry-client/registry-moderation consumer will still treat the label as a hard block — directly contradicting the PR's stated goal and its claim that no code hardcodes the classification.
To resolve this, undeclared-access needs to move from the hardcoded AUTOMATED_BLOCKS set into WARNINGS, the registry-moderation tests need to stop asserting that it blocks (and start asserting that it warns), and because @emdash-cms/registry-moderation is a published package, that consumer-side fix requires a changeset.
Everything inside apps/labeler looks correct; the remaining work is the matching consumer-side policy update.
Findings
-
[needs fixing]
packages/registry-moderation/src/index.ts:167-176The
AUTOMATED_BLOCKSset still includes"undeclared-access". BecauseRELEASE_BLOCK_VALUESis derived from this set, every consumer of@emdash-cms/registry-moderation(and@emdash-cms/registry-client) will continue to hard-block releases carrying this label, defeating the PR's reclassification.This also contradicts the PR description's claim that no code hardcodes the classification. Since
@emdash-cms/registry-moderationis a published package, moving this value out of the block set will also require a changeset.const AUTOMATED_BLOCKS = new Set<string>([ "malware", "data-exfiltration", "credential-harvesting", "supply-chain-compromise", "critical-vulnerability", "artifact-integrity-failure", "invalid-bundle", "impersonation", ]); -
[needs fixing]
packages/registry-moderation/src/index.ts:179-187WARNINGSdoes not include"undeclared-access". Even if it is removed fromAUTOMATED_BLOCKS, the label would be treated as neither block nor warning and would silently disappear from consumer-facing moderation output instead of surfacing as the intended warning.const WARNINGS = new Set<string>([ "suspicious-code", "obfuscated-code", "privacy-risk", "misleading-metadata", "low-quality", "broken-release", "package-disputed", "undeclared-access", ]); -
[needs fixing]
packages/registry-moderation/tests/moderation.test.ts:381-386This test loops over every
RELEASE_BLOCK_VALUESentry and asserts it blocks the release. Onceundeclared-accessis removed from that set, the loop no longer covers it and the existing test suite provides no coverage for the new warning behavior. Add a dedicated test provingundeclared-accessnow keeps a release eligible with a warning label.it("blocks every RELEASE_BLOCK_VALUES value on the exact release", () => { for (const value of RELEASE_BLOCK_VALUES) { const result = evaluate([label({ val: "assessment-passed" }), label({ val: value })]); expect(result.eligibility, `${value} should block the release`).toBe("blocked"); } }); it("treats undeclared-access as a warning, not a block", () => { const result = evaluate([ label({ val: "assessment-passed" }), label({ val: "undeclared-access" }), ]); expect(result).toMatchObject({ eligibility: "eligible", reasonCodes: ["eligible-assessment-pass", "warning-labels"], warningLabels: ["undeclared-access"], blockingLabels: [], }); }); it("blocks every PACKAGE_SCOPE_BLOCK_VALUES value on the publisher DID", () => {
…onsumer-side The labeler-side policy change softened undeclared-access to a warning, but the consumer-side evaluator hardcodes block-vs-warn in registry-moderation's AUTOMATED_BLOCKS/WARNINGS sets — so the aggregator, registry-client, and admin would still hard-block releases carrying the label. Move undeclared-access from AUTOMATED_BLOCKS to WARNINGS so RELEASE_BLOCK_VALUES drops it and the evaluator surfaces it as a warning (release stays eligible). Membership in RELEASE_VALUES is preserved (moved within the union), so it is not silently dropped. Addresses the emdashbot review on #2097. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Good catch — the reclassification was incomplete on the consumer side (commit bf350b0). There are two sources of block-vs-warn: the labeler's Fix: moved Confirmed registry-moderation 154, registry-client 85, aggregator 313, labeler 830 all green; typecheck/lint/format clean. Noted for the maintainer, not fixed here: the two independent classification sources (labeler policy fixture vs ~ 🤖 Claude Fable 5 |
There was a problem hiding this comment.
Re-review: all prior findings resolved
This is a clean follow-up. In my previous review I flagged three blockers: @emdash-cms/registry-moderation still hardcoded undeclared-access as an automated block, omitted it from the warning set, and had no test coverage for the new warning behavior. Those are all fixed in this revision.
What changed since the last review
packages/registry-moderation/src/index.tsis now consistent with the labeler policy:undeclared-accessis removed fromAUTOMATED_BLOCKSand added toWARNINGS. BecauseRELEASE_BLOCK_VALUESderives fromAUTOMATED_BLOCKS, consumer-side evaluation correctly keeps releases with this label eligible.packages/registry-moderation/tests/moderation.test.tsadds a dedicated test proving thatRELEASE_BLOCK_VALUESno longer contains the label and that an exact-CIDundeclared-accesslabel surfaces as a warning on an otherwise eligible release..changeset/undeclared-access-warning.mdadds the required minor changeset for the published@emdash-cms/registry-moderationpackage.
Cross-cutting consistency check
I searched the repo for undeclared-access references. The only remaining non-test/ non-doc references are now harmless:
apps/labeler/fixtures/moderation-policy.json— data-driven policy source, now lists the label aswarning/warn.apps/labeler/src/policy-resolver.tsandapps/labeler/src/policy.ts— derive block/warn sets from the policy fixture, no hardcoded classification.apps/labeler/console/src/labels.ts— derivesAUTOMATED_BLOCK_LABELSfromautomatedBlockCategories(MODERATION_POLICY).apps/labeler/src/code-ai-adapter.ts— prompt mentions the label name, but the allowed category set is passed in from the policy.packages/admin/src/lib/api/registry.ts— only has human-readable display text (name/description); it does not hardcode the category/effect.packages/registry-client/src/moderation.ts— re-exports the block/warn sets from@emdash-cms/registry-moderation.
So the PR's claim that "no code hardcodes the classification" is now accurate.
Approach evaluation
The reclassification remains sound. The runtime sandbox already rejects undeclared capability use (Storage collection not declared, allowedHosts / capability gates on network/content/media/users), so the label describes a manifest/behavior mismatch rather than an executable threat. Genuine malice is still caught by the dedicated blocking categories. The label value itself is unchanged, so queryLabels consumers are unaffected; only the effect softens.
Conventions
- Changeset added for the published package change.
- Tests updated on both the labeler side (
policy-resolver.test.ts,policy.test.ts,service.test.ts,xrpc-router.test.ts) and the consumer side (registry-moderation/tests/moderation.test.ts). - Admin UI label strings were already wrapped in
msgdescriptors; no new untranslated strings were introduced.
No remaining blocking issues. LGTM.
748af2d
into
feat/plugin-registry-labelling-service
What does this PR do?
Reclassifies the
undeclared-accesslabel from an automated block to a warning, from a finding in the severity-amended calibration sweep. Targets thefeat/plugin-registry-labelling-serviceintegration branch (RFC #694, umbrella #1909).The sweep's only flagged code-lane false positive was glm-5.2 blocking a "clean" fixture on a real
undeclared-accessfinding — the plugin declaredstorage: {}but its code calledctx.storage.gallery.put(...). glm was correct, but the finding shouldn't block: the runtime already enforces declared access. The sandbox throwsStorage collection not declared(packages/cloudflare/src/sandbox/bridge.ts:300), and theallowedHosts/ capability gates reject undeclared network/content/media/users access — so an undeclared capability cannot execute. It's a manifest/behavior mismatch (the plugin's undeclared feature silently fails), not an executable threat. Genuine over-reach is already caught by the specific malicious categories (data-exfiltration,credential-harvesting, …) which block on their own merits. Hard-blocking (hiding from install surfaces) a merely-inaccurate manifest is over-enforcement; a warning is the honest signal.moderation-policy.json):undeclared-accesscategoryautomated-block → warning,officialEffectblock → warn;policyVersion/effectiveAtbumped to2026-07-17.experimental.1. The label value is unchanged, soqueryLabels/subscription consumers are unaffected — only the effect softens (additive-safe). The resolver, code-AI prompt, and console are all policy-data-driven — no code hardcodes the classification, confirmed by grep.calibration/fixtures/clean-with-images): declares itsgallerystorage collection so it's a genuine clean baseline (matching its name andexpected: passed), rather than an accidental undeclared-access case.undeclared-accessnow resolves towarnedat any source/severity (never blocked); the "deterministic blocks at any severity" test moved tocredential-harvestingto keep its stated behavior true; policy-version-string pins updated;docs/moderation-model.mdmoved the row into the warning group.Adversary check (in the plan + verified): is there a real threat that manifests only as
undeclared-accesswith no other blocking finding, now let through with a warning? No. Runtime capability enforcement is label-independent — it gates execution, not the label — so undeclared access can never run regardless of the label; genuine malice either trips a specific blocking category (which still blocks, with undeclared-access riding along as a warning) or requires the attacker to declare the access to make it run, at which point it's judged as declared behavior. Residual executable harm: none.Follow-up: because the code-AI system prompt is data-driven from the policy's block∪warn category sets, it now presents
undeclared-accessas a warning category — a confirming calibration re-sweep is warranted before enforcement launch (tracked; the label + fixture changes here are the code side).Part of #1909. Related to #694.
Type of change
Checklist
pnpm typecheckpasses (all three labeler projects)pnpm lintpasses (no new diagnostics)pnpm testpasses (policy-resolver, policy, service, xrpc-router suites; full labeler suite 1005)pnpm formathas been runmoderation-policy.jsonis a labeler-internal fixture;@emdash-cms/labelerisprivate: true.AI-generated code disclosure
Screenshots / test output
Try this PR
Open a fresh playground →
A full working EmDash site, deployed from this branch. Each visit gets its own session-scoped sandbox: no login needed and no shared state. Try the admin, edit content, hit the public site.
Tracks
feat/labeler-undeclared-access-warn. Updated automatically when the playground redeploys.