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

Status
Research archive item
Category
Runtime
Length
6,464 words
Reading time
30 minutes
Report type
evaluation

Key topics

  • Runtime
  • .NET
  • Python
  • NuGet
  • Privacy
  • Semantic Systems
  • Research Archive
  • Audit

Research provenance

Archive status
Research archive item
Content identity
sha256:041c2ed8058ab768102293d391db2e86a693b2d1097245d6fb6e2a1baaf2895a

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:

LabelTopicProposal
Policy proposalImmutable identityEvery publisher receives immutable publisherId and immutable namespace root(s). Display names are mutable and non-authoritative.
Policy proposalLifecycleinvitedpending-verificationactivesuspendedrevokedclosed; historical IDs are never reused.
Policy proposalRolesPublisherOwner, PublisherAdmin, ReleaseManager, SecurityOfficer, BillingAdmin, ReviewerExclusionManager; no single role should both approve trust-tier promotions and publish production artifacts.
Policy proposalMFARequire 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 proposalAutomation authPrefer OIDC-based short-lived credentials for CI over long-lived API tokens; if tokens exist, scope them per project/channel and rotate aggressively.
Policy proposalSoDRequire 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:

LabelTierSubmission freedomReview depthInstall UX
Policy proposalFirst-partyBroadestFull automated + mandatory internal reviewDefault discoverable
Policy proposalVerified partnerBroad, but controlled namespaces and stricter escalationFull automated + targeted human reviewDiscoverable with verified badge
Policy proposalCurated third-partyModerate; restricted capabilities and special casesFull automated + human approval by defaultDiscoverable, opt-in install
Policy proposalExperimentalNarrowest; no native code, no network, no self-update, no privileged capabilitiesFull automated + manual gate on every versionHidden 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.

LabelFormatStrengthsWeaknessesRecommendation
Platform fact + Policy proposalNuGet .nupkgMature .NET ecosystem metadata, dependency model, author/repository signing supportStill a package-manager artifact, not a full plugin policy envelopeAccept as submission input for C#; do not publish raw to clients by default
Platform fact + Policy proposalPython wheel .whlInstallable binary distribution, platform tags, RECORD hashes, native-only when taggedNot a marketplace policy envelope; may still contain risky payloadsAccept as submission input for Python; preferred third-party input
Platform fact + Policy proposalPython sdistArchival source artifact, downstream build useBuild-time execution path, arbitrary build content, local wheel build on install pathReject for third-party publication; allow only controlled first-party build service
Platform fact + Policy proposalMSIXStrong Windows identity/signing/update semanticsHeavyweight for many plugin cases; packaging model may exceed plugin needsUse for standalone helper apps or isolated out-of-proc components only
Policy proposalPlain ZIP bundleSimple and language-neutralNo inherent trust/update semantics; easy to misuseUse only as the outer envelope if canonicalized and fully signed
Platform fact + Policy proposalStandalone EXE/DLLUseful for native helpersHighest review burden; Windows reputation/signing issuesRestrict to heightened-review tiers
Platform fact + Policy proposalOCI-style artifactStrong content-addressing, registry tooling, referrers for metadataNot a native desktop plugin client formatGood 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.json or equivalent evidence for .NET application/runtime bundles;
  • require pylock.toml or 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:

  1. Publisher signature over the submission artifact digests and the canonical outer manifest draft.
  2. Review-service signature over review findings, normalized metadata, policy decisions, and the exact digests of the reviewed payloads.
  3. 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.

