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

Permission path delimiter #48944

Closed
wants to merge 10 commits into from
Closed
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
19 changes: 19 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,24 @@ unless either the `--pending-deprecation` command-line flag, or the
are used to provide a kind of selective "early warning" mechanism that
developers may leverage to detect deprecated API usage.

### `--permission-fs-path-delimiter`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

This flag configures the file system path delimiter for permissions using
the [Permission Model][].

Examples can be found in the [File System Permissions][] documentation.

```bash
node --experimental-permission --permission-fs-path-delimiter=";" \
--allow-fs-read="/path/to/index.js;/path/with,comma" index.js
```

### `--policy-integrity=sri`

<!-- YAML
Expand Down Expand Up @@ -2183,6 +2201,7 @@ Node.js options that are allowed are:
* `--openssl-legacy-provider`
* `--openssl-shared-config`
* `--pending-deprecation`
* `--permission-fs-path-delimiter`
* `--policy-integrity`
* `--preserve-symlinks-main`
* `--preserve-symlinks`
Expand Down
19 changes: 19 additions & 0 deletions doc/api/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,25 @@ Wildcards are supported too:
* `--allow-fs-read=/home/test*` will allow read access to everything
that matches the wildcard. e.g: `/home/test/file1` or `/home/test2`

##### Accessing files with comma in path

To access files with a comma in the path you can change the path delimiter using
the `--permission-fs-path-delimiter` flag to set a value not used in any of the
paths you want to access.

```console
$ node --experimental-permission --allow-fs-read="/with,commas_/home" \
--permission-fs-path-delimiter=_ index.js
```

Note when using special shell characters such as `;` escaping or quoting is
required.

```console
$ node --experimental-permission --allow-fs-read="/home/with,commas;/home" \
--permission-fs-path-delimiter=";" index.js
```

#### Limitations and known issues

There are constraints you need to know before using this system:
Expand Down
3 changes: 3 additions & 0 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,9 @@ Among other uses, this can be used to enable FIPS-compliant crypto if Node.js is
.It Fl -pending-deprecation
Emit pending deprecation warnings.
.
.It Fl -permission-fs-path-delimiter
File system path delimiter used when providing multiple read or write allowed files using the permission model.
.
.It Fl -policy-integrity Ns = Ns Ar sri
Instructs Node.js to error prior to running any code if the policy does not have the specified integrity. It expects a Subresource Integrity string as a parameter.
.
Expand Down
8 changes: 5 additions & 3 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -856,15 +856,17 @@ Environment::Environment(IsolateData* isolate_data,
if (!options_->allow_worker_threads) {
permission()->Apply("*", permission::PermissionScope::kWorkerThreads);
}

const std::string delimiter = options_->permission_fs_path_delimiter;
if (!options_->allow_fs_read.empty()) {
permission()->Apply(options_->allow_fs_read,
permission::PermissionScope::kFileSystemRead);
permission::PermissionScope::kFileSystemRead,
{{"delimiter", delimiter}});
}

