Skip to content

apparmor profile / ELF wrapper. Limit supply-chain attacks #9193

@drzraf

Description

@drzraf

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

References

pnpm equivalent discussion

Metadata

Metadata

Assignees

No one assigned

    Labels

    Bugthing that needs fixingNeeds Triageneeds review for next steps

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions