Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nodejs/trust #828

Merged
merged 1 commit into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions modules/dream2nix/WIP-nodejs-builder-v3/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
# TODO: Verify this is reasonable default;
source = builtins.dirOf cfg.packageLockFile;
makeNodeModules = ./build-node-modules.mjs;
installTrusted = ./install-trusted-modules.mjs;
in
if path == ""
then let
Expand All @@ -144,14 +145,17 @@
name = entry.name + "-dist";
src = source;
buildInputs = with config.deps; [nodejs jq];
env = {
TRUSTED = builtins.toJSON cfg.trustedDeps;
};
configurePhase = ''
cp -r ${prepared-dev}/node_modules node_modules
# TODO: run installScripts of trusted dependencies

node ${installTrusted}
'';
buildPhase = ''
echo "BUILDING... $name"
if [ -n "$runBuild" ] && [ "$(jq '.scripts.build' ./package.json)" != "null" ]; then
if [ "$(jq '.scripts.build' ./package.json)" != "null" ]; then
echo "npm run build"
npm run build
fi;
'';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import fs from "fs";
import { abort } from "process";
import { execSync } from "child_process";

const { TRUSTED } = process.env;

/**@type {string[]} */
const trusted = JSON.parse(TRUSTED);
console.log({ trusted });

/**
* @type {fs.Dirent[]}*/
const packageJsonFiles = fs
.readdirSync(
"./node_modules",
{ recursive: true, withFileTypes: true },
(error) => {
console.error({ error });
abort();
}
)
.filter(
(/**@type {fs.Dirent}*/ entry) =>
entry.isFile() &&
entry.name === "package.json" &&
// Reduce the number of packageJson files that we need to parse.
// Note: The list may still have some false positives. They will be skipped later
trusted.some((trustedName) => entry.path.endsWith(trustedName))
);

// If a dependency is trusted
// Run the following scripts if present
//
// preinstall
// install
// postinstall
// prepublish
// preprepare
// prepare
// postprepare
//
// The lifecycle scripts run only after node_modules are completely initialized with ALL modules
//
// Lifecycle scripts can execute arbitrary code.
// They often violate isolation between packages which makes them potentially insecure.

const lifecycleScripts = [
"preinstall",
"install",
"postinstall",
"prepublish",
"preprepare",
"prepare",
"postprepare",
];

packageJsonFiles.forEach((pjs) => {
const content = fs.readFileSync(`${pjs.path}/${pjs.name}`);

/**@type {{scripts?: { [k: string]: string}, name: string }}*/
const info = JSON.parse(content.toString());
const { scripts, name: packageName } = info;

// Skip false positives
if (trusted.includes(packageName)) {
const run =
scripts &&
Object.entries(scripts).filter(([k]) =>
lifecycleScripts.some((s) => s === k)
);
if (run) {
run.forEach(([scriptName, command]) => {
console.log(`${packageName} - ${scriptName}: ${command}`);
try {
const result = execSync(command, { cwd: pjs.path });
console.log(result.toString());
} catch (err) {
console.error(
`Could not execute lifecycle script '${scriptName}' for ${packageName} (See Trusted Dependencies)`
);
console.error(err);
}
});
} else {
console.warn(
`Trusted package ${packageName} doesnt have any lifecycle scripts. This entry does not have any affect.`
);
}
}
});
39 changes: 39 additions & 0 deletions modules/dream2nix/WIP-nodejs-builder-v3/interface.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,50 @@ in {
The package-lock.json file to use.
'';
};

packageLock = {
type = t.attrs;
description = "The content of the package-lock.json";
};

trustedDeps = {
type = t.listOf (t.str);
default = [];
example = ["@babel/core"];
description = ''
A list of trusted dependencies.

If a dependency is trusted.
Run the following scripts in order if present:

> All versions of a dependency are trusted if there are multiple versions.

preinstall
install
postinstall
prepublish
preprepare
prepare
postprepare

The lifecycle scripts run only after node_modules are completely initialized with ALL dependencies.
Lifecycle scripts can execute arbitrary code. Which makes them potentially insecure.
They often violate isolation between packages. Which makes them potentially insecure.

*TODO*:

Trust all dependencies:

trustedDeps [ "*" ]

Trust all dependencies starting with "@org"

trustedDeps [ "@org/*" ]

which is usefull if you want to add all dependendencies within an organization.
'';
};

inherit
(import ./types.nix {
inherit
Expand Down