-
Notifications
You must be signed in to change notification settings - Fork 4.3k
apparmor profile / ELF wrapper. Limit supply-chain attacks #9193
Description
Is there an existing issue for this?
- I have searched the existing issues
This issue exists in the latest npm version
- I am using the latest npm
Current Behavior
Short version of pypa/pip#13869 the reiterated supply-attacks because most people use a powerful dependencies manager without any sandboxing => access to the whole filesystem by lack of a proper/well-defined permission policy.
npm install
Expected Behavior
Feel safe that your most private files won't get exfiltrated.
Steps To Reproduce
Basically: npm authors legitimately want a powerful software can do many fancy things on the system (bindings compilation => python, gcc, node-gyp & tons of things. publish/git/ssh/run testsuite....)
But developers (now?) legitimately understand they need their dependency-manager to not break open their $HOME and avoid running untrusted code (like everyone care when it comes to email attachments or USB pendrives)
Solution: sandbox. Candidate: apparmor.
(Sandboxing a interpreter running its own (interpreted) dependency-manager is bound to fail (interpreter is generic, dependencies manager is not).
The dependency-manager should rather be wrapped by an ELF whose access can be wisely limited): One wrapper for npm install and another for npm run.
Imperfect sample provided:
abi <abi/4.0>,
include <tunables/global>
# example (to set in /etc/apparmor.d/tunables/home.d/)
# configure the projects directories `npm` is allowed to operated on
# @{js_project_dirs} = @{HOME}/js/{project1,project2}
# add `node_modules/*` directories (some code-editors, MCP, ...)
@{node_modules} = {@{HOME}/.npm/_npx/*/node_modules,@{HOME}/.emacs/cache/lsp/**/lib/node_modules}
profile npm /usr/{local/,}bin/npm {
# npm link
@{HOME}/.npm-prefix{,/**} rwkl,
# npm publish
@{HOME}/{.npmrc,.yarnrc} rw,
# caches & config
@{HOME}/{.cache,.local/share}/pnpm/{,**} rwkl,
@{HOME}/{.config,.local/state}/pnpm/* rwkl,
@{HOME}/{.npm,.yarn,.cache/yarn}/{,**} rwl,
# cache and other JS package manager configurations. TODO: npx
@{HOME}/.npm/_npx/**/cli.js ix,
# the ELF wrapper
/usr/bin/npm rix,
# it's node's libraries
/usr/lib/node_modules/** rwkl,
# project files npm needs ro/rw access to (to be restricted / split according to install vs run)
@{HOME}/@{js_project_dirs}/{pnpm-lock.yaml,yarn.lock,package.json,package-lock.json} rwk,
# note the `npm run <...>` (eg: `npm run test` and all the jest testsuite)
@{HOME}/@{js_project_dirs}/{webpack.config.js,esbuild.config.mjs,.npmrc,.browserslistrc,.stylelintrc,.eslintignore,pnpm-workspace.yaml,jest.config.json,*_spec.js,tsconfig.json,tailwind.config.js,vite.config.mts,next.config.ts,next-env.d.ts} r,
# @TODO: that's were different binaries are needed: One for `npm install` and another for `npm run` because these
# are two really different software use-case and permissions policies.
@{HOME}/@{js_project_dirs}/{tests,fixtures,specs,.next,node_modules,.parcel-cache,parcel-bundle-reports}/{,**} rwkl,
@{HOME}/@{js_project_dirs}/{**/,}{var,dist,build,public*,}{,/**} rwk,
@{HOME}/@{js_project_dirs}/{logs,dist,build}{,/**} rwkl
# want to be careful/audit which subprocess a `npm install <package>` leads to:
@{node_modules}/@mapbox/node-pre-gyp/bin/node-pre-gyp rix,
@{node_modules}/@modelcontextprotocol/server-filesystem/dist/index.js rix,
@{node_modules}/@protobuf-ts/protoc/protoc.js rix,
@{node_modules}/@swc/core-linux-x64-gnu/swc.linux-x64-gnu.node rm,
@{node_modules}/bufferutil/prebuilds/linux-x64/node.napi.node rm,
@{node_modules}/create-vite/index.js rix,
@{node_modules}/http-server/bin/http-server rix,
@{node_modules}/next/dist/bin/next ix,
@{node_modules}/node-gyp*/build-test.js rix,
@{node_modules}/npm/bin/npx-cli.js ixr,
@{node_modules}/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin/node-gyp ixr,
@{node_modules}/react-native-decompiler/out/main.js rix,
@{node_modules}/rebrowser-patches/scripts/patcher.js rix,
@{node_modules}/sass-embedded-linux-x64/dart-sass/src/*.snapshot rm,
@{node_modules}/sass-embedded-linux-x64/dart-sass/src/dart ix,
@{node_modules}/skia-canvas/lib/{v6/,}{skia,index}.node rm,
@{node_modules}/sqlite3/build/Release/node_sqlite3.node rm,
@{node_modules}/typescript-language-server/lib/cli.mjs r,
@{node_modules}/typescript/bin/tsc ix,
@{node_modules}/utf-8-validate/prebuilds/linux-x64/node.napi.node rm,
@{node_modules}/wrangler/node_modules/esbuild/bin/esbuild rPx -> esbuild,
@{node_modules}/{@parcel,@rollup,@next,@img,@lmdb,@tailwindcss,@msgpackr-extract,lightningcss*}/{**/,}*.node rmix,
@{node_modules}/{msgpackr-extract,parcel,prebuild-install,node-gyp*}/{,lib/}bin.js rix,
@{node_modules}/{vite/,wrangler/,}node_modules/@esbuild/{linux-x64/,}bin/esbuild ix,
@{node_modules}/{wrangler/node_modules/,}@cloudflare/workerd-linux-64/bin/workerd ix,
@{node_modules}/{wrangler/node_modules/,}workerd/bin/workerd ix,
@{node_modules}/{{vitepress,vite}/node_modules/,tsx/node_modules/,}esbuild/bin/esbuild rPx -> esbuild,
# otherwise open up the permissions
@{node_modules}/**/binding.node m,
@{node_modules}/**/{hooks,interactivity}_tmp_*/{,**} lm,
@{node_modules}/**/bin/*.{mjs,js} ix,
@{node_modules}/**/{cli,index}.{mjs,js} ix,
}(but a deeper profiling/listing would be best done by npm experts)
The above looks a bit like a "manifest" for a plugin/extension/sandbox policy? Yes. It's definitely what we would expect in order to "overview" comprehensively (and allow/prevent resources access) at the package-level, dependency-tree level, machine level, package repository level.
Refer to the above pip issue for what a 40 lines ELF npm wrapper could look like.
There are more, notably about denying specific sensitive known file paths
I wish the first reports/PR to sandboxed PHP/JS/Python dependency/package-managers would have happened in the 2012-2015 era.
Environment
- OS Name: Linux flavors