LabelStateActor enteringGateNext statesTerminal
Policy proposaldraftpublishermetadata shell existsuploading, withdrawnno
Policy proposaluploadingpublisherresumable upload session openuploaded, abandonedno
Policy proposaluploadedsystemchecksum complete, quarantine storedstructural-validating, rejected-machineno
Policy proposalstructural-validatingsystemarchive, manifest, signature envelope parsepolicy-precheck, rejected-machineno
Policy proposalpolicy-prechecksystemnamespace, trust-tier, size, prohibited-content precheckanalysis-queued, changes-requested-machine, rejected-machineno
Policy proposalanalysis-queuedsystemscan jobs createdanalyzingno
Policy proposalanalyzingsystemstatic/dynamic/security analysis runningreview-ready, rejected-machine, timed-out-analysisno
Policy proposalreview-readysystemevidence package assembledin-human-review, approved-no-human, changes-requested-humanno
Policy proposalin-human-reviewreviewerreviewer assignment + COI check passedapproved-human, changes-requested-human, rejected-human, suspended-reviewno
Policy proposalapproved-no-humansystemlow-risk policy pathsigning, stagedno
Policy proposalapproved-humanreviewerreviewer decision threshold metsigning, stagedno
Policy proposalsigningdistribution servicereview signature + publication metadata preparedstaged, rejected-machineno
Policy proposalstagedrelease managerrollout plan attachedpublished, withdrawnno
Policy proposalpublisheddistribution servicesigned catalog updatedsuspended, revoked, withdrawn-from-discoveryno
Policy proposalsuspendedtrust & safetytemporary holdpublished, revoked, withdrawn-from-discoveryno
Policy proposalrevokedtrust & safetysigned revocation issuedsupersededyes
Policy proposalwithdrawnpublisher or adminunpublished draft/staged removalyes
Policy proposalwithdrawn-from-discoveryadminno longer listed, evidence retainedrevokedno
Policy proposalrejected-machinesystemnon-overridable technical/policy failuredraft-replacementyes
Policy proposalrejected-humanreviewerhuman decision denydraft-replacementyes
Policy proposalchanges-requested-machinesystemfixable machine findingsdraft-replacementno
Policy proposalchanges-requested-humanreviewerfixable human findingsdraft-replacementno
Policy proposaldraft-replacementpublishernew artifact on same submission thread or successor submissionuploadingno
Policy proposalabandonedsystemupload never completedyes
Policy proposaltimed-out-analysissystemanalysis incomplete or unsafe budget exceededanalysis-queued, changes-requested-machineno
Policy proposalsupersededsystemnewer trusted replacement publishedyes

Policy proposal. Rejection codes should be stable and machine-readable:

  • UPL-* upload failures
  • STR-* structural/archive failures
  • MET-* manifest and metadata failures
  • SIG-* signature and attestation failures
  • DEP-* dependency failures
  • SEC-* secret, malware, and vulnerability failures
  • POL-* policy denials
  • REV-* reviewer decisions
  • ROL-* rollout/publish failures
  • REVOC-* 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.

LabelStageInputsOutputsExecutes package codeFailure codesEvidenceFalse positive / false negativeTimeout / retry
Policy proposalUpload quarantinebytes, claimed digest, metadata shellimmutable quarantined blobnoUPL-001..099blob digest, uploader identitylow / noneresumable, retryable
Policy proposalStructural validationarchive bytesnormalized file listingnoSTR-101..199path map, sizes, duplicate-path reportlow / medium for malformed edge casesshort / retry only on infra
Policy proposalSignature envelope parseclaimed signatures, certs, attestationsparsed signer setnoSIG-201..239cert chain info, signature resultmedium / mediumshort / retry only on infra
Policy proposalCanonical manifest validationmanifest.jsonnormalized metadatanoMET-301..349schema reportmedium / lowshort / retryable after fix
Policy proposalPackage-format validation.nupkg, .whl, PE/MSIXper-format findingsnoSTR-160, MET-350package parser outputmedium / mediumshort / retryable after fix
Policy proposalDependency closure analysislock files, manifest, inner metadataresolved dependency graphnoDEP-401..469purl graph, source provenancemedium / mediummoderate / retryable after fix
Policy proposalStatic security analysisassemblies, Python bytecode/source, PE metadataalerts and scoresnoSEC-501..579SARIF-like recordmedium / highmoderate / retry only on infra
Policy proposalSecret scanningall files except allowed test fixturessecret findingsnoSEC-580..599matched detector reportmedium / mediumshort / retryable after fix
Policy proposalVulnerability/reputation analysisdependency graph, digests, namesvuln and reputation findingsnoSEC-600..649vuln IDs, reputation inputsmedium / highshort / retryable
Policy proposalDynamic isolated analysisreviewed unpacked payload in sandboxruntime telemetryyes, in sandboxSEC-650..699trace logs, syscall/network/process graphmedium / highbudgeted / retry limited
Policy proposalPolicy evaluationall findingsdecision recommendationnoPOL-701..779policy snapshot, matched ruleslow / mediumshort / retryable if rules updated
Policy proposalHuman reviewevidence bundlereview decisionno, unless reviewer triggers controlled replayREV-801..849reviewer notes, approvals, COI recordmedium / lowSLA-based / retry by reassignment
Policy proposalPublication signingapproved manifest + digestssigned publication metadatanoROL-901..919signing recordlow / lowshort / retryable
Policy proposalPost-publication monitoringtelemetry, advisories, scandals, retrospectivesadvisories, suspensions, revocationsnoREVOC-950..999incident recordmedium / highcontinuous

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 of ctypes, subprocess, dynamic import loaders, native .pyd files, 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:

