Skip to content

vscode-extensions: add optional Marketplace signature verification#482821

Open
cavebatsofware wants to merge 2 commits into
NixOS:masterfrom
cavebatsofware:vscode-signature-verification
Open

vscode-extensions: add optional Marketplace signature verification#482821
cavebatsofware wants to merge 2 commits into
NixOS:masterfrom
cavebatsofware:vscode-signature-verification

Conversation

@cavebatsofware

@cavebatsofware cavebatsofware commented Jan 22, 2026

Copy link
Copy Markdown
Contributor

vscode-extensions: add opt-in Marketplace signature verification

Adds opt-in cryptographic signature verification for VS Code Marketplace extensions, honoring the same signing contract VS Code enforces as a layer alongside hash pinning.

It is designed to not disrupt the free build path: verification uses Microsoft's unfree vsce-sign, so it is off by default and is never pulled into the default extension build, Hydra, or vscodium. Unfree is only ever reached through an explicit opt-in.

Relates to #522775

While resolving merge conflicts for this work I found it is possible to update to and install an extension whose signature is unavailable. #522775 filters those out via marketplace metadata; this PR adds actual cryptographic verification of the signature itself.

How it works

Pinning (always free). Setting signatureHash on mktplcRef fetches and pins the extension's signature archive (a fixed-output derivation) alongside the VSIX hash. This alone pulls nothing unfree.

Build-time verification (opt-in). Set verifySignature = true on an extension, or vscodeExtensions.verifySignature = true in nixpkgs config to cover every signed extension. The build then verifies the VSIX against its signature with vsce-sign in preUnpackHooks. Because vsce-sign is unfree it is off by default, so signed extensions stay free-buildable on Hydra and usable with vscodium. It is also skipped automatically when the vscode package does not expose passthru.hasVsceSign = true (e.g. vscodium), keeping that path free regardless of the flag.

Update-time verification. The update script and the build hook share one verifier (verify-vsix-signature.sh), so both honor the same vsce-sign invocation and exit codes. --with-signature fetches, adds, and verifies signatureHash on every platform (cross-arch from a single host); --skip-verify populates hashes without verifying. Both are also accepted via VSCODE_EXTENSION_WITH_SIGNATURE / VSCODE_EXTENSION_SKIP_VERIFY env vars, since nix-update/update.py pass the environment through but cannot forward CLI flags. Verification respects the ambient unfree policy and never forces allowUnfree.

Routine updates auto-detect and refresh an existing signatureHash without verifying, so automated updaters such as r-ryantm keep signatures current without pulling the unfree vsce-sign.

What changed

  • New verify-vsix-signature.sh shared verifier (single source of truth) wrapping vsce-sign verify with exit-code handling.
  • verify-vsix-signature-setup-hook.sh thin preUnpackHooks wrapper around the shared verifier.
  • vscode-utils.nix verifySignature (per-extension or global config.vscodeExtensions.verifySignature) gates the hook; signatureArchive is fetched from signatureHash; exports vsceSignExe for the update script.
  • vscode_extension_update.py --with-signature/--skip-verify (and env equivalents), per-platform update time verification, --force to re-fetch on unchanged versions, auto-detect/refresh of existing hashes without verifying.
  • vscode (generic.nix/vscode.nix) passthru.hasVsceSign.

Usage

Opt in per extension:

mktplcRef = {
  publisher = "example";
  name = "extension";
  version = "1.0.0";
  hash = "sha256-...";
  signatureHash = "sha256-...";  # pins the signature
};
# ...
verifySignature = true;          # verify at build time

Manage signatures with the update script:

# Add + verify a signature (needs unfree allowed for vsce-sign)
vscode-extension-update vscode-extensions.publisher.name --with-signature

# Through nix-update / passthru.updateScript (cannot forward flags):
VSCODE_EXTENSION_WITH_SIGNATURE=1 nix-update --use-update-script vscode-extensions.publisher.name

Verification in action

Passing build verifySignature = true:

Running phase: unpackPhase
Verifying VSIX signature...
Signature verification: PASSED

A VSIX that does not match its signature (the case
hash-pinning alone cannot catch) fails the build:

Verifying VSIX signature...
Signature verification: FAILED (exit code: 30)
ERROR: Package integrity check failed - contents don't match signature

Cross-arch update-time verification: a darwin-arm64 VSIX verifies
PASSED on an x86_64-linux host, so one machine can verify all
platforms.

