Runtime
Enterprise Plugin Submission, Review, Signing, and Distribution for C and Python
Report summary
Policy proposal. The strongest design for an enterprise plugin ecosystem is not to publish raw developer-produced .nupkg, .whl, sdist, or arbitrary ZIP files directly to clients. Instead, accept those as submission inputs , generate review evidence against them, then publish a language-neutral, sign
Key topics
- Runtime
- .NET
- Python
- NuGet
- Privacy
- Semantic Systems
- Research Archive
- Audit
Research provenance
For citation, use the report title and canonical URL. Archival presence does not establish authorship or promote report statements into portfolio evidence.
Source availability: 51 citation markers in the source export have no recoverable source links. Those markers are omitted from this reader; any supplied bibliography and ordinary links remain. Check the original sources before relying on the cited claims.
This page renders the archived Markdown as safe, formatted HTML. It is background research and does not become a portfolio claim without evidence review.
Full report
On this page
Executive recommendation
Policy proposal. The strongest design for an enterprise plugin ecosystem is not to publish raw developer-produced .nupkg, .whl, sdist, or arbitrary ZIP files directly to clients. Instead, accept those as submission inputs, generate review evidence against them, then publish a language-neutral, signed plugin envelope that contains a reviewed runtime payload, immutable metadata, checksums, SBOM pointers, provenance attestations, policy decisions, and client-consumable revocation metadata. On the client side, install only from that signed catalog, never by live-restore from public package feeds and never by executing package build scripts during install. This approach aligns best with how NuGet repository signing, Python wheels, TUF-style update metadata, Sigstore/in-toto attestations, and NIST SSDF-style supply-chain controls each solve different parts of the chain rather than the whole problem alone.
Supply-chain synthesis. For publisher governance, separate mutable branding from immutable security identity. Give every publisher an immutable publisherId; give every plugin an immutable pluginId; and treat human-facing display names as non-authoritative labels that can change without changing ownership. Namespace control should combine reserved prefixes, organization verification, strong MFA, short-lived automation credentials, threshold-controlled signing roles, and explicit transfer/abandonment policies. NuGet’s prefix reservation and package owner model, plus PyPI’s name-retention and organization-role models, show that registries need both technical controls and human governance to avoid impersonation, abandonment takeover, and ambiguous ownership.
Policy proposal. For artifact policy, prefer these publication rules: C#: accept .nupkg or a deterministic runtime bundle as submission input; publish a reviewed runtime bundle inside the outer envelope. Python: accept .whl as the normal submission input; reject sdist for third-party publication; allow sdist only in a tightly controlled first-party build service that outputs reviewed wheels; publish wheels or a reviewed wheelhouse-derived runtime bundle inside the outer envelope. Native/standalone executables: allow only when absolutely necessary, require Authenticode or MSIX signing plus heightened review, and mark them as higher-risk trust tier content. The reason is straightforward: wheels are installable binary distributions with RECORD-based file hashing, while sdist exists specifically to be built and may carry arbitrary build content and build hooks; similarly, NuGet packages are ZIP-based containers with metadata and repository-signing support, but the client should not be responsible for resolving new unreviewed dependencies at install time.
Policy proposal. For publication and containment, use a TUF-inspired catalog with pinned root keys, delegated channel metadata, short-lived timestamp metadata, monotonically increasing versions, and signed revocation records. Preserve installed artifacts locally for rollback and forensics, but remove revoked plugins from discovery and block fresh activation according to severity. Offline clients should continue to function against a last-known-good catalog until metadata expiry, after which they should fail closed for new installs and fail according to policy for existing critical revocations. TUF exists precisely to mitigate rollback, freeze, mix-and-match, and mirror attacks that a plain signed index does not address well.
Executive recommendation. Adopt a four-phase marketplace plan: first-party only, selected verified partners, curated third-party, then broader ecosystem. Do not open the next tier until identity proofing, automated validation, reviewer evidence capture, reproducible build policy, emergency revocation, client rollback protection, and adversarial fixture coverage are all operational. NIST SSDF, NIST supply-chain CI/CD guidance, OWASP SCVS, and CISA SBOM guidance all reinforce that supply-chain assurance is a layered operating model, not a single scan or a single signature.
Scope, assumptions, and zero-access declaration
Scope, assumptions, and hard boundary
Scope. This report is a generic proposal for a plugin submission and distribution ecosystem for Windows desktop software that may host first-party and third-party plugins written in C# or Python. It does not assume or inspect any existing product pipeline, package registry, service endpoint, credential, catalog, source tree, portal, or operational workflow. All schemas, APIs, state machines, and examples below are proposed designs meant to be testable by engineers and reviewers, not descriptions of a live system.
Unknown. The following remain deliberately unknown because they are product-specific policy choices rather than discoverable standards questions: the host’s exact plugin ABI, whether Python runs in-proc or out-of-proc, whether the product ships its own runtime, whether plugins are UI-only or may execute background work, whether customers are air-gapped, and whether the ecosystem will permit network egress, self-update, or unmanaged native code. These unknowns materially affect final policy, but they do not change the core recommendation to separate submission artifacts from published client artifacts and to center the system on signed metadata, immutable versions, and strong review evidence.
Source and standards method
Source and standards method. This report prioritizes official platform specifications and supply-chain standards: NuGet packaging and signing guidance, Python packaging specifications and PyPI security documentation, Microsoft Windows signing guidance for MSIX and executables, Sigstore and Rekor documentation, in-toto attestations, SLSA provenance requirements, SPDX and CycloneDX SBOM standards, TUF repository metadata design, NIST SSDF, NIST CI/CD supply-chain guidance, CISA SBOM guidance, and OWASP SCVS. Where those sources do not decide policy, this report marks its reasoning as Supply-chain synthesis or Policy proposal.
Limitations
Limitations. No public standard fully specifies a “plugin marketplace for mixed-language desktop extensions” end to end. NuGet and Python packaging standards define package formats and some trust semantics, but they do not define enterprise reviewer workflows, permission-consent UX, emergency revocation behavior for offline desktop hosts, or a unified identity and namespace model across C# and Python ecosystems. Those parts of this report are necessarily design proposals informed by adjacent standards rather than dictated by them.
Publisher and namespace governance with the package envelope
Trust and governance model
Policy proposal. Use the following publisher lifecycle and trust model:
| Label | Topic | Proposal |
|---|---|---|
| Policy proposal | Immutable identity | Every publisher receives immutable publisherId and immutable namespace root(s). Display names are mutable and non-authoritative. |
| Policy proposal | Lifecycle | invited → pending-verification → active → suspended → revoked → closed; historical IDs are never reused. |
| Policy proposal | Roles | PublisherOwner, PublisherAdmin, ReleaseManager, SecurityOfficer, BillingAdmin, ReviewerExclusionManager; no single role should both approve trust-tier promotions and publish production artifacts. |
| Policy proposal | MFA | Require phishing-resistant MFA for all human accounts in non-experimental tiers; recovery codes must be mandatory, with manual recovery requiring identity proofing and cooling periods. |
| Policy proposal | Automation auth | Prefer OIDC-based short-lived credentials for CI over long-lived API tokens; if tokens exist, scope them per project/channel and rotate aggressively. |
| Policy proposal | SoD | Require two-person control for namespace transfer, signing-key rollover, trust-tier promotion, and emergency revocation cancellation. |
Platform fact. PyPI’s Trusted Publishing model exists specifically to replace long-lived upload credentials with OIDC-based trust and short-lived tokens, and PyPI documents that those tokens expire within 15 minutes; PyPI also records stable repository-owner identifiers to resist account resurrection attacks. NuGet likewise distinguishes individual accounts from organizations, and organization identities are collaborative overlays on top of user identities rather than a substitute for them. These platform patterns strongly support a proposal that automation credentials should be ephemeral and that human identity should remain attributable even when publishing under an organization label.
Platform fact. PyPI requires two-factor authentication and explicitly recommends multiple second factors plus recovery codes; PyPI’s public help also describes account recovery paths for lost email or lost second factors. NuGet’s security guidance states that every nuget.org account has 2FA enabled. These platform facts support mandating strong MFA and formal recovery workflows in any enterprise plugin registry.
Namespace policy and ownership transfer
Policy proposal. Namespace allocation should combine three layers: first, a short immutable namespace root such as com.example.analytics; second, a human-facing display name such as Example Analytics; third, optional marketing aliases searchable in the portal but never used in security decisions. Only the immutable namespace may appear in signing identities, policy rules, dependency declarations, transparency entries, and trust decisions.
Supply-chain synthesis. Reserved prefixes and abandonment rules are both necessary. NuGet’s prefix reservation shows how verified prefix control reduces confusion for future packages, while PyPI’s name-retention policy shows why a registry also needs explicit rules for reachability, abandonment, transfer, invalid projects, and anti-squatting. An enterprise plugin ecosystem should therefore reserve namespace roots early for first-party and verified partners, reject squatting or “empty placeholder” ownership claims, and require evidence for any transfer request.
Policy proposal. Ownership transfers should be allowed only through a signed transfer object with: current owners, incoming owners, namespace scope, effective UTC timestamp, reason code, proof of verified control of legal entity or domain, reviewer approval, cooling-off period, and rollback window. For abandoned plugins, apply a PyPI-like test: unreachable owner, no release activity for a long period, demonstrable successor maintenance, and documented failed contact attempts. Never reassign an active reachable namespace against the objections of a verified current owner except for legal/compliance removal.
Trust tiers
Policy proposal. Use four trust tiers and make the tier visible in the catalog metadata but separate from publisher identity:
| Label | Tier | Submission freedom | Review depth | Install UX |
|---|---|---|---|---|
| Policy proposal | First-party | Broadest | Full automated + mandatory internal review | Default discoverable |
| Policy proposal | Verified partner | Broad, but controlled namespaces and stricter escalation | Full automated + targeted human review | Discoverable with verified badge |
| Policy proposal | Curated third-party | Moderate; restricted capabilities and special cases | Full automated + human approval by default | Discoverable, opt-in install |
| Policy proposal | Experimental | Narrowest; no native code, no network, no self-update, no privileged capabilities | Full automated + manual gate on every version | Hidden behind explicit admin enablement |
Supply-chain synthesis. A trust tier must never mean “safe by default” or “scan-clean.” PyPI’s Trusted Publishing documentation explicitly says that authentication does not assert code safety or author trustworthiness, and NuGet’s signing model similarly distinguishes integrity and origin from broader risk judgment. The tier should therefore control review and client UX, not override security policy.
Package formats and the language-neutral outer envelope
Supply-chain synthesis. The comparison below is based on official NuGet, wheel, sdist, MSIX, and OCI specifications plus Microsoft signing guidance. It mixes platform facts with policy judgment about fitness for an enterprise plugin marketplace.
| Label | Format | Strengths | Weaknesses | Recommendation |
|---|---|---|---|---|
| Platform fact + Policy proposal | NuGet .nupkg | Mature .NET ecosystem metadata, dependency model, author/repository signing support | Still a package-manager artifact, not a full plugin policy envelope | Accept as submission input for C#; do not publish raw to clients by default |
| Platform fact + Policy proposal | Python wheel .whl | Installable binary distribution, platform tags, RECORD hashes, native-only when tagged | Not a marketplace policy envelope; may still contain risky payloads | Accept as submission input for Python; preferred third-party input |
| Platform fact + Policy proposal | Python sdist | Archival source artifact, downstream build use | Build-time execution path, arbitrary build content, local wheel build on install path | Reject for third-party publication; allow only controlled first-party build service |
| Platform fact + Policy proposal | MSIX | Strong Windows identity/signing/update semantics | Heavyweight for many plugin cases; packaging model may exceed plugin needs | Use for standalone helper apps or isolated out-of-proc components only |
| Policy proposal | Plain ZIP bundle | Simple and language-neutral | No inherent trust/update semantics; easy to misuse | Use only as the outer envelope if canonicalized and fully signed |
| Platform fact + Policy proposal | Standalone EXE/DLL | Useful for native helpers | Highest review burden; Windows reputation/signing issues | Restrict to heightened-review tiers |
| Platform fact + Policy proposal | OCI-style artifact | Strong content-addressing, registry tooling, referrers for metadata | Not a native desktop plugin client format | Good backend transport/mirroring option, not preferred host install format |
Policy proposal. The published artifact should be a single plugin envelope file, for example plugin.zip or plugin.pef, with this contract:
- outer manifest is canonicalized UTF-8 JSON;
- one or more immutable payload descriptors identify exact inner reviewed artifacts by digest;
- no network resolution is required during install;
- all dependencies required at runtime are either bundled, mirrored, or explicitly satisfied by the host’s approved shared runtime set;
- the host installs only from the envelope after verifying signatures, thresholds, catalog metadata, and policy compatibility.
Canonical files, dependencies, and prohibited content
Platform fact. Wheels require a .dist-info/RECORD file with secure hashes for almost all files and require installers to verify them during extraction; wheels also define platform compatibility tags and reserve .dist-info/sboms/ for included SBOM files. Source distributions, by contrast, are allowed to contain whatever the build system needs, and tooling is expected to build from them. On the .NET side, NuGet metadata carries dependency IDs and version ranges, and NuGet lock-file support exists specifically to make restore repeatable in CI/CD rather than allowing restore to drift silently.
Policy proposal. Treat these files as canonical and hash-stable for signing purposes:
- outer
manifest.json; - digest files for every embedded artifact;
- publisher signature bundle;
- review-service signature bundle;
- SBOM document digests;
- provenance attestation digests;
- license and notice manifests;
- compatibility and permission manifests.
For inner artifacts, hash the exact bytes of the reviewed file as stored. Do not normalize payload bytes after review. If the build service wants deterministic rebuild comparison, do that as a separate evidence artifact, not by rewriting the publisher artifact.
Policy proposal. Dependency policy should be mirror-first, client-offline, lock-required:
- no client-side restore from public NuGet or PyPI;
- require exact-version plus hash pinning for all resolved runtime dependencies;
- require
packages.lock.jsonor equivalent evidence for .NET application/runtime bundles; - require
pylock.tomlor equivalent wheel-set lock evidence for Python submissions when available; - prohibit floating versions in publication artifacts;
- allow shared-host dependencies only from a signed, host-owned compatibility baseline.
NuGet locked mode and Python pylock.toml both support this direction: NuGet lock files are meant to make restore exact in CI/CD, and the Python lock specification requires wheel support and permits tools not to support sdist at all.
Policy proposal. Prohibited content should include: signing keys, customer credentials, service tokens, private logs, prompt transcripts, sampled model outputs, crash dumps containing customer data, undocumented network bootstrap installers, self-updaters, hidden or obfuscated payloads, executable unpackers, unsigned kernel drivers, and install-time build hooks. For Python, reject sdist, setup.py install, backend hooks that must run at install time, and direct VCS installs in published packages. For .NET, reject packages that require live restore from unapproved sources or that inject arbitrary build targets into the customer environment unless the host explicitly supports and sandboxes them.
Generic directory tree and manifest
Sample. Proposed published envelope layout:
plugin-envelope/
manifest.json
checksums/
SHA256SUMS
signatures/
publisher.dsse.json
review-service.dsse.json
distribution-service.dsse.json
provenance/
publisher.slsa.intoto.jsonl
rebuild.slsa.intoto.jsonl
sbom/
sbom.spdx.json
sbom.cdx.json
policy/
permissions.json
capabilities.json
compatibility.json
notices/
LICENSE.txt
NOTICE.txt
THIRD-PARTY-NOTICES.txt
docs/
README.md
CHANGELOG.md
support.md
icons/
icon-128.png
payload/
dotnet/
plugin.runtime.bundle.zip
original.nupkg
python/
plugin-1.2.3-py3-none-any.whl
wheelhouse/
dep-a-4.5.6-py3-none-any.whl
dep-b-7.8.9-cp312-win_amd64.whl
Sample. Proposed canonical manifest.json:
{
"schemaVersion": "1.0.0",
"pluginId": "com.example.analytics.csv-exporter",
"publisherId": "pub_01JY8W93VJ5T2Q6M4PW7N9G3AZ",
"displayName": "CSV Exporter",
"version": "1.2.3",
"channel": "stable",
"releaseTimestampUtc": "2026-07-11T00:00:00Z",
"trustTier": "verified-partner",
"entrypoints": [
{
"language": "dotnet",
"type": "out-of-proc",
"path": "payload/dotnet/plugin.runtime.bundle.zip",
"sha256": "9b2b0d..."
},
{
"language": "python",
"type": "in-proc",
"path": "payload/python/plugin-1.2.3-py3-none-any.whl",
"sha256": "2e40bf..."
}
],
"compatibility": {
"hostApiVersionRange": "[3.4.0,4.0.0)",
"os": ["windows-10-22H2", "windows-11-24H2"],
"architectures": ["x64", "arm64"],
"python": ["cp312"],
"dotnet": ["net8.0"]
},
"capabilities": [
"document.export",
"ui.panel"
],
"permissions": {
"filesystem": ["user-selected-paths"],
"network": [],
"process": [],
"clipboard": false
},
"dependencies": [
{
"purl": "pkg:nuget/Example.Core@5.1.0",
"sha256": "f1aa..."
},
{
"purl": "pkg:pypi/attrs@25.1.0",
"sha256": "c75a69..."
}
],
"sbom": {
"spdx": "sbom/sbom.spdx.json",
"cyclonedx": "sbom/sbom.cdx.json"
},
"attestations": [
"provenance/publisher.slsa.intoto.jsonl",
"provenance/rebuild.slsa.intoto.jsonl"
],
"publisherSignature": "signatures/publisher.dsse.json",
"reviewSignature": "signatures/review-service.dsse.json",
"distributionSignature": "signatures/distribution-service.dsse.json",
"licenses": [
"MIT",
"Python-2.0"
],
"noticesPath": "notices/THIRD-PARTY-NOTICES.txt"
}
Signing, provenance, SBOM, and transparency
Signature roles and trust decisions
Platform fact. NuGet distinguishes between author signatures and repository signatures. Author signatures assert that the package has not changed since the author signed it, while repository signatures provide integrity guarantees for all packages in a repository; nuget.org automatically repository-signs uploaded packages. PyPI’s newer attestation model likewise accepts cryptographic attestations from maintainers and third parties, and PyPI’s attestations are bound to individual uploaded files by strong digests.
Policy proposal. Use three distinct signing moments:
- Publisher signature over the submission artifact digests and the canonical outer manifest draft.
- Review-service signature over review findings, normalized metadata, policy decisions, and the exact digests of the reviewed payloads.
- Distribution-service signature over published catalog metadata, rollout metadata, and revocation/advisory records.
That split gives better blast-radius control than a single signature because compromise of the publisher does not let an attacker forge review approval, and compromise of the review service does not let an attacker rewrite historical publisher intent without being detected.
Policy proposal. Prefer Sigstore-style keyless signing for CI-attested publisher builds where feasible, but keep support for hardware-backed organizational signing keys for enterprises that need long-lived corporate identities or operate without public OIDC trust paths. Detached DSSE/in-toto envelopes are preferable to ad hoc signature sidecar files because they are already widely used for provenance and predicate-based attestations. Sigstore’s model centers on short-lived certificates and Rekor transparency entries; in-toto provides the typed attestation envelope that SLSA provenance uses.
Provenance and SLSA controls
Standard. SLSA defines provenance as information about who built an artifact, by what process, and from what inputs; higher levels raise the assurance that the artifact, provenance, and build process were not tampered with. NIST’s SSDF and NIST’s CI/CD supply-chain guidance both point toward integrating those controls into the pipeline itself rather than treating them as after-the-fact paperwork.
Policy proposal. Target these provenance expectations by trust tier:
- First-party: require SLSA-aligned provenance from a centrally managed build service, source control provenance, two-party review evidence, reproducible rebuild checks for high-risk plugins, and signed verification summaries.
- Verified partner: require build provenance and source provenance at minimum, plus verified OIDC publishing identity or registered signing certs.
- Curated third-party: require build provenance for the delivered artifacts, but allow weaker source-side guarantees during the earliest phase while compensating with stricter sandboxing and human review.
- Experimental: provenance optional for admission to the draft queue, mandatory before publication.
Supply-chain synthesis. Dynamic scans, malware scans, and provenance address different questions. Provenance can say “this artifact was built by this workflow from these inputs”; it cannot say “the code is benign.” PyPI’s own security model for Trusted Publishing is explicit that safer authentication does not assert code safety or author trustworthiness. That same distinction should be made explicit to reviewers and customers.
SBOM generation, validation, retention, and query model
Standard. SPDX is an international open standard for communicating software package and metadata information, and CycloneDX is an OWASP-backed BOM standard focused on security use cases. The Python wheel specification now reserves .dist-info/sboms/ for SBOM files describing software contained in the distribution archive, which is a notable sign that SBOMs are moving closer to first-class package content rather than external paperwork only. CISA’s SBOM guidance frames SBOMs as the software “ingredients list” used to understand software components and risks.
Policy proposal. Require both SPDX and CycloneDX at publication time when tooling supports it cleanly; otherwise require at least one and generate a normalized registry-side projection for query. Retain SBOMs for every published version forever, even after withdrawal or revocation, because the audit and incident-response value typically outlasts the release’s discoverability. Index them by pluginId, version, publisherId, purl, file digest, language ecosystem, license IDs, and vulnerability/advisory links. SPDX is better for broad interoperability and legal metadata; CycloneDX is often better for operational security workflows.
Transparency log scope and privacy risks
Platform fact. Sigstore’s Rekor transparency log records enough data to verify a signing event without the original private key and exposes an API for inclusion proofs and retrieval by public key or artifact. PyPI’s digital attestation documentation additionally requires inclusion proofs from Rekor and Fulcio CT for verified PyPI attestations.
Supply-chain synthesis. A useful transparency log for a plugin ecosystem should record: artifact digest, manifest digest, signer identity, certificate chain or key identifier, attestation type, submission ID, review decision digest, publication timestamp, channel, supersedence links, withdrawal/revocation records, and inclusion proofs. The main privacy and operational risk is metadata exposure rather than source-code exposure: unpublished plugin names, publisher identities, workflow names, cadence, and incident timing can all leak from transparency metadata. Therefore draft submissions should use a private internal log, and only published or security-relevant events should be replicated to the customer-facing transparency surface.
Key rotation and compromise recovery
Standard + Policy proposal. TUF’s root role is intentionally kept very secure and should be offline, while online roles such as timestamp are designed to have lower blast radius and shorter-lived trust. Apply that split here: keep root trust anchors offline and threshold-controlled; keep distribution metadata signing online but tightly scoped and rotated; keep publisher signing either OIDC-issued or HSM-backed. For compromise recovery: publish a signed key-compromise advisory, rotate affected keys, reissue affected metadata, re-sign still-trusted artifacts under replacement keys when policy permits, and mark unverifiable historical releases as untrusted-but-retained rather than silently disappearing them.
Submission state machine, validation pipeline, and API
Submission state machine
Policy proposal. Separate machine rejection from human review outcomes. The state machine below does that deliberately.
| Label | State | Actor entering | Gate | Next states | Terminal |
|---|---|---|---|---|---|
| Policy proposal | draft | publisher | metadata shell exists | uploading, withdrawn | no |
| Policy proposal | uploading | publisher | resumable upload session open | uploaded, abandoned | no |
| Policy proposal | uploaded | system | checksum complete, quarantine stored | structural-validating, rejected-machine | no |
| Policy proposal | structural-validating | system | archive, manifest, signature envelope parse | policy-precheck, rejected-machine | no |
| Policy proposal | policy-precheck | system | namespace, trust-tier, size, prohibited-content precheck | analysis-queued, changes-requested-machine, rejected-machine | no |
| Policy proposal | analysis-queued | system | scan jobs created | analyzing | no |
| Policy proposal | analyzing | system | static/dynamic/security analysis running | review-ready, rejected-machine, timed-out-analysis | no |
| Policy proposal | review-ready | system | evidence package assembled | in-human-review, approved-no-human, changes-requested-human | no |
| Policy proposal | in-human-review | reviewer | reviewer assignment + COI check passed | approved-human, changes-requested-human, rejected-human, suspended-review | no |
| Policy proposal | approved-no-human | system | low-risk policy path | signing, staged | no |
| Policy proposal | approved-human | reviewer | reviewer decision threshold met | signing, staged | no |
| Policy proposal | signing | distribution service | review signature + publication metadata prepared | staged, rejected-machine | no |
| Policy proposal | staged | release manager | rollout plan attached | published, withdrawn | no |
| Policy proposal | published | distribution service | signed catalog updated | suspended, revoked, withdrawn-from-discovery | no |
| Policy proposal | suspended | trust & safety | temporary hold | published, revoked, withdrawn-from-discovery | no |
| Policy proposal | revoked | trust & safety | signed revocation issued | superseded | yes |
| Policy proposal | withdrawn | publisher or admin | unpublished draft/staged removal | — | yes |
| Policy proposal | withdrawn-from-discovery | admin | no longer listed, evidence retained | revoked | no |
| Policy proposal | rejected-machine | system | non-overridable technical/policy failure | draft-replacement | yes |
| Policy proposal | rejected-human | reviewer | human decision deny | draft-replacement | yes |
| Policy proposal | changes-requested-machine | system | fixable machine findings | draft-replacement | no |
| Policy proposal | changes-requested-human | reviewer | fixable human findings | draft-replacement | no |
| Policy proposal | draft-replacement | publisher | new artifact on same submission thread or successor submission | uploading | no |
| Policy proposal | abandoned | system | upload never completed | — | yes |
| Policy proposal | timed-out-analysis | system | analysis incomplete or unsafe budget exceeded | analysis-queued, changes-requested-machine | no |
| Policy proposal | superseded | system | newer trusted replacement published | — | yes |
Policy proposal. Rejection codes should be stable and machine-readable:
UPL-*upload failuresSTR-*structural/archive failuresMET-*manifest and metadata failuresSIG-*signature and attestation failuresDEP-*dependency failuresSEC-*secret, malware, and vulnerability failuresPOL-*policy denialsREV-*reviewer decisionsROL-*rollout/publish failuresREVOC-*revocation events
Every transition must emit an immutable audit event with actor, prior state, new state, reason code, evidence digest, and UTC timestamp.
Ordered validation and review pipeline
Supply-chain synthesis. The ordered pipeline below is the practical heart of the design. It is intentionally front-loaded with checks that can run before any package code executes, because archives, metadata, signatures, and dependency references can all be validated without running untrusted code. That matches the spirit of package-format rules, TUF-style metadata validation, SSDF guidance, and PyPI’s recent hardening against archive-confusion attacks.
| Label | Stage | Inputs | Outputs | Executes package code | Failure codes | Evidence | False positive / false negative | Timeout / retry |
|---|---|---|---|---|---|---|---|---|
| Policy proposal | Upload quarantine | bytes, claimed digest, metadata shell | immutable quarantined blob | no | UPL-001..099 | blob digest, uploader identity | low / none | resumable, retryable |
| Policy proposal | Structural validation | archive bytes | normalized file listing | no | STR-101..199 | path map, sizes, duplicate-path report | low / medium for malformed edge cases | short / retry only on infra |
| Policy proposal | Signature envelope parse | claimed signatures, certs, attestations | parsed signer set | no | SIG-201..239 | cert chain info, signature result | medium / medium | short / retry only on infra |
| Policy proposal | Canonical manifest validation | manifest.json | normalized metadata | no | MET-301..349 | schema report | medium / low | short / retryable after fix |
| Policy proposal | Package-format validation | .nupkg, .whl, PE/MSIX | per-format findings | no | STR-160, MET-350 | package parser output | medium / medium | short / retryable after fix |
| Policy proposal | Dependency closure analysis | lock files, manifest, inner metadata | resolved dependency graph | no | DEP-401..469 | purl graph, source provenance | medium / medium | moderate / retryable after fix |
| Policy proposal | Static security analysis | assemblies, Python bytecode/source, PE metadata | alerts and scores | no | SEC-501..579 | SARIF-like record | medium / high | moderate / retry only on infra |
| Policy proposal | Secret scanning | all files except allowed test fixtures | secret findings | no | SEC-580..599 | matched detector report | medium / medium | short / retryable after fix |
| Policy proposal | Vulnerability/reputation analysis | dependency graph, digests, names | vuln and reputation findings | no | SEC-600..649 | vuln IDs, reputation inputs | medium / high | short / retryable |
| Policy proposal | Dynamic isolated analysis | reviewed unpacked payload in sandbox | runtime telemetry | yes, in sandbox | SEC-650..699 | trace logs, syscall/network/process graph | medium / high | budgeted / retry limited |
| Policy proposal | Policy evaluation | all findings | decision recommendation | no | POL-701..779 | policy snapshot, matched rules | low / medium | short / retryable if rules updated |
| Policy proposal | Human review | evidence bundle | review decision | no, unless reviewer triggers controlled replay | REV-801..849 | reviewer notes, approvals, COI record | medium / low | SLA-based / retry by reassignment |
| Policy proposal | Publication signing | approved manifest + digests | signed publication metadata | no | ROL-901..919 | signing record | low / low | short / retryable |
| Policy proposal | Post-publication monitoring | telemetry, advisories, scandals, retrospectives | advisories, suspensions, revocations | no | REVOC-950..999 | incident record | medium / high | continuous |
What must happen before any package code executes
Policy proposal. The following checks must complete before any package code executes:
- digest verification;
- archive traversal safety;
- duplicate-path and canonical-path detection;
- decompression-ratio and size limits;
- symlink and hardlink policy;
- file-type and magic-byte validation;
- manifest schema validation;
- signing and certificate validation;
- namespace/publisher authorization;
- dependency declaration parsing;
- prohibited-content screening;
- secret scanning;
- known-vulnerability and reputation checks on names and digests.
Platform fact. Python packaging now explicitly limits unsafe tar extraction behavior and requires safer extraction approaches for sdist, while PyPI rejects ZIP parser-confusion archives and is deprecating wheels with incorrect RECORD files. Those platform moves underscore that archive and metadata safety checks belong in the front of the pipeline rather than after any build or install attempt.
Static checks and dynamic analysis scope
Policy proposal. Static checks should include:
- for .NET: assembly metadata, strong-name presence if used, target frameworks, referenced assemblies, P/Invoke declarations, COM activation, reflection-heavy loaders, embedded resources, native library references, trimming/AOT clues, manifest resources, Authenticode status of PE files, and suspicious MSBuild assets if present;
- for Python: wheel metadata, entry points,
.dist-info/RECORD, platform tags, presence of compiled extensions, serialized bytecode, use ofctypes,subprocess, dynamic import loaders, native.pydfiles, install-data contents, and any attempts to mimic deprecated wheel signatures or malformed RECORD usage; - for native binaries: PE headers, imports, signed/unsigned state, packer/obfuscator indicators, drivers/services, unusual RWX sections, and network-beaconing imports;
- for all payloads: licenses, notices, URLs, hard-coded hosts, keys, tokens, telemetry endpoints, permission declarations, and hidden dual-use tools.
Policy proposal. Safe dynamic analysis can run only in a tightly isolated environment with no real customer data, no reusable secrets, deny-by-default egress, fake services, fake filesystem targets, and strict CPU/time/memory ceilings. It can safely observe install/startup behavior, process trees, file writes, registry writes, socket attempts, DLL loading, imports, plugin-host handshake behavior, and declared capability use. It can never prove the absence of malicious behavior, the absence of logic bombs, or the absence of behaviors gated on time, locale, target presence, or external instructions.
Human review and evidence rules
Policy proposal. Human review should be mandatory when any of the following are true: new publisher in non-first-party tier; any native code; any network capability; any permission increase; any unsigned PE payload; any obfuscation; any sandbox escape indicator; any self-update mechanism; any executable download behavior; any code generation; any macro/script interpreter embedding; any dynamic dependency acquisition; any secret or credential finding; any vulnerable dependency above policy threshold; or any trust-and-safety signal such as typosquatting, impersonation, or policy-evasion patterns.
Policy proposal. Every reviewer action should record: reviewer identity, conflict-of-interest attestation, assigned trust tier, review checklist version, evidence digests, reproduced findings, requested changes, final decision, second approver if required, and the exact policy snapshot used. Reviewer comments should be immutable append-only records; edits should create new versions, not rewrite history.
Submission API proposal
Policy proposal. The generic API resource model should look like this:
| Label | Resource | Purpose |
|---|---|---|
| Policy proposal | /publishers | identity, verification, suspension, recovery, keys |
| Policy proposal | /namespaces | reservations, delegation, transfer, disputes |
| Policy proposal | /plugins | immutable plugin identity and mutable display metadata |
| Policy proposal | /plugin-versions | immutable version objects and status |
| Policy proposal | /submissions | draft/review workflow container |
| Policy proposal | /uploads | resumable upload sessions and checksum completion |
| Policy proposal | /artifacts | content-addressed blobs and derived payloads |
| Policy proposal | /findings | machine-readable scan and policy findings |
| Policy proposal | /reviews | human review records and decisions |
| Policy proposal | /attestations | provenance, SBOM signatures, verification summaries |
| Policy proposal | /rollouts | staged release, cohorts, freeze, rollback |
| Policy proposal | /advisories | vulnerabilities, incidents, customer notices |
| Policy proposal | /revocations | signed revocation records and severity |
| Policy proposal | /evidence | immutable evidence bundles with controlled access |
| Policy proposal | /catalog | signed client-consumable discovery metadata |
Sample. Generic operations and semantics:
POST /submissions
POST /submissions/{id}/uploads
PATCH /uploads/{id}/parts/{n}
POST /uploads/{id}:complete
GET /submissions/{id}
GET /submissions/{id}/findings
POST /submissions/{id}:replace-draft
POST /submissions/{id}:request-review
POST /reviews/{id}:approve
POST /reviews/{id}:changes-requested
POST /reviews/{id}:reject
POST /plugin-versions/{id}:stage
POST /rollouts
POST /rollouts/{id}:publish
POST /plugin-versions/{id}:suspend
POST /plugin-versions/{id}:revoke
GET /evidence/{id}
GET /catalog/channels/{channel}
Policy proposal. Idempotency and concurrency rules:
- all side-effecting
POSToperations acceptIdempotency-Key; - draft resources are mutable with
ETag+If-Match; - published version resources are immutable;
- uploads complete only when the declared whole-file SHA-256 matches the uploaded bytes;
- duplicate submissions with identical artifact digests should deduplicate storage but create separate submission records if metadata or publisher context differs;
- retries must be safe for upload-part replay and submission-creation replay;
- publication must use optimistic concurrency on channel metadata to prevent rollout races.
Policy proposal. Abuse and denial-of-service controls should include publisher quotas, namespace reservation caps, upload size ceilings, decompression ratio ceilings, submission/job concurrency budgets per organization, attestation-registration rate limits, sandbox CPU/network budgets, deduplicated storage by digest, and backpressure with explicit retry-after semantics. PyPI already rate-limits some trusted-publisher registration paths, and package ecosystems increasingly separate audit sources from package sources to manage performance and trust boundaries.
Threat model with publication, updates, rollback, revocation, testing, and phased rollout
Threat model
Supply-chain synthesis. The table below maps the required threat categories to concrete controls.
| Label | Threat | Primary controls | Detection and containment |
|---|---|---|---|
| Supply-chain synthesis | Malicious publisher | strict permission model, trust tiers, human review, sandbox, no client-side restore | policy denial, staged rollout, revocation |
| Supply-chain synthesis | Compromised publisher account | phishing-resistant MFA, short-lived OIDC tokens, offboarding review, key rotation | unusual submission signals, suspend publisher |
| Supply-chain synthesis | Compromised build system | provenance requirements, rebuild checks, verified source/workflow binding | attestation mismatch, trust-tier downgrade |
| Supply-chain synthesis | Dependency confusion | source mapping, mirrored sources, exact pins, no client live restore | block unapproved source and mirror drift |
| Supply-chain synthesis | Typosquatting / impersonation | reserved namespaces, name review, similarity detection | quarantine submission, trust-and-safety review |
| Supply-chain synthesis | Poisoned update | immutable versions, TUF metadata, staged rollout, permission re-consent | halt rollout, rollback cohort |
| Supply-chain synthesis | Signing-key theft | tiered key roles, offline root, fast rotation, transparency log, advisory | revoke keys, reissue metadata |
| Supply-chain synthesis | Reviewer compromise | SoD, reviewer COI, audit logs, sampled re-review | freeze review lane, re-adjudicate |
| Supply-chain synthesis | Scanner evasion | layered scanners, static + dynamic + human review, adversarial fixtures | retrospective detection, post-publish advisory |
| Supply-chain synthesis | Archive bomb / traversal | structural validation before execution | immediate machine reject |
| Supply-chain synthesis | Native payload abuse | heightened review, Authenticode/MSIX, P/Invoke/native import policy | quarantine or trust-tier restriction |
| Supply-chain synthesis | Catalog rollback / freeze / mix-and-match | TUF root/snapshot/timestamp/targets, expiry, version monotonicity | client detection and fail closed |
| Supply-chain synthesis | Transparency equivocation | inclusion proofs, external witnesses if used, public roots | client/auditor verification |
| Supply-chain synthesis | Telemetry leakage | permission manifest, denial by default, endpoint review, sandbox tracing | consent gates, suspension |
| Supply-chain synthesis | Revocation abuse | signed revocation objects, threshold approval, reason codes, audit trail | appeal path, revocation rollback with higher threshold |
Platform fact. TUF is explicitly designed to mitigate arbitrary installation, endless-data, freeze, mix-and-match, and rollback attacks; NuGet warns about dependency confusion when multiple sources are configured; Package Source Mapping exists specifically to make source resolution deterministic and safer; and OWASP’s CI/CD dependency-chain guidance names dependency confusion, typosquatting, dependency hijacking, and brandjacking as common patterns. Those are not theoretical threats for package ecosystems; they are design-shaping realities.
Distribution and update policy
Policy proposal. Distribution should use signed channels such as canary, preview, stable, lts, and emergency-freeze. Plugin versions are immutable. Channels point to immutable versions through signed metadata; channels themselves are what move. Compatibility cohorts should be expressed by host version, OS build, architecture, and policy posture. A plugin may be published to preview without ever reaching stable. Client policy should always persist the last-known-good version per plugin and per cohort.
Policy proposal. The client should verify, in order:
- pinned root metadata;
- timestamp freshness;
- snapshot version and hash;
- targets metadata and delegated channel metadata;
- envelope digest and all contained payload digests;
- publisher/review/distribution signatures;
- compatibility, permission, and trust-tier policy;
- local consent status and revocation status.
That ordering follows the TUF model in spirit: root delegates trust, timestamp proves freshness, snapshot prevents metadata mix-and-match, and targets delegates what content is trusted.
Policy proposal. Any update that expands requested permissions or capabilities must require fresh user or administrator consent even if the plugin is already trusted. Minor versioning must not bypass consent. Approval may be sticky only for a narrowly defined permission category and version range that was shown during install policy review.
Supply-chain synthesis. Removing a package from discovery while preserving audit evidence is the right default. PyPI’s yanking model is non-destructive and causes compliant installers to ignore the release except when explicitly pinned; PyPI also distinguishes yanking from deletion, and deletion is disruptive. That is the right mental model for plugins too: stop recommending or newly installing dangerous content without erasing historical truth.
Emergency revocation, rollback, and offline behavior
Policy proposal. Emergency revocation should publish a signed revocation record containing: plugin/version scope, severity (informational, blocked-on-new-install, blocked-on-activation, must-disable), reason code, evidence digest, effective UTC time, replacement recommendation if any, and expiry/review date. The host should never perform arbitrary remote deletion of bytes already on disk. Instead, it should mark local installations inactive or not activatable according to policy and preserve them for forensics and rollback if permitted.
Policy proposal. Offline clients should cache:
- the last trusted root chain,
- the last valid timestamp/snapshot/targets set,
- the installed envelope,
- the last-known-good activation decision,
- and a signed local incident journal.
If the catalog is stale but not expired, the client may continue using already permitted plugins. If metadata is expired and the plugin is under must-disable, the client should disable activation once the signed revocation is known. If the client has no revocation information and is fully offline, policy should distinguish between already-installed low-severity risk and known critical risk once reconnected.
Automated tests and adversarial fixtures
Policy proposal. The fixture catalog should include at least the following packages and expected dispositions:
| Label | Fixture | Language | Expected result |
|---|---|---|---|
| Policy proposal | valid minimal reviewed envelope | both | accept |
| Policy proposal | path traversal entries (../, drive roots, ADS) | both | STR-111 reject |
| Policy proposal | duplicate normalized paths / case collisions | both | STR-112 reject |
| Policy proposal | extreme compression ratio archive bomb | both | STR-113 reject |
| Policy proposal | symlink / hardlink escape | both | STR-114 reject |
| Policy proposal | malformed wheel missing correct RECORD | Python | STR-161 reject |
| Policy proposal | wheel with mismatched RECORD hashes | Python | SIG-231 reject |
| Policy proposal | sdist requiring build hook | Python | POL-711 reject |
| Policy proposal | unexpected native .pyd or DLL in “pure” package | Python/.NET | POL-721 escalate |
| Policy proposal | unsigned PE payload | both | SIG-241 escalate or reject by tier |
| Policy proposal | dependency confusion reference to unapproved source | both | DEP-421 reject |
| Policy proposal | typosquat name similarity to reserved first-party plugin | both | POL-731 quarantine |
| Policy proposal | manifest/signature digest mismatch | both | SIG-211 reject |
| Policy proposal | embedded secret/token | both | SEC-581 reject |
| Policy proposal | new permission added relative to prior version | both | REV-812 consent-required + human review |
| Policy proposal | known vulnerable dependency over threshold | both | SEC-611 reject or escalate |
| Policy proposal | rollback attack on catalog metadata | catalog | client rejects update |
| Policy proposal | stale/frozen timestamp metadata | catalog | client rejects refresh |
| Policy proposal | equivocated targets metadata | catalog | client rejects and reports |
| Policy proposal | post-publication emergency revocation | catalog | client stops discovery; activation depends on severity |
Supply-chain synthesis. The fixture set should be version-controlled, replayable in CI, and include both expected-safe and expected-malicious packages. PyPI’s archive-confusion hardening and modern package-manager vulnerability/audit paths are good reminders that packaging security regresses unless the test corpus includes adversarial parser cases, malformed metadata, and confusing-but-valid edge cases.
Phased ecosystem plan
Policy proposal. Use this staged rollout:
| Label | Phase | Allowed publishers | Prerequisites to enter phase | Expansion gate |
|---|---|---|---|---|
| Policy proposal | First-party only | internal teams | signed catalog, client verification, sandbox, SBOM/provenance retention, emergency revocation | two successful internal incident drills |
| Policy proposal | Selected partners | verified organizations | namespace reservation, MFA/OIDC, human review, legal/support process, evidence export | partner pilot with staged rollout and rollback proofs |
| Policy proposal | Curated public | approved third parties | anti-abuse tooling, reputation systems, reviewer capacity, public policy docs, appeals path | sustained low false-negative/false-positive rates |
| Policy proposal | Broader ecosystem | wider community | mature moderation, scalable trust-and-safety, economic controls, transparency/audit maturity | executive risk acceptance |
Supply-chain synthesis. Expanding before operational controls are ready is usually what creates the visible supply-chain incidents that later force painful reversals. NIST, OWASP, and CISA all point in the same direction: governance, evidence, and process are part of the security control set, not bureaucracy layered on top of it.
Open implementation and policy questions
Unknown. The final design still depends on several product decisions:
- whether C# and Python plugins run in-proc, out-of-proc, or both;
- whether the host permits native code at all;
- whether network access is ever allowed;
- whether plugin permissions are enforced by OS sandbox, process isolation, host mediation, or only contractual review;
- whether customers are commonly offline or air-gapped;
- whether the host ships a fixed Python runtime and a fixed .NET runtime;
- whether the host wants tenant-level policy overlays;
- and whether the ecosystem will support private tenant catalogs in addition to a central catalog.
Policy proposal. If those questions are unresolved, the safest default is: out-of-proc preferred, no network by default, no sdist, no self-update, no customer-data-in-package, immutable versions, signed TUF-style catalog, and mandatory human review for any native or permission-bearing plugin until the enforcement story is production-grade.
Sources
Sources. The most heavily used standards and official documents in this report are listed below. Each entry is included because it shaped a key design choice, not merely as background reading.
| Label | Source | Why it matters |
|---|---|---|
| Standard | NuGet signed packages, author vs repository signing | Defines core package-signing semantics for .nupkg and repository trust. |
| Platform fact | NuGet Package Source Mapping and dependency-confusion warnings | Supports mirror-first, deterministic source policy. |
| Platform fact | NuGet ID prefix reservation and organization accounts | Informs namespace governance and identity/display-name separation. |
| Platform fact | NuGet lock files and locked mode | Supports exact dependency closure for reproducible restore. |
| Standard | Python wheel specification, RECORD, tags, SBOM directory | Defines installable Python binary distribution semantics. |
| Standard | Python source distribution format and PEP 517 | Explains why sdist implies build-time execution risk. |
| Platform fact | PyPI Trusted Publishing and digital attestations | Informs OIDC publishing, short-lived tokens, and index-hosted attestations. |
| Platform fact | PyPI roles, name retention, yanking, project status markers | Informs ownership, abandonment, quarantine, archival, and non-destructive removal. |
| Standard | Sigstore and Rekor | Basis for keyless signing and transparency-log semantics. |
| Standard | in-toto Attestation Framework | Basis for typed provenance and review attestations. |
| Standard | SLSA specification | Basis for provenance maturity targets. |
| Standard | SPDX and CycloneDX | Basis for SBOM generation, interchange, and retention. |
| Standard | TUF specification | Basis for rollback/freeze/mix-and-match resistance and root/snapshot/timestamp roles. |
| External guidance | NIST SSDF and NIST supply-chain CI/CD guidance | Basis for pipeline control placement and secure SDLC expectations. |
| External guidance | CISA SBOM guidance | Reinforces SBOM as operational supply-chain data, not paperwork. |
| External guidance | OWASP SCVS and CI/CD dependency-chain abuse guidance | Reinforces software-component assurance and common registry attack patterns. |
Limitations. Some relevant public sources were less decisive than the standards above because they describe a single ecosystem rather than a mixed-language desktop plugin marketplace. For that reason, this report uses those sources to anchor the hard constraints and then makes explicit Policy proposals where standards do not decide the answer. The result is intentionally concrete and testable, but it remains a design recommendation, not a description of an existing service.