LabelResourcePurpose
Policy proposal/publishersidentity, verification, suspension, recovery, keys
Policy proposal/namespacesreservations, delegation, transfer, disputes
Policy proposal/pluginsimmutable plugin identity and mutable display metadata
Policy proposal/plugin-versionsimmutable version objects and status
Policy proposal/submissionsdraft/review workflow container
Policy proposal/uploadsresumable upload sessions and checksum completion
Policy proposal/artifactscontent-addressed blobs and derived payloads
Policy proposal/findingsmachine-readable scan and policy findings
Policy proposal/reviewshuman review records and decisions
Policy proposal/attestationsprovenance, SBOM signatures, verification summaries
Policy proposal/rolloutsstaged release, cohorts, freeze, rollback
Policy proposal/advisoriesvulnerabilities, incidents, customer notices
Policy proposal/revocationssigned revocation records and severity
Policy proposal/evidenceimmutable evidence bundles with controlled access
Policy proposal/catalogsigned 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 POST operations accept Idempotency-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.

LabelThreatPrimary controlsDetection and containment
Supply-chain synthesisMalicious publisherstrict permission model, trust tiers, human review, sandbox, no client-side restorepolicy denial, staged rollout, revocation
Supply-chain synthesisCompromised publisher accountphishing-resistant MFA, short-lived OIDC tokens, offboarding review, key rotationunusual submission signals, suspend publisher
Supply-chain synthesisCompromised build systemprovenance requirements, rebuild checks, verified source/workflow bindingattestation mismatch, trust-tier downgrade
Supply-chain synthesisDependency confusionsource mapping, mirrored sources, exact pins, no client live restoreblock unapproved source and mirror drift
Supply-chain synthesisTyposquatting / impersonationreserved namespaces, name review, similarity detectionquarantine submission, trust-and-safety review
Supply-chain synthesisPoisoned updateimmutable versions, TUF metadata, staged rollout, permission re-consenthalt rollout, rollback cohort
Supply-chain synthesisSigning-key thefttiered key roles, offline root, fast rotation, transparency log, advisoryrevoke keys, reissue metadata
Supply-chain synthesisReviewer compromiseSoD, reviewer COI, audit logs, sampled re-reviewfreeze review lane, re-adjudicate
Supply-chain synthesisScanner evasionlayered scanners, static + dynamic + human review, adversarial fixturesretrospective detection, post-publish advisory
Supply-chain synthesisArchive bomb / traversalstructural validation before executionimmediate machine reject
Supply-chain synthesisNative payload abuseheightened review, Authenticode/MSIX, P/Invoke/native import policyquarantine or trust-tier restriction
Supply-chain synthesisCatalog rollback / freeze / mix-and-matchTUF root/snapshot/timestamp/targets, expiry, version monotonicityclient detection and fail closed
Supply-chain synthesisTransparency equivocationinclusion proofs, external witnesses if used, public rootsclient/auditor verification
Supply-chain synthesisTelemetry leakagepermission manifest, denial by default, endpoint review, sandbox tracingconsent gates, suspension
Supply-chain synthesisRevocation abusesigned revocation objects, threshold approval, reason codes, audit trailappeal 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:

  1. pinned root metadata;
  2. timestamp freshness;
  3. snapshot version and hash;
  4. targets metadata and delegated channel metadata;
  5. envelope digest and all contained payload digests;
  6. publisher/review/distribution signatures;
  7. compatibility, permission, and trust-tier policy;
  8. 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:

LabelFixtureLanguageExpected result
Policy proposalvalid minimal reviewed envelopebothaccept
Policy proposalpath traversal entries (../, drive roots, ADS)bothSTR-111 reject
Policy proposalduplicate normalized paths / case collisionsbothSTR-112 reject
Policy proposalextreme compression ratio archive bombbothSTR-113 reject
Policy proposalsymlink / hardlink escapebothSTR-114 reject
Policy proposalmalformed wheel missing correct RECORDPythonSTR-161 reject
Policy proposalwheel with mismatched RECORD hashesPythonSIG-231 reject
Policy proposalsdist requiring build hookPythonPOL-711 reject
Policy proposalunexpected native .pyd or DLL in “pure” packagePython/.NETPOL-721 escalate
Policy proposalunsigned PE payloadbothSIG-241 escalate or reject by tier
Policy proposaldependency confusion reference to unapproved sourcebothDEP-421 reject
Policy proposaltyposquat name similarity to reserved first-party pluginbothPOL-731 quarantine
Policy proposalmanifest/signature digest mismatchbothSIG-211 reject
Policy proposalembedded secret/tokenbothSEC-581 reject
Policy proposalnew permission added relative to prior versionbothREV-812 consent-required + human review
Policy proposalknown vulnerable dependency over thresholdbothSEC-611 reject or escalate
Policy proposalrollback attack on catalog metadatacatalogclient rejects update
Policy proposalstale/frozen timestamp metadatacatalogclient rejects refresh
Policy proposalequivocated targets metadatacatalogclient rejects and reports
Policy proposalpost-publication emergency revocationcatalogclient 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:

LabelPhaseAllowed publishersPrerequisites to enter phaseExpansion gate
Policy proposalFirst-party onlyinternal teamssigned catalog, client verification, sandbox, SBOM/provenance retention, emergency revocationtwo successful internal incident drills
Policy proposalSelected partnersverified organizationsnamespace reservation, MFA/OIDC, human review, legal/support process, evidence exportpartner pilot with staged rollout and rollback proofs
Policy proposalCurated publicapproved third partiesanti-abuse tooling, reputation systems, reviewer capacity, public policy docs, appeals pathsustained low false-negative/false-positive rates
Policy proposalBroader ecosystemwider communitymature moderation, scalable trust-and-safety, economic controls, transparency/audit maturityexecutive 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.

LabelSourceWhy it matters
StandardNuGet signed packages, author vs repository signingDefines core package-signing semantics for .nupkg and repository trust.
Platform factNuGet Package Source Mapping and dependency-confusion warningsSupports mirror-first, deterministic source policy.
Platform factNuGet ID prefix reservation and organization accountsInforms namespace governance and identity/display-name separation.
Platform factNuGet lock files and locked modeSupports exact dependency closure for reproducible restore.
StandardPython wheel specification, RECORD, tags, SBOM directoryDefines installable Python binary distribution semantics.
StandardPython source distribution format and PEP 517Explains why sdist implies build-time execution risk.
Platform factPyPI Trusted Publishing and digital attestationsInforms OIDC publishing, short-lived tokens, and index-hosted attestations.
Platform factPyPI roles, name retention, yanking, project status markersInforms ownership, abandonment, quarantine, archival, and non-destructive removal.
StandardSigstore and RekorBasis for keyless signing and transparency-log semantics.
Standardin-toto Attestation FrameworkBasis for typed provenance and review attestations.
StandardSLSA specificationBasis for provenance maturity targets.
StandardSPDX and CycloneDXBasis for SBOM generation, interchange, and retention.
StandardTUF specificationBasis for rollback/freeze/mix-and-match resistance and root/snapshot/timestamp roles.
External guidanceNIST SSDF and NIST supply-chain CI/CD guidanceBasis for pipeline control placement and secure SDLC expectations.
External guidanceCISA SBOM guidanceReinforces SBOM as operational supply-chain data, not paperwork.
External guidanceOWASP SCVS and CI/CD dependency-chain abuse guidanceReinforces 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.