Backwards compatibility

Fully backwards compatible. Extensions without signatureHash are
unchanged. Extensions with signatureHash but no verifySignature build
exactly as before (free), with the signature pinned and verifiable on
demand. No extension is forced to pull vsce-sign unless it (or the
user's config) opts in.


Things done

  • Built on platform(s):
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • Ran nixpkgs-review on this PR.
  • Tested basic functionality of all binary files, usually in ./result/bin/.
  • Fits CONTRIBUTING.md and other READMEs.

@cavebatsofware cavebatsofware changed the title vscode-extensions: add optional cryptographic signature verification vscode-extensions: add optional cryptographic signature verification (using MS cert) Jan 22, 2026
@cavebatsofware cavebatsofware changed the title vscode-extensions: add optional cryptographic signature verification (using MS cert) vscode-extensions: add optional Marketplace signature verification Jan 22, 2026
@nixpkgs-ci nixpkgs-ci Bot added 12.first-time contribution This PR is the author's first one; please be gentle! 10.rebuild-linux: 1-10 This PR causes between 1 and 10 packages to rebuild on Linux. 10.rebuild-darwin: 1-10 This PR causes between 1 and 10 packages to rebuild on Darwin. 10.rebuild-darwin: 1 This PR causes 1 package to rebuild on Darwin. 10.rebuild-linux: 1 This PR causes 1 package to rebuild on Linux. 9.needs: reviewer This PR currently has no reviewers requested and needs attention. 6.topic: vscode A free and versatile code editor that supports almost every major programming language. labels Jan 22, 2026
@cavebatsofware cavebatsofware force-pushed the vscode-signature-verification branch from 9eb82ec to a65411f Compare January 24, 2026 16:43
@cavebatsofware

Copy link
Copy Markdown
Contributor Author

Tagging a few committers that have recently reviewed VSCode related PRs in case anyone has time to take a look at this VSCode Marketplace Signature Verification support change.

@matteo-pacini , @NickCao , @xiaoxiangmoe , @TheMaxMur

@cybardev

cybardev commented Jan 29, 2026

Copy link
Copy Markdown
Contributor

Squash the formatting commit into the prior one. See: https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#commit-conventions

@cavebatsofware cavebatsofware force-pushed the vscode-signature-verification branch from a65411f to 4d68cf2 Compare January 29, 2026 20:48
@cavebatsofware

Copy link
Copy Markdown
Contributor Author

Squash the formatting commit into the prior one. See: https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#commit-conventions

@cybardev Fixed the commit history, thank you for taking a look.

Also added some brief documentation in the README for vscode-extensions so the feature is documented.

@cavebatsofware cavebatsofware force-pushed the vscode-signature-verification branch from 4d68cf2 to 1c6b76e Compare January 29, 2026 21:05
@cavebatsofware

Copy link
Copy Markdown
Contributor Author

@cybardev Also removed support for the signatureSha256 pattern after realizing in the code and documentation that the sha256 pattern is deprecated and it seems like continuing a deprecated pattern would have been undesirable.

@cavebatsofware

Copy link
Copy Markdown
Contributor Author

These action failures are unexpected and the errors look CI / workflow related rather than code related.

@nixpkgs-ci nixpkgs-ci Bot added the 2.status: merge conflict This PR has merge conflicts with the target branch label Mar 4, 2026
@nixpkgs-ci nixpkgs-ci Bot removed the 2.status: merge conflict This PR has merge conflicts with the target branch label Apr 10, 2026
@nixpkgs-ci nixpkgs-ci Bot requested a review from xiaoxiangmoe April 10, 2026 22:49
@nixpkgs-ci nixpkgs-ci Bot added 2.status: merge conflict This PR has merge conflicts with the target branch and removed 10.rebuild-linux: 1 This PR causes 1 package to rebuild on Linux. 9.needs: reviewer This PR currently has no reviewers requested and needs attention. labels Apr 10, 2026
@matteo-pacini

Copy link
Copy Markdown
Contributor

@cavebatsofware Sorry for the late reply, and great work.

Tried testing this on Darwin with the following hook patch:

diff --git a/pkgs/applications/editors/vscode/extensions/verify-vsix-signature-setup-hook.sh b/pkgs/applications/editors/vscode/extensions/verify-vsix-signature-setup-hook.sh
index dd38a4ccea69..f693c23b02f8 100644
--- a/pkgs/applications/editors/vscode/extensions/verify-vsix-signature-setup-hook.sh
+++ b/pkgs/applications/editors/vscode/extensions/verify-vsix-signature-setup-hook.sh
@@ -14,7 +14,14 @@ _verifyVsixSignaturePreUnpack() {
         return 0
     fi
 
-    local vsceSign="@vscode@/lib/vscode/resources/app/node_modules/@vscode/vsce-sign/bin/vsce-sign"
+    local vsceSign
+    if [[ -d "@vscode@/Applications" ]]; then
+        # macOS .app bundle layout
+        vsceSign="@vscode@/Applications/Visual Studio Code.app/Contents/Resources/app/node_modules/@vscode/vsce-sign/bin/vsce-sign"
+    else
+        # Linux layout
+        vsceSign="@vscode@/lib/vscode/resources/app/node_modules/@vscode/vsce-sign/bin/vsce-sign"
+    fi
 
     if [[ ! -x "$vsceSign" ]]; then
         echo "WARNING: vsce-sign not found at $vsceSign - signature verification skipped" >&2

The signature process works fine, but only on Darwin systems with disabled sandbox - relaxed and enabled would require more work.

@cavebatsofware

cavebatsofware commented Apr 13, 2026 via email

Copy link
Copy Markdown
Contributor Author

@nixpkgs-ci nixpkgs-ci Bot added 10.rebuild-linux: 101-500 This PR causes between 101 and 500 packages to rebuild on Linux. 10.rebuild-darwin: 101-500 This PR causes between 101 and 500 packages to rebuild on Darwin. and removed 10.rebuild-linux: 1-10 This PR causes between 1 and 10 packages to rebuild on Linux. 10.rebuild-darwin: 1-10 This PR causes between 1 and 10 packages to rebuild on Darwin. 10.rebuild-darwin: 1 This PR causes 1 package to rebuild on Darwin. labels Apr 15, 2026
@nixpkgs-ci nixpkgs-ci Bot removed the 12.first-time contribution This PR is the author's first one; please be gentle! label Apr 30, 2026
@sarahec

sarahec commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

@cavebatsoftware looking good! A couple of nits:

  1. Prefix the anthropic commits like this: vscode-extensions.anthropic.claude-code: some brilliant change. We use the package name as the prefix; you can use https://search.nixos.org/packages to verify a package name.
  2. You might do the same with vscode (in its own commit after adding the extensions) if that makes sense.

Here's the logic re: commits: "If this commit introduces a problem downstream, can I revert this one commit safely?" Also "is this commit self-contained and readable enough that someone can understand what I did?"

@sarahec

sarahec commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

The signature process works fine, but only on Darwin systems with disabled sandbox - relaxed and enabled would require more work.

FYI, Hydra builds all Darwin packages with the sandbox off. Having something work on a restricted sandbox is nice, but not required for Darwin. (nix itself doesn't build in a restricted sandbox on Darwin.)

@sarahec

sarahec commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

nixpkgs-review result

Generated using nixpkgs-review.

Command: nixpkgs-review pr 482821
Commit: 9e9ba11e0d125feae71c28b8c308d446af9917fa


x86_64-linux

✅ 14 packages built:
  • antigravity
  • antigravity-fhs
  • code-cursor
  • code-cursor-fhs
  • kiro
  • kiro-fhs
  • vscode
  • vscode-extension-update
  • vscode-extensions.anthropic.claude-code
  • vscode-fhs
  • vscode-with-extensions
  • vscodium
  • vscodium-fhs
  • windsurf

@sarahec

sarahec commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

nixpkgs-review result

Generated using nixpkgs-review.

Command: nixpkgs-review pr 482821
Commit: 9e9ba11e0d125feae71c28b8c308d446af9917fa


aarch64-darwin

❌ 1 package failed to build:
  • kiro
✅ 8 packages built:
  • antigravity
  • code-cursor
  • vscode
  • vscode-extension-update
  • vscode-extensions.anthropic.claude-code
  • vscode-with-extensions
  • vscodium
  • windsurf

Error logs: `aarch64-darwin`
kiro
Running phase: unpackPhase
@nix { "action": "setPhase", "phase": "unpackPhase" }
unpacking source archive /nix/store/10knw4zipvmgjashbvsb9x3kxvd4m8n2-kiro-ide-0.11.133-stable-darwin-arm64.dmg
do not know how to unpack source archive /nix/store/10knw4zipvmgjashbvsb9x3kxvd4m8n2-kiro-ide-0.11.133-stable-darwin-arm64.dmg

@sarahec

sarahec commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Don't worry about kiro not building on Darwin, this is a known issue.

@cavebatsofware

Copy link
Copy Markdown
Contributor Author

The signature process works fine, but only on Darwin systems with disabled sandbox - relaxed and enabled would require more work.

FYI, Hydra builds all Darwin packages with the sandbox off. Having something work on a restricted sandbox is nice, but not required for Darwin. (nix itself doesn't build in a restricted sandbox on Darwin.)

I did make changes that ensure that relaxed sandboxes work. Restricted sandboxes require an override:

(pkgs.vscode-extensions.publisher.extension.override { allowMissingVsceSign = true; })

@cavebatsofware cavebatsofware force-pushed the vscode-signature-verification branch from 9e9ba11 to 42e0aeb Compare April 30, 2026 20:01
@cavebatsofware

Copy link
Copy Markdown
Contributor Author

updated second commit message lead to "vscode-extensions.anthropic.claude-code:"

@nixpkgs-ci nixpkgs-ci Bot added the 2.status: merge conflict This PR has merge conflicts with the target branch label Apr 30, 2026
@cavebatsofware cavebatsofware force-pushed the vscode-signature-verification branch from 42e0aeb to 7a6e0b2 Compare May 1, 2026 04:14
@cavebatsofware

cavebatsofware commented May 1, 2026

Copy link
Copy Markdown
Contributor Author

re-based on master again to resolve conflicts (this happens when the auto-update scripts run, and will continue to happen until this can merge)

@cavebatsofware cavebatsofware force-pushed the vscode-signature-verification branch from 7a6e0b2 to 7bc6094 Compare May 1, 2026 04:16
@nixpkgs-ci nixpkgs-ci Bot removed the 2.status: merge conflict This PR has merge conflicts with the target branch label May 1, 2026
@cavebatsofware

cavebatsofware commented May 1, 2026

Copy link
Copy Markdown
Contributor Author

@sarahec I really appreciate you taking a look at this and drawing some attention.

@Zaczero I really appreciate your initial review when this wasn't getting any looks among many other things. If I had the clearance I would certainly help review and get things approved. I understand your frustration. We are in it together, you have support in your efforts, even when things are not moving as quickly as we would like.

This change means a-lot to me as it fixes a notable security issue for a very popular userspace application. An issue that in some environments would otherwise violate security guidelines, ie in this org users can use vscode but not if the installation method bypasses signature verification of installed extensions etc, and it is just something I like to have as piece of mind, which is why I developed this for my own use. I feel like this is a reasonable contribution to the security acceptance argument of nixos.

I really love and appreciate all the time contributors that work on this repo give. Everyone is doing so much and putting so much out there that it IS hard to keep up with but that is a great problem to have; and I think that it's a problem we'll solve together.

@Vuks69

Vuks69 commented May 1, 2026

Copy link
Copy Markdown
Contributor

I'm following this too, I just don't think I know enough about nix or vscode/extensions to actually review this. All I've done so far is package kiro, and even that still fails the darwin builds (which I have no idea how to fix, and can't test because I don't own a Mac).

@cavebatsofware

cavebatsofware commented May 1, 2026

Copy link
Copy Markdown
Contributor Author

I'm following this too, I just don't think I know enough about nix or vscode/extensions to actually review this. All I've done so far is package kiro, and even that still fails the darwin builds (which I have no idea how to fix, and can't test because I don't own a Mac).

@Vuks69 I have an M4 mac-mini that I could probably use to take a look at the kiro build issue, separately from this PR of course.

@MattSturgeon MattSturgeon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Docs should follow one-sentence-per-line

This makes reviews and suggestions much easier, since GitHub's review system is based on lines.
It also helps identifying long sentences at a glance.

Comment thread pkgs/applications/editors/vscode/extensions/README.md Outdated
Comment thread pkgs/applications/editors/vscode/extensions/README.md Outdated
Comment thread pkgs/applications/editors/vscode/extensions/README.md Outdated
Comment thread pkgs/applications/editors/vscode/extensions/README.md Outdated
@cavebatsofware

Copy link
Copy Markdown
Contributor Author

Docs should follow one-sentence-per-line

This makes reviews and suggestions much easier, since GitHub's review system is based on lines.
It also helps identifying long sentences at a glance.

@MattSturgeon I have gone through my readme changes and updated them as recommended to adhere to the documentation standards. Thanks for looking this over.

@MattSturgeon MattSturgeon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the nix side of the PR. SGTM overall, some design questions and nitpicks. I didn't look at the python impl in any detail.

Comment thread pkgs/applications/editors/vscode/generic.nix Outdated
Comment thread pkgs/applications/editors/vscode/extensions/mktplcExtRefToFetchArgs.nix Outdated
Comment thread pkgs/applications/editors/vscode/extensions/README.md Outdated
Comment on lines +21 to +23
Signature verification runs only when the `vscode` package exposes `passthru.hasVsceSign = true`.
Microsoft's build does; `vscodium` does not because it ships without the `vsce-sign` binary).
Users who overlay `vscode = vscodium` or other variant without `vsce-sign` get verification automatically skipped.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Q: does this need to be flagged via a passthru attr, or is it possible to probe for vsce-sign support during the build?

E.g. we could do something like

if command -v vsce-sign > /dev/null; then
  # TODO: validate signature
else
  echo "Warning: vsce-sign is not available" >&2
fi

(EDIT: reading down, this is essentially your [[ ! -x "$vsceSign" ]] branch).

I guess it's still useful to have an attr define whether the else branch here should exit, error, warn, or log quietly?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is part of the vscode/other package it doesn't need passthrough in the extension since it is used elsewhere and was already available. Although that may not fully answer your question.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I was essentially asking whether you could do an automatic built-time check for "is vsce-sign available" without having the eval-time attribute hasVsceSign.

The advantage of the attribute is that you can error if the attribute is true but the exe is not available.

Comment thread pkgs/applications/editors/vscode/extensions/vscode-utils.nix Outdated
Comment thread pkgs/applications/editors/vscode/extensions/vscode-utils.nix Outdated
Comment on lines +138 to +155
# Fetch signature archive for cryptographic verification
fetchSignatureFromVscodeMarketplace =
mktplcExtRef:
let
inherit (mktplcExtRef) publisher name version;
arch = mktplcExtRef.arch or "";
archurl = if arch == "" then "" else "?targetPlatform=${arch}";
baseUrl = "https://${publisher}.gallery.vsassets.io/_apis/public/gallery/publisher/${publisher}/extension/${name}/${version}/assetbyname";
signatureHash = mktplcExtRef.signatureHash or "";
in
if signatureHash != "" then
fetchurl {
url = "${baseUrl}/Microsoft.VisualStudio.Services.VsixSignature${archurl}";
name = "${publisher}-${name}.sigzip";
hash = signatureHash;
}
else
null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This seems extremely similar to the existing mktplcExtRefToFetchArgs impl. Maybe a future refactor could share common code and return either a extension or signature depending on inputs? Or even return both FODs in a tuple attrset. Or return the extension FOD with its signature FOD as a passthru.

I'd personally consolidate something like:

{
  publisher,
  name,
  version,
  arch ? "",
  sha256 ? "",
  hash ? "",
  signatureHash ? "",
}@mktplcExtRef:
let
  archurl = (if arch == "" then "" else "?targetPlatform=${arch}");
  baseUrl = "https://${publisher}.gallery.vsassets.io/_apis/public/gallery/publisher/${publisher}/extension/${name}/${version}/assetbyname";
in
fetchurl {
  # VSIX package fetch arguments (for fetchurl)
  url = "${baseUrl}/Microsoft.VisualStudio.Services.VSIXPackage${archurl}";
  inherit sha256 hash;
  # The `*.vsix` file is in the end a simple zip file. Force it using .vsix extension
  # so that existing `unpackVsixSetupHook` hooks takes care of the unpacking.
  name = "${publisher}-${name}.vsix";

  passthru = {
    ${if signatureHash == "" then null else "signature"} = fetchurl {
      url = "${baseUrl}/Microsoft.VisualStudio.Services.VsixSignature${archurl}";
      name = "${publisher}-${name}.sigzip";
      hash = signatureHash;
    };
  };
}

Removing the overly-abstracted mktplcExtRefToFetchArgs.nix file unless it is actually used by several places.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This could probably be improved. I'm fairly new with nix so I probably didn't implement this as cleanly as I probably should have. I will attempt to clean this up some when I address your other findings.

@cavebatsofware cavebatsofware May 3, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@MattSturgeon I made a small consolidation around the URL but avoided a larger refactor to avoid impacting vscodeExts2nix.nix which also uses this function. Switching the file to return a fetchurl derivation would require updating vscodeExts2nix.nix's call site. Which should probably go in another PR that is more focused on refactoring these patterns. Although I'm not opposed to doing that here if that is the general consensus.

For the dedup itself I extracted a small mktplcAssetBaseUrl helper in vscode-utils.nix:

mktplcAssetBaseUrl =
  { publisher, name, version, ... }:
  "https://${publisher}.gallery.vsassets.io/_apis/public/gallery/publisher/${publisher}/extension/${name}/${version}/assetbyname";

fetchSignatureFromVscodeMarketplace now uses it instead of inlining the literal. The duplication shrinks without moving the public shape of mktplcExtRefToFetchArgs.nix

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ideally we'd consolidate these two functions, but I agree it is beyond the scope of this PR.

@MattSturgeon MattSturgeon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry for the delay. I was waiting for 26.05 branch off; master is now on 26.11!

I had another quick skim through and didn't see any major issues. I left some minor comments and there are merge conflicts that need rebasing, though.

Ideally, someone more familiar with the vscode infrastructure would take a look (do you know anyone we can ping?). But otherwise, I'm happy to merge once you're confident.

"eval",
"--raw",
"-f",
".",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I believe this depends on the current working directory. Ideally we'd use a fixed path, relative to the current Path(__file__)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is moved existing code, so not necessarily this PR's job to fix.

"nix-command",
"eval",
"--raw",
"-f",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

My personal preference is for persistent scripts to avoid "short" flags in favour of the more descriptive long flags

Suggested change
"-f",
"--file",

Fetches the signature hash for an extension version from the VS Code Marketplace.
Returns the SRI hash or None if fetch fails.
"""
platform_suffix = f"?targetPlatform={target_platform}" if target_platform else ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is fragile if it is possible that a target_platform string could ever contain URI syntax chars. Unlikely, but best practice would be to build the query parameter string using a URI library, or at least URI-escape the value.

Not blocking here.

@cavebatsofware

cavebatsofware commented May 24, 2026

Copy link
Copy Markdown
Contributor Author

Sorry for the delay. I was waiting for 26.05 branch off; master is now on 26.11!

I had another quick skim through and didn't see any major issues. I left some minor comments and there are merge conflicts that need rebasing, though.

Ideally, someone more familiar with the vscode infrastructure would take a look (do you know anyone we can ping?). But otherwise, I'm happy to merge once you're confident.

Ill take a look at (and correct) all of the issues you identified in your last review. Good finds, I agree with pretty much all of your comments and appreciate the feedback.

As far as "anyone that I can ping that is familiar with this package infrastructure" well, I don't know of anyone that actually knows how this works other than myself. I know there are maintainers etc but I personally have not seen anyone (that is a maintainer) have any interest in this PR.

The merge conflicts are very easy to solve obviously and only need a vscode update script run to fix. I have been doing that regularly as the force-push records reflect but I am not going to do that any more until someone is actually going to merge this.

@emilazy

emilazy commented May 25, 2026

Copy link
Copy Markdown
Member

(I am not very knowledgeable about VS Code or our extensions infrastructure.)

Do I understand correctly that the keys are bundled in vsce-sign as included with VS Code?

In that case, this only provides any security guarantees over the current “downloading from Microsoft servers” if we can gain trust in the VS Code package itself beyond “downloading from Microsoft servers”, right? Otherwise the security reduces to the same TLS but with extra steps (admittedly only on every VS Code update rather than every extension update, but it seems like fundamentally the same MITM task to me).

So we’d need to verify updates to the VS Code package itself somehow for this to have a benefit. Is that possible? Are the VS Code packages themselves signed in a way that we could verify (e.g. with the previous version’s vsce-sign, or by checking in a long‐term key into Nixpkgs)?

@emilazy

emilazy commented May 25, 2026

Copy link
Copy Markdown
Member

After doing some more research:

It seems that the vsce-sign tool is proprietary and not even source‐available, so checking how it verifies signatures is non‐trivial. I couldn’t find any public documentation on how it works, but I did find that the binary embeds 1266(!) unique root CA certificates – it looks like it might be Microsoft’s entire root CA store, or at least all the root code signing certificates. If it’s simply verifying against any of those it seems dubious whether there’s any benefit over the WebPKI TLS checking we already get, even with verification of VS Code updates themselves.

Using the vsce-sign tool from within the extension builds might not make sense regardless: the FOD hash already pins the exact contents of the extension. Verifying during update should be sufficient: even a malicious PR injecting a fake FOD hash would fail unless the CDN was compromised at the exact moment Hydra downloads it, and a malicious PR sadly has easier ways to do FOD injection attacks. Using it would also mean that VS Code extensions will not build without unfree packages being enabled, and therefore could not be built on Hydra (I don’t know whether they currently are or not, though); that could also be an issue if the same extension packages are shared with VSCodium (which, again, I don’t know if they are).

@cavebatsofware

cavebatsofware commented May 29, 2026

Copy link
Copy Markdown
Contributor Author

After doing some more research:

It seems that the vsce-sign tool is proprietary and not even source‐available, so checking how it verifies signatures is non‐trivial. I couldn’t find any public documentation on how it works, but I did find that the binary embeds 1266(!) unique root CA certificates – it looks like it might be Microsoft’s entire root CA store, or at least all the root code signing certificates. If it’s simply verifying against any of those it seems dubious whether there’s any benefit over the WebPKI TLS checking we already get, even with verification of VS Code updates themselves.

Using the vsce-sign tool from within the extension builds might not make sense regardless: the FOD hash already pins the exact contents of the extension. Verifying during update should be sufficient: even a malicious PR injecting a fake FOD hash would fail unless the CDN was compromised at the exact moment Hydra downloads it, and a malicious PR sadly has easier ways to do FOD injection attacks. Using it would also mean that VS Code extensions will not build without unfree packages being enabled, and therefore could not be built on Hydra (I don’t know whether they currently are or not, though); that could also be an issue if the same extension packages are shared with VSCodium (which, again, I don’t know if they are).

Without getting into exactly how vsce-sign actually works, which would be guesswork on my part, beyond what has been publicly documented, I will try to address the other concerns in these last comments.

Regarding unfree concerns etc; vscode itself is "unfree" and any extension signing support is only used when vsce-sign is available. In other words, this has provisions to allow vscodium to use extensions normally as it does already.

Regarding speculation as weather or not this provides any real additional security; I believe that this at the very least lets the vscode application verify signatures as the application normally would when installing extensions from the marketplace directly. Nixos has a hash element for the extension itself but the signature is the cannonical way to validate that the Marketplace has actually signed the package. This is different than just checking the packages marketplace metadata and getting a json result that claims that the package was signed. The latter is a filter the former is an actual signature by microsoft.

I didn't validate how Microsoft has designed vsce-sign and I actually don't think that should be a concern of this PR. This PR just allows VSCode extension builds on Nixos to actually validate the extensions the same way the vscode application does. I don't think the package builder should skip things like this just because we have another hash that we think is as good as the built in application extension verification. If Nixos is going to skip this verification it seems like something that should be documented as intentional, so users are aware that this check is being bypassed.

@sarahec

sarahec commented May 29, 2026

Copy link
Copy Markdown
Contributor

Regarding unfree concerns etc; vscode itself is "unfree" and any extension signing support is only used when vsce-sign is available. In other words, this has provisions to allow vscodium to use extensions normally as it does already.

You seem to misunderstand the concern. vscode my be unfree, but vscodium isn't. Many of the extensions are FOSS. Requiring vsce-sign in the build process prevents all extensions from being built on our build infrastructure (forcing local rebuilds for all). That has a giant impact on all vscodium-built editors and their users.

Nixos has a hash element for the extension itself but the signature is the cannonical way to validate that the Marketplace has actually signed the package.

Does the Marketplace host unsigned packages? We download directly from Microsoft's servers.

I didn't validate how Microsoft has designed vsce-sign and I actually don't think that should be a concern of this PR.

It makes a difference if the signing tool has to be used in the build. Building a signed package for Nix invalidates the old signature, so we have to re-sign it. (Emily, who posted the comment above, is one of our Darwin leads.)

This PR just allows VSCode extension builds on Nixos to actually validate the extensions the same way the vscode application does.

Maybe I'm missing something. Can you share a proof of concept where the tools built with this patch correctly validate nix-built extensions? That would set everyone's mind at ease.

I don't think the package builder should skip things like this just because we have another hash that we think is as good as the built in application extension verification.

Can you show an example where the Nix validation passes on an incorrect extension (from Marketplace) but the signature check catches it?

@cavebatsofware

cavebatsofware commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

@sarahec I have read your and @emilazy comments and questions a bit more closely, done some more detailed research on the process and testing with vscodium which confirmed that the concerns related to Hydra and vscodium user (depending on how they are configuring it) impacts are definitely valid. There is somewhat of an in-between option for doing this in the update script on the maintainer box, or on the user box. It seems like this should run on the maintainer box at update time and if a vscode user wants to run this verification locally (extension build) they can do so.

EDIT: Vscodium users could also, if they wanted to, use the vscode signature verification IF they want to do it. They WOULD have to make the compromise of allowing unfree for that build process, but would not have to fully install vscode (aside from its build requirement) on their system to do so. Just noting that it is possible for vscodium users to use this but it comes with some tradeoffs for them if they are trying to run a fully FOSS install END EDIT.

FOSS packages would then still work just fine with signatureHash set.

Regarding some of the other questions:

Does the Marketplace host unsigned packages? We download directly from Microsoft's servers.


Yes, Marketplace does host unsigned packages. This can happen when a packages is in per-release or fails validation or virus scan and possible other reasons that I am not aware of. I created a gate here #522773 to prevent this on update but that doesn't actually validate the signature and depends on the json response from the MS server to be valid and not change in structure or meaning.

Can you show an example where the Nix validation passes on an incorrect extension (from Marketplace) but the signature check catches it?


Same as the above reference: #522773
Basically TLDR; what happened there is the vscode-extensions.anthropic.claude-code package was updated by the update script and merged into master but there was no valid signature and the json response showed that the version of that extension on master at that time had failed validation and was not listed on the Marketplace web interface or VSCode marketplace search.

Updates:

Instead of running this verification automatically I have refactored it so it is manual and can be done at update time by the maintainer, or a dedicated CI that has verification and unfree support enabled. The existing CI tools should remain fully functional for FOSS extensions that have signatureHash enabled. Users or package maintainers (of unfree or FOSS packages) can enable signatureHash without forcing verification by letting users opt-in with config.vscodeExtensions.verifySignature and/or unfree packages can set verification on by default individually.

The improvement here is now the maintainer can easily validate all the different platform binaries signature at the same time without having to actually be on those platforms. This is just because running vsce-sign in various sig/archs works independent of build env arch. So all target platforms are verified in a single update as expected.

…ation

Adds opt-in cryptographic signature verification for VS Code Marketplace
extensions. Setting `signatureHash` on `mktplcRef` pins the extension's
signature archive (a fixed-output derivation) alongside the VSIX hash.

Build-time verification is opt-in via `verifySignature`, enabled per
extension or globally with `vscodeExtensions.verifySignature = true` in
nixpkgs config. It checks the VSIX against its signature with Microsoft's
`vsce-sign` (bundled with VS Code) in `preUnpackHooks`. Because
`vsce-sign` is unfree, this is off by default so signed extensions stay
free-buildable on Hydra and usable with vscodium; it is also skipped
when the `vscode` package does not expose `passthru.hasVsceSign = true`.

`vscode_extension_update.py` verifies signatures at update time, sharing
one verifier (`verify-vsix-signature.sh`) with the build hook so both
honour the same `vsce-sign` invocation and exit codes. `--with-signature`
fetches, adds, and verifies `signatureHash` on every platform, and
`--skip-verify` populates hashes without verifying; both are also
accepted via `VSCODE_EXTENSION_WITH_SIGNATURE` and
`VSCODE_EXTENSION_SKIP_VERIFY`, since update runners (nix-update,
update.py) pass the environment through but cannot forward CLI flags.
Verification respects the ambient unfree policy.

Routine updates auto-detect and refresh an existing `signatureHash`
without verifying, so automated updaters such as r-ryantm keep
signatures current without pulling the unfree `vsce-sign`.
Updates hash values to reflect new version 2.1.170 of the Anthropic Claude Code VSCode extension. Adds signature hash for verification.

Introduces signatureHash fields across all platform architectures to enable cryptographic verification of downloaded extensions, ensuring extension integrity and authenticity from the marketplace
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

2.status: merge conflict This PR has merge conflicts with the target branch 6.topic: vscode A free and versatile code editor that supports almost every major programming language. 10.rebuild-darwin: 1-10 This PR causes between 1 and 10 packages to rebuild on Darwin. 10.rebuild-linux: 1-10 This PR causes between 1 and 10 packages to rebuild on Linux. 12.approvals: 1 This PR was reviewed and approved by one person.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants