What if your package manager started with “no”?

Share

npm install should not be a trusted code execution path.

That sounds harsh. But it is true more often than we like to admit.

When you run:

npm install
npm ci
pip install -r requirements.txt
npx some-tool
python -m pip install ...

You are not just moving files into a project; you may be giving a large tree of third-party code access to your machine.

That code may run near:

.env files
SSH keys
cloud credentials
GitHub tokens
npm tokens
PyPI tokens
source code
build files
CI secrets
package publishing keys

That install step is one of the most trusted moments in a developer workflow.

It is also one of the best places for an attacker to hide.

So we have been building OMC around a different default:

What if a package got no access unless you clearly gave it access?

Not “install first, scan later.”

Not “trust it because the package name looks familiar.”

Not “let postinstall run and hope nothing bad happens.”

Start with no.

Grant on purpose.

Record the decision.

That is the idea.

Package install is not passive

Package managers were built to make things work.

That matters. Developers want packages to install. Builds need to pass. Projects need to work on laptops and in CI.

But that ease came with a security cost.

In JavaScript, packages can run install hooks like:

postinstall
prepare
preinstall

In Python, package install has its own sharp edges too:

build backends
source builds
native extension builds
.pth files
sitecustomize.py
usercustomize.py

Most of the time, nothing bad happens.

But the default is still wrong.

A random package should not automatically get to:

read environment variables
read files
write files
use the network
run shell commands
use eval
look for credentials

Just because it appeared in a dependency tree.

That is too much trust.

OMC flips the default

OMC is a package manager prototype for npm and PyPI.

It checks packages before it installs them.

The flow looks like this:

read dependency file
resolve package versions
download package archive
do not run install scripts
inspect the package
find risky behavior
check that behavior against policy
record the result
install only if accepted

The important part is simple:

OMC does not run package install scripts during install.

For npm, lifecycle scripts are treated as something to inspect, not something to run.

For Python, OMC avoids installing startup hook files like:

.pth
sitecustomize.py
usercustomize.py

The package has to pass policy first.

Only then does it land in your project.

That changes the main question.

The old question was:

Is this package known to be bad?

That still matters.

But OMC asks a better question first:

What is this package trying to do?

A package should describe its behavior

Most lockfiles tell you what version you installed.

That is useful.

But it is not enough.

OMC wants the lockfile to also tell you what you allowed.

Instead of this:

left-pad@1.3.0: downloaded, unpacked, trusted

we want this:

is-odd@3.0.1: pure, no host access

requests@2.32.3: network-capable, grant needed

esbuild@0.19.12: install-time file, process, env, and network behavior, blocked by default

some-package@2.1.1: changed from pure to env + network + postinstall, review needed

That is the real shift.

A dependency update should not only show a version change.

It should show a behavior change.

If a package needed no host access yesterday but wants env vars and network access today, that should be visible.

If a transitive package adds a postinstall script, that should be visible.

If a package starts reading secrets, writing files, spawning processes, using eval, or sending data to the network, that should be a review event.

Not a surprise hidden inside a tarball.

Install it

OMC ships as one binary.

The current Homebrew install is:

brew install turenlabs/tap/omc

Some newer Homebrew setups may ask you to trust the tap first:

brew trust turenlabs/tap
brew install turenlabs/tap/omc

The shims are optional. OMC does not have to replace your system node, npm, pip, or python.

You can call OMC directly:

omc npm install
omc pip install
omc node app.js
omc python app.py

Or you can opt into drop-in names by putting the shim path first:

export PATH="$(brew --prefix omc)/libexec/shims:$PATH"

Then commands like npm, pip, node, and python route through OMC.

Same engine.

Different entry point.

A simple install should be boring

Here is a tiny npm example:

omc init --name myapp
omc add --npm is-odd@3.0.1

The output should be boring in the best way:

is-odd 3.0.1     no host access
is-number 6.0.0  no host access

✓ Installed 2 packages -> ./node_modules

Then run it:

omc node -e "console.log(require('is-odd')(3), require('is-odd')(4))"
true false

Nothing dramatic happened because nothing dramatic was needed.

The package did not need your env vars.

It did not need the network.

It did not need to run a process.

It did not need broad file access.

So it installed.

That should be normal.

You can find the full source of OMC at: https://github.com/turenlabs/omc


Having problems with software at speed? Turen can help. Sign up for a 14-day trial at https://turen.io or view the live demo at https://try.turen.io