Skip to content

Commit e684aa7

Browse files
davidnbrclaude
andcommitted
refactor: harden security policy and DRY version name helpers
- Scope allowInsecurePredicate to nodejs.* and openssl.* pnames only; the previous (_: true) blanket-allowed every insecure package across all pinned nixpkgs imports - Remove allowUnfree = true (Node/yarn/pnpm are all free-licensed) - Extract sanitizeVersion helper to replace three identical inline builtins.replaceStrings calls - Derive defaultVersion from versionsData.default or "22.22" with a clear throw if the key is missing; eliminates the silent attribute- not-found failure on the magic string - Apply canOverrideNodejs guard to the pnpm null-fallback branch for consistency with the main pnpm and yarn paths - Consolidate getNixpkgs to use getVersionInfo internally, removing the redundant hasVersion check and duplicate throw message Constraint: allowInsecurePredicate must permit old Node (14.x/16.x) and their bundled OpenSSL — name-prefix match covers all variants Rejected: allow by exact pname list | maintenance burden as nixpkgs renames attrs Confidence: high Scope-risk: narrow Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent cc23c87 commit e684aa7

1 file changed

Lines changed: 53 additions & 25 deletions

File tree

flake.nix

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
versionsData = builtins.fromJSON (builtins.readFile ./versions.json);
1919
versionMap = versionsData.versions;
2020

21+
# The default version shown as `packages.<system>.default`.
22+
# Set "default" in versions.json to override; falls back to "22.22".
23+
defaultVersion = versionsData.default or "22.22";
24+
25+
# Converts "22.22" → "22_22" for use as Nix attribute names.
26+
sanitizeVersion = builtins.replaceStrings [ "." ] [ "_" ];
27+
2128
systems = [
2229
"x86_64-linux"
2330
"aarch64-linux"
@@ -43,24 +50,27 @@
4350

4451
# NOTE: This uses an impure fetchTarball, which is often discouraged
4552
# in top-level packages, but is sometimes accepted for version pins.
53+
#
54+
# allowInsecurePredicate is scoped to Node.js and OpenSSL packages only.
55+
# Old Node.js versions (14.x, 16.x) bundle EOL OpenSSL and are flagged
56+
# insecure by nixpkgs; we need to allow them intentionally here.
4657
getNixpkgs =
4758
{ system, version }:
48-
if hasVersion { inherit version; } then
49-
let
50-
info = versionMap.${version};
51-
in
52-
import
53-
(builtins.fetchTarball {
54-
url = "https://github.com/NixOS/nixpkgs/archive/${info.rev}.tar.gz";
55-
sha256 = info.sha256;
56-
})
57-
{
58-
inherit system;
59-
config.allowUnfree = true;
60-
config.allowInsecurePredicate = (_: true);
61-
}
62-
else
63-
throw "Node.js version ${version} not found in versionMap";
59+
let
60+
info = getVersionInfo version;
61+
in
62+
import
63+
(builtins.fetchTarball {
64+
url = "https://github.com/NixOS/nixpkgs/archive/${info.rev}.tar.gz";
65+
sha256 = info.sha256;
66+
})
67+
{
68+
inherit system;
69+
config.allowInsecurePredicate =
70+
pkg:
71+
builtins.match "nodejs.*" pkg.pname != null
72+
|| builtins.match "openssl.*" pkg.pname != null;
73+
};
6474
};
6575

6676
packagesForSystem =
@@ -83,11 +93,11 @@
8393

8494
aliases = nixpkgs.lib.mapAttrs' (
8595
version: pkg:
86-
nixpkgs.lib.nameValuePair ("nodejs_" + (builtins.replaceStrings [ "." ] [ "_" ] version)) pkg
96+
nixpkgs.lib.nameValuePair ("nodejs_" + sanitizeVersion version) pkg
8797
) basePackages;
8898

8999
# Create yarn packages bundled with the specific node version.
90-
# Uses the version-pinned nixpkgs to ensure yarn/node compatibility
100+
# Uses the version-pinned nixpkgs to ensure yarn/node compatibility.
91101
# Falls back to latest nixpkgs if yarn is absent from the pinned rev.
92102
# yarn.override is a callable attrset (same structure as pnpm); __functionArgs
93103
# is checked before calling to guard against future packaging changes.
@@ -102,7 +112,7 @@
102112
&& yarnOverride ? __functionArgs
103113
&& builtins.hasAttr "nodejs" yarnOverride.__functionArgs;
104114
in
105-
nixpkgs.lib.nameValuePair ("yarn_" + (builtins.replaceStrings [ "." ] [ "_" ] version)) (
115+
nixpkgs.lib.nameValuePair ("yarn_" + sanitizeVersion version) (
106116
pkgs.symlinkJoin {
107117
name = "yarn-" + version;
108118
paths = [
@@ -119,11 +129,12 @@
119129
#
120130
# Older nixpkgs keep pnpm under nodePackages.pnpm; newer ones promote it to
121131
# pkgs.pnpm with a callable-attrset override that accepts a nodejs argument.
122-
# When that override is available we thread
123-
# our exact Node derivation through it; otherwise we use pnpm as-is.
132+
# When that override is available we thread our exact Node derivation through
133+
# it; otherwise we use pnpm as-is.
124134
#
125135
# Falls back to latest nixpkgs pnpm only when the pinned rev has no pnpm at
126-
# all (rare, but guards against evaluation errors).
136+
# all (rare, but guards against evaluation errors). The fallback also applies
137+
# the same __functionArgs guard for consistency.
127138
pnpmPackages = nixpkgs.lib.mapAttrs' (
128139
version: pkg:
129140
let
@@ -143,15 +154,27 @@
143154

144155
pnpmPkg =
145156
if builtins.isNull pinnedPnpm then
146-
pkgs.pnpm.override { nodejs = pkg; }
157+
# Pinned rev has no pnpm at all; fall back to unstable nixpkgs.
158+
# Apply the same __functionArgs guard — pkgs is always modern but
159+
# defensive coding avoids future breakage.
160+
let
161+
fallbackOverride = pkgs.pnpm.override or null;
162+
canOverrideFallback =
163+
builtins.isAttrs fallbackOverride
164+
&& fallbackOverride ? __functionArgs
165+
&& builtins.hasAttr "nodejs" fallbackOverride.__functionArgs;
166+
in
167+
if canOverrideFallback then fallbackOverride { nodejs = pkg; } else pkgs.pnpm
147168
else if canOverrideNodejs then
148169
pnpmOverride { nodejs = pkg; }
149170
else
150171
pinnedPnpm;
151172
in
152-
nixpkgs.lib.nameValuePair ("pnpm_" + (builtins.replaceStrings [ "." ] [ "_" ] version)) (
173+
nixpkgs.lib.nameValuePair ("pnpm_" + sanitizeVersion version) (
153174
pkgs.symlinkJoin {
154175
name = "pnpm-" + version;
176+
# pkg first: symlinkJoin uses lndir which skips existing symlinks,
177+
# so the first path wins on conflict. pkg must win for node/npm/npx.
155178
paths = [
156179
pkg
157180
pnpmPkg
@@ -168,7 +191,12 @@
168191
let
169192
allPkgs = packagesForSystem system;
170193
in
171-
allPkgs // { default = allPkgs."22.22"; }
194+
allPkgs
195+
// {
196+
default =
197+
allPkgs.${defaultVersion}
198+
or (throw "Default Node.js version '${defaultVersion}' not found in versions.json");
199+
}
172200
);
173201

174202
overlays.default =

0 commit comments

Comments
 (0)