if (!options_->allow_fs_write.empty()) {
permission()->Apply(options_->allow_fs_write,
permission::PermissionScope::kFileSystemWrite);
permission::PermissionScope::kFileSystemWrite,
{{"delimiter", delimiter}});
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
"allow permissions to read the filesystem",
&EnvironmentOptions::allow_fs_read,
kAllowedInEnvvar);
AddOption("--permission-fs-path-delimiter",
"set the delimiter char for the file system permission model",
&EnvironmentOptions::permission_fs_path_delimiter,
kAllowedInEnvvar);
AddOption("--allow-fs-write",
"allow permissions to write in the filesystem",
&EnvironmentOptions::allow_fs_write,
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class EnvironmentOptions : public Options {
bool experimental_permission = false;
std::string allow_fs_read;
std::string allow_fs_write;
std::string permission_fs_path_delimiter = ",";
bool allow_child_process = false;
bool allow_worker_threads = false;
bool experimental_repl_await = true;
Expand Down
7 changes: 5 additions & 2 deletions src/permission/child_process_permission.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "child_process_permission.h"

#include <string>
#include <unordered_map>
#include <vector>

namespace node {
Expand All @@ -9,8 +10,10 @@ namespace permission {

// Currently, ChildProcess manage a single state
// Once denied, it's always denied
void ChildProcessPermission::Apply(const std::string& allow,
PermissionScope scope) {
void ChildProcessPermission::Apply(
const std::string& allow,
PermissionScope scope,
const std::unordered_map<std::string, std::string>& options) {
deny_all_ = true;
}

Expand Down
6 changes: 5 additions & 1 deletion src/permission/child_process_permission.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include <unordered_map>
#include <vector>
#include "permission/permission_base.h"

Expand All @@ -12,7 +13,10 @@ namespace permission {

class ChildProcessPermission final : public PermissionBase {
public:
void Apply(const std::string& allow, PermissionScope scope) override;
void Apply(const std::string& allow,
PermissionScope scope,
const std::unordered_map<std::string, std::string>& options = {})
override;
bool is_granted(PermissionScope perm,
const std::string_view& param = "") override;

Expand Down
9 changes: 7 additions & 2 deletions src/permission/fs_permission.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <filesystem>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>

namespace {
Expand Down Expand Up @@ -116,9 +117,13 @@ namespace permission {

// allow = '*'
// allow = '/tmp/,/home/example.js'
void FSPermission::Apply(const std::string& allow, PermissionScope scope) {
void FSPermission::Apply(
const std::string& allow,
PermissionScope scope,
const std::unordered_map<std::string, std::string>& options) {
using std::string_view_literals::operator""sv;
for (const std::string_view res : SplitString(allow, ","sv)) {
const std::string delimiter = options.at("delimiter");
for (const std::string_view res : SplitString(allow, delimiter)) {
if (res == "*"sv) {
if (scope == PermissionScope::kFileSystemRead) {
deny_all_in_ = false;
Expand Down
5 changes: 4 additions & 1 deletion src/permission/fs_permission.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ namespace permission {

class FSPermission final : public PermissionBase {
public:
void Apply(const std::string& allow, PermissionScope scope) override;
void Apply(const std::string& allow,
PermissionScope scope,
const std::unordered_map<std::string, std::string>& options = {})
override;
bool is_granted(PermissionScope perm, const std::string_view& param) override;

struct RadixTree {
Expand Down
7 changes: 5 additions & 2 deletions src/permission/inspector_permission.cc
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
#include "inspector_permission.h"

#include <string>
#include <unordered_map>

namespace node {

namespace permission {

// Currently, Inspector manage a single state
// Once denied, it's always denied
void InspectorPermission::Apply(const std::string& allow,
PermissionScope scope) {
void InspectorPermission::Apply(
const std::string& allow,
PermissionScope scope,
const std::unordered_map<std::string, std::string>& options) {
deny_all_ = true;
}

Expand Down
6 changes: 5 additions & 1 deletion src/permission/inspector_permission.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include <string>
#include <unordered_map>
#include "permission/permission_base.h"

namespace node {
Expand All @@ -12,7 +13,10 @@ namespace permission {

class InspectorPermission final : public PermissionBase {
public:
void Apply(const std::string& allow, PermissionScope scope) override;
void Apply(const std::string& allow,
PermissionScope scope,
const std::unordered_map<std::string, std::string>& options = {})
override;
bool is_granted(PermissionScope perm,
const std::string_view& param = "") override;

Expand Down
8 changes: 6 additions & 2 deletions src/permission/permission.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include <memory>
#include <string>
#include <unordered_map>
#include <vector>

namespace node {
Expand Down Expand Up @@ -130,10 +131,13 @@ void Permission::EnablePermissions() {
}
}

void Permission::Apply(const std::string& allow, PermissionScope scope) {
void Permission::Apply(
const std::string& allow,
PermissionScope scope,
const std::unordered_map<std::string, std::string>& options) {
auto permission = nodes_.find(scope);
if (permission != nodes_.end()) {
permission->second->Apply(allow, scope);
permission->second->Apply(allow, scope, options);
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/permission/permission.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ class Permission {
const std::string_view& res);

// CLI Call
void Apply(const std::string& allow, PermissionScope scope);
void Apply(const std::string& allow,
PermissionScope scope,
const std::unordered_map<std::string, std::string>& options = {});
void EnablePermissions();

private:
Expand Down
6 changes: 5 additions & 1 deletion src/permission/permission_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <map>
#include <string>
#include <string_view>
#include <unordered_map>
#include "v8.h"

namespace node {
Expand Down Expand Up @@ -39,7 +40,10 @@ enum class PermissionScope {

class PermissionBase {
public:
virtual void Apply(const std::string& allow, PermissionScope scope) = 0;
virtual void Apply(
const std::string& allow,
PermissionScope scope,
const std::unordered_map<std::string, std::string>& options = {}) = 0;
virtual bool is_granted(PermissionScope perm,
const std::string_view& param = "") = 0;
};
Expand Down
6 changes: 5 additions & 1 deletion src/permission/worker_permission.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "permission/worker_permission.h"

#include <string>
#include <unordered_map>
#include <vector>

namespace node {
Expand All @@ -9,7 +10,10 @@ namespace permission {

// Currently, PolicyDenyWorker manage a single state
// Once denied, it's always denied
void WorkerPermission::Apply(const std::string& allow, PermissionScope scope) {
void WorkerPermission::Apply(
const std::string& allow,
PermissionScope scope,
const std::unordered_map<std::string, std::string>& options) {
deny_all_ = true;
}

Expand Down
6 changes: 5 additions & 1 deletion src/permission/worker_permission.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include <unordered_map>
#include <vector>
#include "permission/permission_base.h"

Expand All @@ -12,7 +13,10 @@ namespace permission {

class WorkerPermission final : public PermissionBase {
public:
void Apply(const std::string& allow, PermissionScope scope) override;
void Apply(const std::string& allow,
PermissionScope scope,
const std::unordered_map<std::string, std::string>& options = {})
override;
bool is_granted(PermissionScope perm,
const std::string_view& param = "") override;

Expand Down
Loading