-
Notifications
You must be signed in to change notification settings - Fork 0
Description
In light of the recent event-stream security issue, and the previous left-pad debacle, it seems sensible to open discussion on approaches to more secure methods of guaranteeing authorship and safe publishing & installation of packages.
What exists already?
note: "exists" refers to in the design, not code, wool is still very early pre-alpha
Indirect packages
In its current design, wool helps surface indirect / transient packages. This helps developers be fully aware of the quantity of dependencies in their project and whose code is running.
wool add hello/world
To add hello/world I would like to install the following packages:
hello/world 1.2.0 1.0.0 <= v < 2.0.0
hello/other 2.0.0 2.0.0 <= v < 3.0.0
Should I install these packages? [Y/n]
{
"dependencies": {
"direct": {
"hello/world": "1.0.0 <= v < 2.0.0"
},
"indirect": {
"hello/other": "2.0.0 <= v < 3.0.0"
}
}
}
Automatic semver
It also only allows a single entry point into packages with an automatically semantically versioned interface.
Registries
When creating a project, the developer must choose which package registries to point at (defaulting to just registry.wool.org).
{
"registries": ["registry.wool.org"]
}
All registries must be accessible over https.
When adding a package, its registry is stored in the wool.lock, e.g.
{
"hello/world": {
"version": "1.2.0",
"constraint": "1.0.0 <= v < 2.0.0",
"registry": "registry.wool.org"
}
}
Further ideas
Package permissions and sandboxing
Restricted default file access
Most packages do not need direct access to the filesystem, so by default they should not be allowed to read or write anywhere.
Restricted default network access
Most packages also do not need network access, so by default they should also not be allowed to send network requests.
Granting permissions
Packages should be provided opt-in permissions to let them perform certain, risky actions.
When running wool make ., wool would warn them that a package does not have the permissions it requires:
❖ PERMISSIONS REQUIRED --------------------------------------------- hello/world
The package hello/world requires the following permissions:
"read": true
"write: ["./path/to/folder"]
Do you wish to grant these permissions to hello/world? [y/N]
This would then update their wool.json and re-attempt the compilation.
{
"dependencies": {
"direct": {
- "hello/world": "1.0.0 <= v < 2.0.0"
+ "hello/world": {
+ "constraint": "1.0.0 <= v < 2.0.0",
+ "permissions": {
+ "read": true,
+ "write": ["./path/to/folder"]
+ }
+ }
}
}
}For example, to restrict file access this can be acheived through the wool loader by preventing installed packages from using the fs package directly.
Instead import via wool/fs (with the loader explicitly only allowing the wool namespace direct access to node in-built packages).
-import * as fs from 'fs';
+import * as fs from 'wool/fs';Publish verification
2-factor authentication should be mandatory for all publishing, even to private registries.
Package archival
It should be possible for maintainers to archive packages and put them in a read-only mode.
wool archive .
Both archiving and unarchiving a package would require 2-factor authentication.
Developers adding archived projects will be warned:
❖ PACKAGE ARCHIVED ----------------------------------------------- hello/world
The package hello/world has been archived and is no longer being maintained.
It may present a security risk for your project if there are un-patched
vulnerabilities.
Do you wish to continue using hello/world? [y/N]
{
"dependencies": {
"direct": {
- "hello/world": "1.0.0 <= v < 2.0.0"
+ "hello/world": {
+ "constraint": "1.0.0 <= v < 2.0.0",
+ "allowArchived": true
+ }
}
}
}Transferal of ownership
It should be feasible to transfer ownership of a project that the maintainer no longer wishes to support.
This would involve the owner pushing a "transference key" to the registry, this would immediately lock the package and prevent any new publications. They would then share the key with the intended new owner, who would then use it to unlock and take control of the package.
Only one account should be able to publish a package at any given time.
Transferal of ownership would enforce the next publication to be a major version update bump.
If a developer updates their constraint to the next major version they will be warned about the ownership change.
❖ PACKAGE OWNER CHANGED --------------------------------------- hello/world
The package `hello/world` has transferred ownership from:
hello@world.com
to:
bonjour@monde.com
Before updating from 1.2.0 to 2.0.0 you should check the new package owner
is trustworthy.
Do you wish to continue? [y/N]
Checksum imports
When compiling, check the imported package code against their checksums stored in the wool.lock.
{
"hello/world": {
"version": "1.2.0",
"constraint": "1.0.0 <= v < 2.0.0",
"registry": "registry.wool.org",
"checksum": "123456"
}
}
This could be generated by running a concatenated toString() on the exported functions & values. If another package mutates the function it will return a different toString() value and fail the checksum validation.
e.g.
import hello from 'hello/world'
let originalGreet = hello.greet;
hello.greet = async (message) => {
await mineSomeBitcoin();
return hello.greet(message);
};Questions
Decentralisation and package name clashes
One of the main issues around a decentralised approach to package registries is namespace squatting and package name clashes.
Even from non-malicious actors this would be confusing and annoying.
If a package name exists on multiple registries, the user will be asked to confirm which registry they wish to install from:
❖ PACKAGE NAME CLASH --------------------------------------- hello/world
The package `hello/world` exists on more than one registry:
registry.wool.org
registry.wool-community.net
Which registry should I download this package from? # multiple choice input
{
"registries": ["registry.wool.org"]
"dependencies": {
"direct": {
- "hello/world": "1.0.0 <= v < 2.0.0"
+ "hello/world": {
+ "constraint": "1.0.0 <= v < 2.0.0",
+ "registry": "registry.wool.org"
+ }
}
}
}Is this enough? Is there any sensible way to prevent name clashing between decentralised registries?
What about namespace squatting?
Say the company hello registers their namespace on registry.wool.org, and publishes hello/world.
Then on another registry, for example registry.wool-community.net, an unaffiliated user registers hello. What recourse can the company hello take? Can the user mimic the company and freely cause confusion?
Should there be a single central authority for public namespace registration? Ideally not.
Perhaps allow whole namespaces to be tied to registries, instead of on a per-package basis.
{
"registries": [
"registry.wool.org",
+ {
+ "namespace": "hello",
+ "registry": "registry.hello.com"
+ }
],
...
}And importantly, how can wool assuage non-developer stakeholders of the risks?
Commit verification
Should publishing be tied to the git repository? The primary downside is it would force package authors to use git, however this is by far the most dominant vcs used. And security is more important than convenience.
This would allow commit verification as a requirement.
It could be an opt-in for registries, i.e. a registry could chose to force all users to verify their commits.
Anything else?
There are probably numerous other issues and attack vectors possible.