permission: drop process.permission.deny

PR-URL: https://github.com/nodejs/node/pull/47335
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Beth Griggs <bethanyngriggs@gmail.com>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
This commit is contained in:
Rafael Gonzaga authored and GitHub committed 2023-04-04 17:14:04 +00:00
1 parent 7ec93fb296
commit 6fd147c4b0
36 files changed
+451 -797

No files matched your search

@@ -1,19 +0,0 @@
'use strict';
const common = require('../common.js');
const configs = {
n: [1e5],
concurrent: [1, 10],
};
const options = { flags: ['--experimental-permission'] };
const bench = common.createBenchmark(main, configs, options);
async function main(conf) {
bench.start();
for (let i = 0; i < conf.n; i++) {
process.permission.deny('fs.read', ['/home/example-file-' + i]);
}
bench.end(conf.n);
}
@@ -1,6 +1,5 @@
'use strict';
const common = require('../common.js');
const fs = require('fs/promises');
const path = require('path');
const configs = {
@@ -19,22 +18,11 @@ const options = {
const bench = common.createBenchmark(main, configs, options);
const recursivelyDenyFiles = async (dir) => {
const files = await fs.readdir(dir, { withFileTypes: true });
for (const file of files) {
if (file.isDirectory()) {
await recursivelyDenyFiles(path.join(dir, file.name));
} else if (file.isFile()) {
process.permission.deny('fs.read', [path.join(dir, file.name)]);
}
}
};
// This is a naive benchmark and might not demonstrate real-world use cases.
// New benchmarks will be created once the permission model config is available
// through a config file.
async function main(conf) {
const benchmarkDir = path.join(__dirname, '../..');
// Get all the benchmark files and deny access to it
await recursivelyDenyFiles(benchmarkDir);
bench.start();
for (let i = 0; i < conf.n; i++) {
+3 -43
View File
@@ -492,24 +492,7 @@ using the [`--allow-child-process`][] and [`--allow-worker`][] respectively.
When enabling the Permission Model through the [`--experimental-permission`][]
flag a new property `permission` is added to the `process` object.
This property contains two functions:
##### `permission.deny(scope [,parameters])`
API call to deny permissions at runtime ([`permission.deny()`][])
```js
process.permission.deny('fs'); // Deny permissions to ALL fs operations
// Deny permissions to ALL FileSystemWrite operations
process.permission.deny('fs.write');
// deny FileSystemWrite permissions to the protected-folder
process.permission.deny('fs.write', ['/home/rafaelgss/protected-folder']);
// Deny permissions to ALL FileSystemRead operations
process.permission.deny('fs.read');
// deny FileSystemRead permissions to the protected-folder
process.permission.deny('fs.read', ['/home/rafaelgss/protected-folder']);
```
This property contains one function:
##### `permission.has(scope ,parameters)`
@@ -519,10 +502,8 @@ API call to check permissions at runtime ([`permission.has()`][])
process.permission.has('fs.write'); // true
process.permission.has('fs.write', '/home/rafaelgss/protected-folder'); // true
process.permission.deny('fs.write', '/home/rafaelgss/protected-folder');
process.permission.has('fs.write'); // true
process.permission.has('fs.write', '/home/rafaelgss/protected-folder'); // false
process.permission.has('fs.read'); // true
process.permission.has('fs.read', '/home/rafaelgss/protected-folder'); // false
```
#### File System Permissions
@@ -560,31 +541,11 @@ There are constraints you need to know before using this system:
* Native modules are restricted by default when using the Permission Model.
* Relative paths are not supported through the CLI (`--allow-fs-*`).
The runtime API supports relative paths.
* The model does not inherit to a child node process.
* The model does not inherit to a worker thread.
* When creating symlinks the target (first argument) should have read and
write access.
* Permission changes are not retroactively applied to existing resources.
Consider the following snippet:
```js
const fs = require('node:fs');
// Open a fd
const fd = fs.openSync('./README.md', 'r');
// Then, deny access to all fs.read operations
process.permission.deny('fs.read');
// This call will NOT fail and the file will be read
const data = fs.readFileSync(fd);
```
Therefore, when possible, apply the permissions rules before any statement:
```js
process.permission.deny('fs.read');
const fd = fs.openSync('./README.md', 'r');
// Error: Access to this API has been restricted
```
[Security Policy]: https://github.com/nodejs/node/blob/main/SECURITY.md
[`--allow-child-process`]: cli.md#--allow-child-process
@@ -592,7 +553,6 @@ const fd = fs.openSync('./README.md', 'r');
[`--allow-fs-write`]: cli.md#--allow-fs-write
[`--allow-worker`]: cli.md#--allow-worker
[`--experimental-permission`]: cli.md#--experimental-permission
[`permission.deny()`]: process.md#processpermissiondenyscope-reference
[`permission.has()`]: process.md#processpermissionhasscope-reference
[import maps]: https://url.spec.whatwg.org/#relative-url-with-fragment-string
[relative-url string]: https://url.spec.whatwg.org/#relative-url-with-fragment-string
-28
View File
@@ -2634,34 +2634,6 @@ This API is available through the [`--experimental-permission`][] flag.
for the current process. Additional documentation is available in the
[Permission Model][].
### `process.permission.deny(scope[, reference])`
<!-- YAML
added: REPLACEME
-->
* `scopes` {string}
* `reference` {Array}
* Returns: {boolean}
Deny permissions at runtime.
The available scopes are:
* `fs` - All File System
* `fs.read` - File System read operations
* `fs.write` - File System write operations
The reference has a meaning based on the provided scope. For example,
the reference when the scope is File System means files and folders.
```js
// Deny READ operations to the ./README.md file
process.permission.deny('fs.read', ['./README.md']);
// Deny ALL WRITE operations
process.permission.deny('fs.write');
```
### `process.permission.has(scope[, reference])`
<!-- YAML
+1 -23
View File
@@ -2,11 +2,10 @@
const {
ObjectFreeze,
ArrayPrototypePush,
} = primordials;
const permission = internalBinding('permission');
const { validateString, validateArray } = require('internal/validators');
const { validateString } = require('internal/validators');
const { isAbsolute, resolve } = require('path');
let experimentalPermission;
@@ -20,27 +19,6 @@ module.exports = ObjectFreeze({
}
return experimentalPermission;
},
deny(scope, references) {
validateString(scope, 'scope');
if (references == null) {
return permission.deny(scope, references);
}
validateArray(references, 'references');
// TODO(rafaelgss): change to call fs_permission.resolve when available
const normalizedParams = [];
for (let i = 0; i < references.length; ++i) {
if (isAbsolute(references[i])) {
ArrayPrototypePush(normalizedParams, references[i]);
} else {
// TODO(aduh95): add support for WHATWG URLs and Uint8Arrays.
ArrayPrototypePush(normalizedParams, resolve(references[i]));
}
}
return permission.deny(scope, normalizedParams);
},
has(scope, reference) {
validateString(scope, 'scope');
if (reference != null) {
+2 -2
View File
@@ -784,10 +784,10 @@ Environment::Environment(IsolateData* isolate_data,
if (!options_->allow_fs_read.empty() || !options_->allow_fs_write.empty()) {
options_->allow_native_addons = false;
if (!options_->allow_child_process) {
permission()->Deny(permission::PermissionScope::kChildProcess, {});
permission()->Apply("*", permission::PermissionScope::kChildProcess);
}
if (!options_->allow_worker_threads) {
permission()->Deny(permission::PermissionScope::kWorkerThreads, {});
permission()->Apply("*", permission::PermissionScope::kWorkerThreads);
}
}
+1 -5
View File
@@ -10,12 +10,8 @@ namespace permission {
// Currently, ChildProcess manage a single state
// Once denied, it's always denied
void ChildProcessPermission::Apply(const std::string& deny,
PermissionScope scope) {}
bool ChildProcessPermission::Deny(PermissionScope perm,
const std::vector<std::string>& params) {
PermissionScope scope) {
deny_all_ = true;
return true;
}
bool ChildProcessPermission::is_granted(PermissionScope perm,
@@ -13,8 +13,6 @@ namespace permission {
class ChildProcessPermission final : public PermissionBase {
public:
void Apply(const std::string& deny, PermissionScope scope) override;
bool Deny(PermissionScope scope,
const std::vector<std::string>& params) override;
bool is_granted(PermissionScope perm,
const std::string_view& param = "") override;
+5 -41
View File
@@ -48,8 +48,7 @@ void FreeRecursivelyNode(
delete node;
}
bool is_tree_granted(node::permission::FSPermission::RadixTree* deny_tree,
node::permission::FSPermission::RadixTree* granted_tree,
bool is_tree_granted(node::permission::FSPermission::RadixTree* granted_tree,
const std::string_view& param) {
#ifdef _WIN32
// is UNC file path
@@ -60,11 +59,10 @@ bool is_tree_granted(node::permission::FSPermission::RadixTree* deny_tree,
starting_pos += 4; // "UNC\"
}
auto normalized = param.substr(starting_pos);
return !deny_tree->Lookup(normalized) &&
granted_tree->Lookup(normalized, true);
return granted_tree->Lookup(normalized, true);
}
#endif
return !deny_tree->Lookup(param) && granted_tree->Lookup(param, true);
return granted_tree->Lookup(param, true);
}
} // namespace
@@ -91,40 +89,6 @@ void FSPermission::Apply(const std::string& allow, PermissionScope scope) {
}
}
bool FSPermission::Deny(PermissionScope perm,
const std::vector<std::string>& params) {
if (perm == PermissionScope::kFileSystem) {
deny_all_in_ = true;
deny_all_out_ = true;
return true;
}
bool deny_all = params.size() == 0;
if (perm == PermissionScope::kFileSystemRead) {
if (deny_all) deny_all_in_ = true;
// when deny_all_in is already true permission.deny should be idempotent
if (deny_all_in_) return true;
allow_all_in_ = false;
for (auto& param : params) {
deny_in_fs_.Insert(WildcardIfDir(param));
}
return true;
}
if (perm == PermissionScope::kFileSystemWrite) {
if (deny_all) deny_all_out_ = true;
// when deny_all_out is already true permission.deny should be idempotent
if (deny_all_out_) return true;
allow_all_out_ = false;
for (auto& param : params) {
deny_out_fs_.Insert(WildcardIfDir(param));
}
return true;
}
return false;
}
void FSPermission::GrantAccess(PermissionScope perm, std::string res) {
const std::string path = WildcardIfDir(res);
if (perm == PermissionScope::kFileSystemRead) {
@@ -144,11 +108,11 @@ bool FSPermission::is_granted(PermissionScope perm,
case PermissionScope::kFileSystemRead:
return !deny_all_in_ &&
((param.empty() && allow_all_in_) || allow_all_in_ ||
is_tree_granted(&deny_in_fs_, &granted_in_fs_, param));
is_tree_granted(&granted_in_fs_, param));
case PermissionScope::kFileSystemWrite:
return !deny_all_out_ &&
((param.empty() && allow_all_out_) || allow_all_out_ ||
is_tree_granted(&deny_out_fs_, &granted_out_fs_, param));
is_tree_granted(&granted_out_fs_, param));
default:
return false;
}
-11
View File
@@ -17,8 +17,6 @@ namespace permission {
class FSPermission final : public PermissionBase {
public:
void Apply(const std::string& deny, PermissionScope scope) override;
bool Deny(PermissionScope scope,
const std::vector<std::string>& params) override;
bool is_granted(PermissionScope perm, const std::string_view& param) override;
// For debugging purposes, use the gist function to print the whole tree
@@ -135,18 +133,9 @@ class FSPermission final : public PermissionBase {
void GrantAccess(PermissionScope scope, std::string param);
void RestrictAccess(PermissionScope scope,
const std::vector<std::string>& params);
// /tmp/* --grant
// /tmp/dsadsa/t.js denied in runtime
//
// /tmp/text.txt -- grant
// /tmp/text.txt -- denied in runtime
//
// fs granted on startup
RadixTree granted_in_fs_;
RadixTree granted_out_fs_;
// fs denied in runtime
RadixTree deny_in_fs_;
RadixTree deny_out_fs_;
bool deny_all_in_ = true;
bool deny_all_out_ = true;
-49
View File
@@ -14,10 +14,8 @@
namespace node {
using v8::Array;
using v8::Context;
using v8::FunctionCallbackInfo;
using v8::Integer;
using v8::Local;
using v8::Object;
using v8::String;
@@ -27,42 +25,6 @@ namespace permission {
namespace {
// permission.deny('fs.read', ['/tmp/'])
// permission.deny('fs.read')
static void Deny(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
v8::Isolate* isolate = env->isolate();
CHECK(args[0]->IsString());
std::string deny_scope = *String::Utf8Value(isolate, args[0]);
PermissionScope scope = Permission::StringToPermission(deny_scope);
if (scope == PermissionScope::kPermissionsRoot) {
return args.GetReturnValue().Set(false);
}
std::vector<std::string> params;
if (args.Length() == 1 || args[1]->IsUndefined()) {
return args.GetReturnValue().Set(env->permission()->Deny(scope, params));
}
CHECK(args[1]->IsArray());
Local<Array> js_params = Local<Array>::Cast(args[1]);
Local<Context> context = isolate->GetCurrentContext();
for (uint32_t i = 0; i < js_params->Length(); ++i) {
Local<Value> arg;
if (!js_params->Get(context, Integer::New(isolate, i)).ToLocal(&arg)) {
return;
}
String::Utf8Value utf8_arg(isolate, arg);
if (*utf8_arg == nullptr) {
return;
}
params.push_back(*utf8_arg);
}
return args.GetReturnValue().Set(env->permission()->Deny(scope, params));
}
// permission.has('fs.in', '/tmp/')
// permission.has('fs.in')
static void Has(const FunctionCallbackInfo<Value>& args) {
@@ -169,27 +131,16 @@ void Permission::Apply(const std::string& allow, PermissionScope scope) {
}
}
bool Permission::Deny(PermissionScope scope,
const std::vector<std::string>& params) {
auto permission = nodes_.find(scope);
if (permission != nodes_.end()) {
return permission->second->Deny(scope, params);
}
return false;
}
void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
SetMethod(context, target, "deny", Deny);
SetMethodNoSideEffect(context, target, "has", Has);
target->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen).FromJust();
}
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(Deny);
registry->Register(Has);
}
-2
View File
@@ -47,8 +47,6 @@ class Permission {
// CLI Call
void Apply(const std::string& deny, PermissionScope scope);
// Permission.Deny API
bool Deny(PermissionScope scope, const std::vector<std::string>& params);
void EnablePermissions();
private:
-2
View File
@@ -37,8 +37,6 @@ enum class PermissionScope {
class PermissionBase {
public:
virtual void Apply(const std::string& deny, PermissionScope scope) = 0;
virtual bool Deny(PermissionScope scope,
const std::vector<std::string>& params) = 0;
virtual bool is_granted(PermissionScope perm,
const std::string_view& param = "") = 0;
};
+1 -5
View File
@@ -9,12 +9,8 @@ namespace permission {
// Currently, PolicyDenyWorker manage a single state
// Once denied, it's always denied
void WorkerPermission::Apply(const std::string& deny, PermissionScope scope) {}
bool WorkerPermission::Deny(PermissionScope perm,
const std::vector<std::string>& params) {
void WorkerPermission::Apply(const std::string& deny, PermissionScope scope) {
deny_all_ = true;
return true;
}
bool WorkerPermission::is_granted(PermissionScope perm,
-2
View File
@@ -13,8 +13,6 @@ namespace permission {
class WorkerPermission final : public PermissionBase {
public:
void Apply(const std::string& deny, PermissionScope scope) override;
bool Deny(PermissionScope scope,
const std::vector<std::string>& params) override;
bool is_granted(PermissionScope perm,
const std::string_view& param = "") override;
@@ -1,29 +1,19 @@
// Flags: --experimental-permission --allow-fs-read=* --allow-fs-write=*
'use strict';
const common = require('../common');
common.skipIfWorker();
const common = require('../../common');
const assert = require('assert');
const fs = require('fs');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const path = require('path');
const os = require('os');
const blockedFile = fixtures.path('permission', 'deny', 'protected-file.md');
const relativeProtectedFile = './test/fixtures/permission/deny/protected-file.md';
const absoluteProtectedFile = path.resolve(relativeProtectedFile);
const blockedFolder = tmpdir.path;
const blockedFile = process.env.BLOCKEDFILE;
const blockedFolder = process.env.BLOCKEDFOLDER;
const allowedFolder = process.env.ALLOWEDFOLDER;
const regularFile = __filename;
const uid = os.userInfo().uid;
const gid = os.userInfo().gid;
{
tmpdir.refresh();
assert.ok(process.permission.deny('fs.read', [blockedFile, blockedFolder]));
}
// fs.readFile
{
assert.throws(() => {
@@ -33,13 +23,6 @@ const gid = os.userInfo().gid;
permission: 'FileSystemRead',
resource: path.toNamespacedPath(blockedFile),
}));
assert.throws(() => {
fs.readFile(relativeProtectedFile, () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
resource: path.toNamespacedPath(absoluteProtectedFile),
}));
assert.throws(() => {
fs.readFile(path.join(blockedFolder, 'anyfile'), () => {});
}, common.expectsError({
@@ -65,17 +48,6 @@ const gid = os.userInfo().gid;
resource: path.toNamespacedPath(blockedFile),
})).then(common.mustCall());
assert.rejects(() => {
return new Promise((_resolve, reject) => {
const stream = fs.createReadStream(relativeProtectedFile);
stream.on('error', reject);
});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
resource: path.toNamespacedPath(absoluteProtectedFile),
})).then(common.mustCall());
assert.rejects(() => {
return new Promise((_resolve, reject) => {
const stream = fs.createReadStream(blockedFile);
@@ -97,13 +69,6 @@ const gid = os.userInfo().gid;
permission: 'FileSystemRead',
resource: path.toNamespacedPath(blockedFile),
}));
assert.throws(() => {
fs.stat(relativeProtectedFile, () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
resource: path.toNamespacedPath(absoluteProtectedFile),
}));
assert.throws(() => {
fs.stat(path.join(blockedFolder, 'anyfile'), () => {});
}, common.expectsError({
@@ -127,13 +92,6 @@ const gid = os.userInfo().gid;
permission: 'FileSystemRead',
resource: path.toNamespacedPath(blockedFile),
}));
assert.throws(() => {
fs.access(relativeProtectedFile, fs.constants.R_OK, () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
resource: path.toNamespacedPath(absoluteProtectedFile),
}));
assert.throws(() => {
fs.access(path.join(blockedFolder, 'anyfile'), fs.constants.R_OK, () => {});
}, common.expectsError({
@@ -159,15 +117,6 @@ const gid = os.userInfo().gid;
permission: 'FileSystemRead',
resource: path.toNamespacedPath(blockedFile),
}));
assert.throws(() => {
// This operation will work fine
fs.chownSync(relativeProtectedFile, uid, gid);
fs.readFileSync(relativeProtectedFile);
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
resource: path.toNamespacedPath(absoluteProtectedFile),
}));
}
// fs.copyFile
@@ -179,13 +128,6 @@ const gid = os.userInfo().gid;
permission: 'FileSystemRead',
resource: path.toNamespacedPath(blockedFile),
}));
assert.throws(() => {
fs.copyFile(relativeProtectedFile, path.join(blockedFolder, 'any-other-file'), () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
resource: path.toNamespacedPath(absoluteProtectedFile),
}));
assert.throws(() => {
fs.copyFile(blockedFile, path.join(__dirname, 'any-other-file'), () => {});
}, common.expectsError({
@@ -205,19 +147,12 @@ const gid = os.userInfo().gid;
// cpSync calls statSync before reading blockedFile
resource: path.toNamespacedPath(blockedFolder),
}));
assert.throws(() => {
fs.cpSync(relativeProtectedFile, path.join(blockedFolder, 'any-other-file'));
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
resource: path.toNamespacedPath(blockedFolder),
}));
assert.throws(() => {
fs.cpSync(blockedFile, path.join(__dirname, 'any-other-file'));
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
resource: path.toNamespacedPath(blockedFile),
resource: path.toNamespacedPath(__dirname),
}));
}
@@ -230,13 +165,6 @@ const gid = os.userInfo().gid;
permission: 'FileSystemRead',
resource: path.toNamespacedPath(blockedFile),
}));
assert.throws(() => {
fs.open(relativeProtectedFile, 'r', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
resource: path.toNamespacedPath(absoluteProtectedFile),
}));
assert.throws(() => {
fs.open(path.join(blockedFolder, 'anyfile'), 'r', () => {});
}, common.expectsError({
@@ -278,7 +206,7 @@ const gid = os.userInfo().gid;
resource: path.toNamespacedPath(blockedFolder),
}));
// doesNotThrow
fs.opendir(__dirname, (err, dir) => {
fs.opendir(allowedFolder, (err, dir) => {
assert.ifError(err);
dir.closeSync();
});
@@ -295,7 +223,7 @@ const gid = os.userInfo().gid;
}));
// doesNotThrow
fs.readdir(__dirname, (err) => {
fs.readdir(allowedFolder, (err) => {
assert.ifError(err);
});
}
@@ -309,16 +237,9 @@ const gid = os.userInfo().gid;
permission: 'FileSystemRead',
resource: path.toNamespacedPath(blockedFile),
}));
assert.throws(() => {
fs.watch(relativeProtectedFile, () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
resource: path.toNamespacedPath(absoluteProtectedFile),
}));
// doesNotThrow
fs.readdir(__dirname, (err) => {
fs.readdir(allowedFolder, (err) => {
assert.ifError(err);
});
}
@@ -332,12 +253,4 @@ const gid = os.userInfo().gid;
permission: 'FileSystemRead',
resource: path.toNamespacedPath(blockedFile),
}));
assert.throws(() => {
fs.rename(relativeProtectedFile, 'newfile', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
resource: path.toNamespacedPath(absoluteProtectedFile),
}));
}
tmpdir.refresh();
@@ -1,30 +1,23 @@
// Flags: --experimental-permission --allow-fs-read=* --allow-fs-write=* --allow-child-process
'use strict';
'use strict'
const common = require('../common');
common.skipIfWorker();
if (!common.canCreateSymLink())
common.skip('insufficient privileges');
const common = require('../../common');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const tmpdir = require('../common/tmpdir');
tmpdir.refresh(true);
const readOnlyFolder = path.join(tmpdir.path, 'read-only');
const readWriteFolder = path.join(tmpdir.path, 'read-write');
const writeOnlyFolder = path.join(tmpdir.path, 'write-only');
fs.mkdirSync(readOnlyFolder);
fs.mkdirSync(readWriteFolder);
fs.mkdirSync(writeOnlyFolder);
fs.writeFileSync(path.join(readOnlyFolder, 'file'), 'evil file contents');
fs.writeFileSync(path.join(readWriteFolder, 'file'), 'NO evil file contents');
const readOnlyFolder = process.env.READONLYFOLDER;
const readWriteFolder = process.env.READWRITEFOLDER;
const writeOnlyFolder = process.env.WRITEONLYFOLDER;
{
assert.ok(process.permission.deny('fs.write', [readOnlyFolder]));
assert.ok(process.permission.deny('fs.read', [writeOnlyFolder]));
assert.ok(!process.permission.has('fs.write', readOnlyFolder));
assert.ok(!process.permission.has('fs.read', writeOnlyFolder));
assert.ok(process.permission.has('fs.write', readWriteFolder));
assert.ok(process.permission.has('fs.write', writeOnlyFolder));
assert.ok(process.permission.has('fs.read', readOnlyFolder));
assert.ok(process.permission.has('fs.read', readWriteFolder));
}
{
@@ -1,31 +1,21 @@
// Flags: --experimental-permission --allow-fs-read=* --allow-fs-write=* --allow-child-process
'use strict';
'use strict'
const common = require('../common');
common.skipIfWorker();
const fixtures = require('../common/fixtures');
if (!common.canCreateSymLink())
common.skip('insufficient privileges');
const common = require('../../common');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const tmpdir = require('../common/tmpdir');
tmpdir.refresh(true);
const blockedFile = fixtures.path('permission', 'deny', 'protected-file.md');
const blockedFolder = path.join(tmpdir.path, 'subdirectory');
const blockedFolder = process.env.BLOCKEDFOLDER;
const blockedFile = process.env.BLOCKEDFILE;
const regularFile = __filename;
const symlinkFromBlockedFile = path.join(tmpdir.path, 'example-symlink.md');
fs.mkdirSync(blockedFolder);
const symlinkFromBlockedFile = process.env.EXISTINGSYMLINK;
{
// Symlink previously created
fs.symlinkSync(blockedFile, symlinkFromBlockedFile);
assert.ok(process.permission.deny('fs.read', [blockedFile, blockedFolder]));
assert.ok(process.permission.deny('fs.write', [blockedFile, blockedFolder]));
assert.ok(!process.permission.has('fs.read', blockedFile))
assert.ok(!process.permission.has('fs.read', blockedFolder))
assert.ok(!process.permission.has('fs.write', blockedFile))
assert.ok(!process.permission.has('fs.write', blockedFolder))
}
{
@@ -101,4 +91,3 @@ fs.mkdirSync(blockedFolder);
permission: 'FileSystemRead',
}));
}
tmpdir.refresh(true);
+57
View File
@@ -0,0 +1,57 @@
'use strict'
const common = require('../../common');
const assert = require('assert');
const fs = require('fs');
{
assert.throws(() => {
fs.readFile('/test.txt', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
}));
// doesNotThrow
fs.readFile('/tmp/foo/file', () => {});
}
{
// doesNotThrow
fs.readFile('/example/foo/file', () => {});
fs.readFile('/example/foo2/file', () => {});
fs.readFile('/example/foo2', () => {});
assert.throws(() => {
fs.readFile('/example/fo/foo2.js', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
}));
assert.throws(() => {
fs.readFile('/example/for', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
}));
}
{
// doesNotThrow
fs.readFile('/example/bar/file', () => {});
fs.readFile('/example/bar2/file', () => {});
fs.readFile('/example/bar', () => {});
assert.throws(() => {
fs.readFile('/example/ba/foo2.js', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
}));
}
{
fs.readFile('/folder/a/subfolder/b', () => {});
fs.readFile('/folder/a/subfolder/b/c.txt', () => {});
fs.readFile('/folder/a/foo2.js', () => {});
}
@@ -1,27 +1,24 @@
// Flags: --experimental-permission --allow-fs-read=* --allow-fs-write=*
'use strict';
const common = require('../common');
const common = require('../../common');
common.skipIfWorker();
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const fixtures = require('../common/fixtures');
const blockedFolder = fixtures.path('permission', 'deny', 'protected-folder');
const blockedFile = fixtures.path('permission', 'deny', 'protected-file.md');
const relativeProtectedFile = './test/fixtures/permission/deny/protected-file.md';
const relativeProtectedFolder = './test/fixtures/permission/deny/protected-folder';
const regularFolder = process.env.ALLOWEDFOLDER;
const regularFile = process.env.ALLOWEDFILE;
const blockedFolder = process.env.BLOCKEDFOLDER;
const blockedFile = process.env.BLOCKEDFILE;
const relativeProtectedFile = process.env.RELATIVEBLOCKEDFILE;
const relativeProtectedFolder = process.env.RELATIVEBLOCKEDFOLDER;
const absoluteProtectedFile = path.resolve(relativeProtectedFile);
const absoluteProtectedFolder = path.resolve(relativeProtectedFolder);
const regularFolder = fixtures.path('permission', 'deny');
const regularFile = fixtures.path('permission', 'deny', 'regular-file.md');
{
assert.ok(process.permission.deny('fs.write', [blockedFolder]));
assert.ok(process.permission.deny('fs.write', [blockedFile]));
assert.ok(!process.permission.has('fs.write', blockedFolder));
assert.ok(!process.permission.has('fs.write', blockedFile));
}
// fs.writeFile
@@ -226,17 +223,6 @@ const regularFile = fixtures.path('permission', 'deny', 'regular-file.md');
permission: 'FileSystemWrite',
resource: path.toNamespacedPath(absoluteProtectedFolder),
}));
// The user shouldn't be capable to rmdir of a non-protected folder
// but that contains a protected file.
// The regularFolder contains a protected file
assert.throws(() => {
fs.rmSync(regularFolder, { recursive: true });
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemWrite',
resource: path.toNamespacedPath(blockedFile),
}));
}
// fs.open
@@ -1,70 +0,0 @@
// Flags: --experimental-permission --allow-fs-read=* --allow-child-process
'use strict';
const common = require('../common');
common.skipIfWorker();
const assert = require('assert');
const childProcess = require('child_process');
if (process.argv[2] === 'child') {
process.exit(0);
}
{
// doesNotThrow
const spawn = childProcess.spawn(process.execPath, ['--version']);
spawn.kill();
const exec = childProcess.exec(process.execPath, ['--version']);
exec.kill();
const fork = childProcess.fork(__filename, ['child']);
fork.kill();
const execFile = childProcess.execFile(process.execPath, ['--version']);
execFile.kill();
assert.ok(process.permission.deny('child'));
// When a permission is set by API, the process shouldn't be able
// to spawn
assert.throws(() => {
childProcess.spawn(process.execPath, ['--version']);
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'ChildProcess',
}));
assert.throws(() => {
childProcess.spawnSync(process.execPath, ['--version']);
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'ChildProcess',
}));
assert.throws(() => {
childProcess.exec(process.execPath, ['--version']);
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'ChildProcess',
}));
assert.throws(() => {
childProcess.execSync(process.execPath, ['--version']);
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'ChildProcess',
}));
assert.throws(() => {
childProcess.fork(__filename, ['child']);
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'ChildProcess',
}));
assert.throws(() => {
childProcess.execFile(process.execPath, ['--version']);
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'ChildProcess',
}));
assert.throws(() => {
childProcess.execFileSync(process.execPath, ['--version']);
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'ChildProcess',
}));
}
@@ -1,131 +0,0 @@
// Flags: --experimental-permission --allow-fs-read=*
'use strict';
const common = require('../common');
common.skipIfWorker();
const assert = require('assert');
const fs = require('fs');
const path = require('path');
if (common.isWindows) {
const { root } = path.parse(process.cwd());
const abs = (p) => path.join(root, p);
const denyList = [
'tmp\\*',
'example\\foo*',
'example\\bar*',
'folder\\*',
'show',
'slower',
'slown',
'home\\foo\\*',
].map(abs);
assert.ok(process.permission.deny('fs.read', denyList));
assert.ok(process.permission.has('fs.read', abs('slow')));
assert.ok(process.permission.has('fs.read', abs('slows')));
assert.ok(!process.permission.has('fs.read', abs('slown')));
assert.ok(!process.permission.has('fs.read', abs('home\\foo')));
assert.ok(!process.permission.has('fs.read', abs('home\\foo\\')));
assert.ok(process.permission.has('fs.read', abs('home\\fo')));
} else {
const denyList = [
'/tmp/*',
'/example/foo*',
'/example/bar*',
'/folder/*',
'/show',
'/slower',
'/slown',
'/home/foo/*',
];
assert.ok(process.permission.deny('fs.read', denyList));
assert.ok(process.permission.has('fs.read', '/slow'));
assert.ok(process.permission.has('fs.read', '/slows'));
assert.ok(!process.permission.has('fs.read', '/slown'));
assert.ok(!process.permission.has('fs.read', '/home/foo'));
assert.ok(!process.permission.has('fs.read', '/home/foo/'));
assert.ok(process.permission.has('fs.read', '/home/fo'));
}
{
assert.throws(() => {
fs.readFile('/tmp/foo/file', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
}));
// doesNotThrow
fs.readFile('/test.txt', () => {});
fs.readFile('/tmpd', () => {});
}
{
assert.throws(() => {
fs.readFile('/example/foo/file', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
}));
assert.throws(() => {
fs.readFile('/example/foo2/file', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
}));
assert.throws(() => {
fs.readFile('/example/foo2', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
}));
// doesNotThrow
fs.readFile('/example/fo/foo2.js', () => {});
fs.readFile('/example/for', () => {});
}
{
assert.throws(() => {
fs.readFile('/example/bar/file', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
}));
assert.throws(() => {
fs.readFile('/example/bar2/file', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
}));
assert.throws(() => {
fs.readFile('/example/bar', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
}));
// doesNotThrow
fs.readFile('/example/ba/foo2.js', () => {});
}
{
assert.throws(() => {
fs.readFile('/folder/a/subfolder/b', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
}));
assert.throws(() => {
fs.readFile('/folder/a/subfolder/b/c.txt', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
}));
assert.throws(() => {
fs.readFile('/folder/a/foo2.js', () => {});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
}));
}
@@ -1,32 +0,0 @@
// Flags: --experimental-permission --allow-fs-read=* --allow-worker
'use strict';
const common = require('../common');
const assert = require('assert');
const {
Worker,
isMainThread,
} = require('worker_threads');
const { once } = require('events');
async function createWorker() {
// doesNotThrow
const worker = new Worker(__filename);
await once(worker, 'exit');
// When a permission is set by API, the process shouldn't be able
// to create worker threads
assert.ok(process.permission.deny('worker'));
assert.throws(() => {
new Worker(__filename);
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'WorkerThreads',
}));
}
if (isMainThread) {
createWorker();
} else {
process.exit(0);
}
-97
View File
@@ -1,97 +0,0 @@
// Flags: --experimental-permission --allow-fs-read=* --allow-fs-write=*
'use strict';
const common = require('../common');
common.skipIfWorker();
const fs = require('fs');
const fsPromises = require('node:fs/promises');
const assert = require('assert');
const path = require('path');
const fixtures = require('../common/fixtures');
const protectedFolder = fixtures.path('permission', 'deny');
const protectedFile = fixtures.path('permission', 'deny', 'protected-file.md');
const regularFile = fixtures.path('permission', 'deny', 'regular-file.md');
// Assert has and deny exists
{
assert.ok(typeof process.permission.has === 'function');
assert.ok(typeof process.permission.deny === 'function');
}
// Guarantee the initial state when no flags
{
assert.ok(process.permission.has('fs.read'));
assert.ok(process.permission.has('fs.write'));
assert.ok(process.permission.has('fs.read', protectedFile));
assert.ok(process.permission.has('fs.read', regularFile));
assert.ok(process.permission.has('fs.write', protectedFolder));
assert.ok(process.permission.has('fs.write', regularFile));
// doesNotThrow
fs.readFileSync(protectedFile);
}
// Deny access to fs.read
{
assert.ok(process.permission.deny('fs.read', [protectedFile]));
assert.ok(process.permission.has('fs.read'));
assert.ok(process.permission.has('fs.write'));
assert.ok(process.permission.has('fs.read', regularFile));
assert.ok(!process.permission.has('fs.read', protectedFile));
assert.ok(process.permission.has('fs.write', protectedFolder));
assert.ok(process.permission.has('fs.write', regularFile));
assert.rejects(() => {
return fsPromises.readFile(protectedFile);
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
})).then(common.mustCall());
// doesNotThrow
fs.openSync(regularFile, 'w');
}
// Deny access to fs.write
{
assert.ok(process.permission.deny('fs.write', [protectedFolder]));
assert.ok(process.permission.has('fs.read'));
assert.ok(process.permission.has('fs.write'));
assert.ok(!process.permission.has('fs.read', protectedFile));
assert.ok(process.permission.has('fs.read', regularFile));
assert.ok(!process.permission.has('fs.write', protectedFolder));
assert.ok(!process.permission.has('fs.write', regularFile));
assert.rejects(() => {
return fsPromises
.writeFile(path.join(protectedFolder, 'new-file'), 'data');
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemWrite',
})).then(common.mustCall());
assert.throws(() => {
fs.openSync(regularFile, 'w');
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemWrite',
}));
}
// Should not crash if wrong parameter is provided
{
// Array is expected as second parameter
assert.throws(() => {
process.permission.deny('fs.read', protectedFolder);
}, common.expectsError({
code: 'ERR_INVALID_ARG_TYPE',
}));
}
+47
View File
@@ -0,0 +1,47 @@
// Flags: --experimental-permission --allow-fs-read=* --allow-fs-write=* --allow-child-process
'use strict';
const common = require('../common');
common.skipIfWorker();
if (!common.hasCrypto) {
common.skip('no crypto');
}
const assert = require('assert');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const { spawnSync } = require('child_process');
const path = require('path');
const blockedFile = fixtures.path('permission', 'deny', 'protected-file.md');
const blockedFolder = tmpdir.path;
const file = fixtures.path('permission', 'fs-read.js');
const commonPathWildcard = path.join(__filename, '../../common*');
const commonPath = path.join(__filename, '../../common');
{
tmpdir.refresh();
}
{
const { status, stderr } = spawnSync(
process.execPath,
[
'--experimental-permission', `--allow-fs-read=${file},${commonPathWildcard}`, file,
],
{
env: {
...process.env,
BLOCKEDFILE: blockedFile,
BLOCKEDFOLDER: blockedFolder,
ALLOWEDFOLDER: commonPath,
},
}
);
assert.strictEqual(status, 0, stderr.toString());
}
{
tmpdir.refresh();
}
@@ -5,26 +5,8 @@ const common = require('../common');
common.skipIfWorker();
const assert = require('assert');
const fixtures = require('../common/fixtures');
const { spawnSync } = require('child_process');
const protectedFile = fixtures.path('permission', 'deny', 'protected-file.md');
const relativeProtectedFile = './test/fixtures/permission/deny/protected-file.md';
// Note: for relative path on fs.* calls, check test-permission-deny-fs-[read/write].js files
{
// permission.deny relative path should work
assert.ok(process.permission.has('fs.read', protectedFile));
assert.ok(process.permission.deny('fs.read', [relativeProtectedFile]));
assert.ok(!process.permission.has('fs.read', protectedFile));
}
{
// permission.has relative path should work
assert.ok(!process.permission.has('fs.read', relativeProtectedFile));
}
{
// Relative path as CLI args are NOT supported yet
const { status, stdout } = spawnSync(
@@ -0,0 +1,57 @@
// Flags: --experimental-permission --allow-fs-read=* --allow-fs-write=* --allow-child-process
'use strict';
const common = require('../common');
common.skipIfWorker();
if (!common.canCreateSymLink())
common.skip('insufficient privileges');
if (!common.hasCrypto)
common.skip('no crypto');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const tmpdir = require('../common/tmpdir');
const fixtures = require('../common/fixtures');
const { spawnSync } = require('child_process');
{
tmpdir.refresh();
}
const readOnlyFolder = path.join(tmpdir.path, 'read-only');
const readWriteFolder = path.join(tmpdir.path, 'read-write');
const writeOnlyFolder = path.join(tmpdir.path, 'write-only');
const file = fixtures.path('permission', 'fs-symlink-target-write.js');
const commonPathWildcard = path.join(__filename, '../../common*');
fs.mkdirSync(readOnlyFolder);
fs.mkdirSync(readWriteFolder);
fs.mkdirSync(writeOnlyFolder);
fs.writeFileSync(path.join(readOnlyFolder, 'file'), 'evil file contents');
fs.writeFileSync(path.join(readWriteFolder, 'file'), 'NO evil file contents');
{
const { status, stderr } = spawnSync(
process.execPath,
[
'--experimental-permission',
`--allow-fs-read=${file},${commonPathWildcard},${readOnlyFolder},${readWriteFolder}`,
`--allow-fs-write=${readWriteFolder},${writeOnlyFolder}`,
file,
],
{
env: {
...process.env,
READONLYFOLDER: readOnlyFolder,
WRITEONLYFOLDER: writeOnlyFolder,
READWRITEFOLDER: readWriteFolder,
},
}
);
assert.strictEqual(status, 0, stderr.toString());
}
{
tmpdir.refresh();
}
@@ -0,0 +1,58 @@
// Flags: --experimental-permission --allow-fs-read=* --allow-fs-write=* --allow-child-process
'use strict';
const common = require('../common');
common.skipIfWorker();
const fixtures = require('../common/fixtures');
if (!common.canCreateSymLink())
common.skip('insufficient privileges');
if (!common.hasCrypto)
common.skip('no crypto');
const assert = require('assert');
const fs = require('fs');
const { spawnSync } = require('child_process');
const path = require('path');
const tmpdir = require('../common/tmpdir');
const file = fixtures.path('permission', 'fs-symlink.js');
const commonPathWildcard = path.join(__filename, '../../common*');
const blockedFile = fixtures.path('permission', 'deny', 'protected-file.md');
const blockedFolder = path.join(tmpdir.path, 'subdirectory');
const symlinkFromBlockedFile = path.join(tmpdir.path, 'example-symlink.md');
{
tmpdir.refresh();
fs.mkdirSync(blockedFolder);
}
{
// Symlink previously created
fs.symlinkSync(blockedFile, symlinkFromBlockedFile);
}
{
const { status, stderr } = spawnSync(
process.execPath,
[
'--experimental-permission',
`--allow-fs-read=${file},${commonPathWildcard},${symlinkFromBlockedFile}`,
`--allow-fs-write=${symlinkFromBlockedFile}`,
file,
],
{
env: {
...process.env,
BLOCKEDFOLDER: blockedFolder,
BLOCKEDFILE: blockedFile,
EXISTINGSYMLINK: symlinkFromBlockedFile,
},
}
);
assert.strictEqual(status, 0, stderr.toString());
}
{
tmpdir.refresh();
}
@@ -0,0 +1,93 @@
// Flags: --experimental-permission --allow-fs-read=* --allow-child-process
'use strict';
const common = require('../common');
common.skipIfWorker();
const assert = require('assert');
const path = require('path');
const { spawnSync } = require('child_process');
const fixtures = require('../common/fixtures');
const commonPathWildcard = path.join(__filename, '../../common*');
const file = fixtures.path('permission', 'fs-wildcard.js');
let allowList = [];
if (common.isWindows) {
const { root } = path.parse(process.cwd());
const abs = (p) => path.join(root, p);
allowList = [
'tmp\\*',
'example\\foo*',
'example\\bar*',
'folder\\*',
'show',
'slower',
'slown',
'home\\foo\\*',
].map(abs);
const { status, stderr } = spawnSync(
process.execPath,
[
'--experimental-permission',
`--allow-fs-read=${allowList.join(',')}`,
'-e',
`
const path = require('path');
const assert = require('assert');
const { root } = path.parse(process.cwd());
const abs = (p) => path.join(root, p);
assert.ok(!process.permission.has('fs.read', abs('slow')));
assert.ok(!process.permission.has('fs.read', abs('slows')));
assert.ok(process.permission.has('fs.read', abs('slown')));
assert.ok(process.permission.has('fs.read', abs('home\\\\foo')));
assert.ok(process.permission.has('fs.read', abs('home\\\\foo\\\\')));
assert.ok(!process.permission.has('fs.read', abs('home\\\\fo')));
`,
]
);
assert.strictEqual(status, 0, stderr.toString());
} else {
allowList = [
'/tmp/*',
'/example/foo*',
'/example/bar*',
'/folder/*',
'/show',
'/slower',
'/slown',
'/home/foo/*',
];
const { status, stderr } = spawnSync(
process.execPath,
[
'--experimental-permission',
`--allow-fs-read=${allowList.join(',')}`,
'-e',
`
const assert = require('assert')
assert.ok(!process.permission.has('fs.read', '/slow'));
assert.ok(!process.permission.has('fs.read', '/slows'));
assert.ok(process.permission.has('fs.read', '/slown'));
assert.ok(process.permission.has('fs.read', '/home/foo'));
assert.ok(process.permission.has('fs.read', '/home/foo/'));
assert.ok(!process.permission.has('fs.read', '/home/fo'));
`,
]
);
assert.strictEqual(status, 0, stderr.toString());
}
{
const { status, stderr } = spawnSync(
process.execPath,
[
'--experimental-permission',
`--allow-fs-read=${file},${commonPathWildcard},${allowList.join(',')}`,
file,
],
);
assert.strictEqual(status, 0, stderr.toString());
}
@@ -1,66 +1,40 @@
// Flags: --experimental-permission --allow-fs-read=* --allow-fs-write=* --allow-child-process
// Flags: --experimental-permission --allow-fs-read=* --allow-child-process
'use strict';
const common = require('../common');
common.skipIfWorker();
const assert = require('assert');
const fixtures = require('../common/fixtures');
const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');
if (!common.isWindows) {
common.skip('windows test');
}
const protectedFolder = fixtures.path('permission', 'deny', 'protected-folder');
{
assert.ok(process.permission.has('fs.write', protectedFolder));
assert.ok(process.permission.deny('fs.write', [protectedFolder]));
assert.ok(!process.permission.has('fs.write', protectedFolder));
common.skip('windows UNC path test');
}
{
assert.throws(() => {
fs.openSync(path.join(protectedFolder, 'protected-file.md'), 'w');
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemWrite',
resource: path.toNamespacedPath(path.join(protectedFolder, 'protected-file.md')),
}));
assert.rejects(() => {
return new Promise((_resolve, reject) => {
const stream = fs.createWriteStream(path.join(protectedFolder, 'protected-file.md'));
stream.on('error', reject);
});
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemWrite',
resource: path.toNamespacedPath(path.join(protectedFolder, 'protected-file.md')),
})).then(common.mustCall());
}
{
const { stdout } = spawnSync(process.execPath, [
const { stdout, status } = spawnSync(process.execPath, [
'--experimental-permission', '--allow-fs-write', 'C:\\\\', '-e',
'console.log(process.permission.has("fs.write", "C:\\\\"))',
]);
assert.strictEqual(stdout.toString(), 'true\n');
assert.strictEqual(status, 0);
}
{
assert.ok(process.permission.has('fs.write', 'C:\\home'));
assert.ok(process.permission.deny('fs.write', ['C:\\home']));
assert.ok(!process.permission.has('fs.write', 'C:\\home'));
const { stdout, status, stderr } = spawnSync(process.execPath, [
'--experimental-permission', '--allow-fs-write="\\\\?\\C:\\"', '-e',
'console.log(process.permission.has("fs.write", "C:\\\\"))',
]);
assert.strictEqual(stdout.toString(), 'false\n', stderr.toString());
assert.strictEqual(status, 0);
}
{
assert.ok(process.permission.has('fs.write', '\\\\?\\C:\\'));
assert.ok(process.permission.deny('fs.write', ['\\\\?\\C:\\']));
// UNC aren't supported so far
assert.ok(process.permission.has('fs.write', 'C:/'));
assert.ok(process.permission.has('fs.write', '\\\\?\\C:\\'));
const { stdout, status, stderr } = spawnSync(process.execPath, [
'--experimental-permission', '--allow-fs-write', 'C:\\', '-e',
`const path = require('path');
console.log(process.permission.has('fs.write', path.toNamespacedPath('C:\\\\')))`,
]);
assert.strictEqual(stdout.toString(), 'true\n', stderr.toString());
assert.strictEqual(status, 0);
}
+45
View File
@@ -0,0 +1,45 @@
// Flags: --experimental-permission --allow-fs-read=* --allow-child-process
'use strict';
const common = require('../common');
common.skipIfWorker();
if (!common.hasCrypto)
common.skip('no crypto');
const assert = require('assert');
const path = require('path');
const { spawnSync } = require('child_process');
const fixtures = require('../common/fixtures');
const blockedFolder = fixtures.path('permission', 'deny', 'protected-folder');
const blockedFile = fixtures.path('permission', 'deny', 'protected-file.md');
const relativeProtectedFile = './test/fixtures/permission/deny/protected-file.md';
const relativeProtectedFolder = './test/fixtures/permission/deny/protected-folder';
const commonPath = path.join(__filename, '../../common');
const regularFile = fixtures.path('permission', 'deny', 'regular-file.md');
const file = fixtures.path('permission', 'fs-write.js');
{
const { status, stderr } = spawnSync(
process.execPath,
[
'--experimental-permission',
'--allow-fs-read=*',
`--allow-fs-write=${regularFile},${commonPath}`,
file,
],
{
env: {
...process.env,
BLOCKEDFILE: blockedFile,
BLOCKEDFOLDER: blockedFolder,
RELATIVEBLOCKEDFOLDER: relativeProtectedFolder,
RELATIVEBLOCKEDFILE: relativeProtectedFile,
ALLOWEDFILE: regularFile,
ALLOWEDFOLDER: commonPath,
},
}
);
assert.strictEqual(status, 0, stderr.toString());
}
+23
View File
@@ -0,0 +1,23 @@
// Flags: --experimental-permission --allow-fs-read=*
'use strict';
const common = require('../common');
const assert = require('assert');
{
assert.ok(typeof process.permission.has === 'function');
assert.throws(() => {
process.permission.has(null, '');
}, common.expectsError({
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "scope" argument must be of type string. Received null',
}));
assert.ok(!process.permission.has('invalid-key'));
assert.throws(() => {
process.permission.has('fs', {});
}, common.expectsError({
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "reference" argument must be of type string. Received an instance of Object',
}));
}