GeezerVison is showing github.com — permission: do not create symlinks if target is relative · nodejs/node@3fdecc4
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/fs.js +22Lines changed: 22 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -58,6 +58,7 @@ const { | 58 | 58 | } = constants; | 59 | 59 |
| 60 | 60 | const pathModule = require('path'); | | 61 | +const { isAbsolute } = pathModule; | 61 | 62 | const { isArrayBufferView } = require('internal/util/types'); | 62 | 63 |
| 63 | 64 | const binding = internalBinding('fs'); | @@ -68,6 +69,7 @@ const { Buffer } = require('buffer'); | 68 | 69 | const { | 69 | 70 | aggregateTwoErrors, | 70 | 71 | codes: { | | 72 | + ERR_ACCESS_DENIED, | 71 | 73 | ERR_FS_FILE_TOO_LARGE, | 72 | 74 | ERR_INVALID_ARG_VALUE, | 73 | 75 | }, | @@ -143,6 +145,8 @@ const { | 143 | 145 | kValidateObjectAllowNullable, | 144 | 146 | } = require('internal/validators'); | 145 | 147 |
| | 148 | +const permission = require('internal/process/permission'); | | 149 | + | 146 | 150 | let truncateWarn = true; | 147 | 151 | let fs; | 148 | 152 |
| @@ -1742,6 +1746,15 @@ function symlink(target, path, type_, callback_) { | 1742 | 1746 | const type = (typeof type_ === 'string' ? type_ : null); | 1743 | 1747 | const callback = makeCallback(arguments[arguments.length - 1]); | 1744 | 1748 |
| | 1749 | + if (permission.isEnabled()) { | | 1750 | + // The permission model's security guarantees fall apart in the presence of | | 1751 | + // relative symbolic links. Thus, we have to prevent their creation. | | 1752 | + if (typeof target !== 'string' || !isAbsolute(toPathIfFileURL(target))) { | | 1753 | + callback(new ERR_ACCESS_DENIED('relative symbolic link target')); | | 1754 | + return; | | 1755 | + } | | 1756 | + } | | 1757 | + | 1745 | 1758 | target = getValidatedPath(target, 'target'); | 1746 | 1759 | path = getValidatedPath(path); | 1747 | 1760 |
| @@ -1798,6 +1811,15 @@ function symlinkSync(target, path, type) { | 1798 | 1811 | type = 'dir'; | 1799 | 1812 | } | 1800 | 1813 | } | | 1814 | + | | 1815 | + if (permission.isEnabled()) { | | 1816 | + // The permission model's security guarantees fall apart in the presence of | | 1817 | + // relative symbolic links. Thus, we have to prevent their creation. | | 1818 | + if (typeof target !== 'string' || !isAbsolute(toPathIfFileURL(target))) { | | 1819 | + throw new ERR_ACCESS_DENIED('relative symbolic link target'); | | 1820 | + } | | 1821 | + } | | 1822 | + | 1801 | 1823 | target = getValidatedPath(target, 'target'); | 1802 | 1824 | path = getValidatedPath(path); | 1803 | 1825 |
| |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/fs/promises.js +14Lines changed: 14 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -33,6 +33,7 @@ const { Buffer } = require('buffer'); | 33 | 33 |
| 34 | 34 | const { | 35 | 35 | codes: { | | 36 | + ERR_ACCESS_DENIED, | 36 | 37 | ERR_FS_FILE_TOO_LARGE, | 37 | 38 | ERR_INVALID_ARG_VALUE, | 38 | 39 | ERR_INVALID_STATE, | @@ -86,6 +87,8 @@ const { | 86 | 87 | kValidateObjectAllowNullable, | 87 | 88 | } = require('internal/validators'); | 88 | 89 | const pathModule = require('path'); | | 90 | +const { isAbsolute } = pathModule; | | 91 | +const { toPathIfFileURL } = require('internal/url'); | 89 | 92 | const { | 90 | 93 | kEmptyObject, | 91 | 94 | lazyDOMException, | @@ -98,6 +101,8 @@ const nonNativeWatcher = require('internal/fs/recursive_watch'); | 98 | 101 | const { isIterable } = require('internal/streams/utils'); | 99 | 102 | const assert = require('internal/assert'); | 100 | 103 |
| | 104 | +const permission = require('internal/process/permission'); | | 105 | + | 101 | 106 | const kHandle = Symbol('kHandle'); | 102 | 107 | const kFd = Symbol('kFd'); | 103 | 108 | const kRefs = Symbol('kRefs'); | @@ -974,6 +979,15 @@ async function symlink(target, path, type_) { | 974 | 979 | type = 'file'; | 975 | 980 | } | 976 | 981 | } | | 982 | + | | 983 | + if (permission.isEnabled()) { | | 984 | + // The permission model's security guarantees fall apart in the presence of | | 985 | + // relative symbolic links. Thus, we have to prevent their creation. | | 986 | + if (typeof target !== 'string' || !isAbsolute(toPathIfFileURL(target))) { | | 987 | + throw new ERR_ACCESS_DENIED('relative symbolic link target'); | | 988 | + } | | 989 | + } | | 990 | + | 977 | 991 | target = getValidatedPath(target, 'target'); | 978 | 992 | path = getValidatedPath(path); | 979 | 993 | return await PromisePrototypeThen( | |
Collapse file Copy file name to clipboard +27Lines changed: 27 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +// Flags: --experimental-permission --allow-fs-read=* --allow-fs-write=* | | 2 | +'use strict'; | | 3 | + | | 4 | +const common = require('../common'); | | 5 | +common.skipIfWorker(); | | 6 | + | | 7 | +const assert = require('assert'); | | 8 | +const { symlinkSync, symlink, promises: { symlink: symlinkAsync } } = require('fs'); | | 9 | + | | 10 | +const error = { | | 11 | + code: 'ERR_ACCESS_DENIED', | | 12 | + message: /relative symbolic link target/, | | 13 | +}; | | 14 | + | | 15 | +for (const targetString of ['a', './b/c', '../d', 'e/../f', 'C:drive-relative', 'ntfs:alternate']) { | | 16 | + for (const target of [targetString, Buffer.from(targetString)]) { | | 17 | + for (const path of [__filename, __dirname, process.execPath]) { | | 18 | + assert.throws(() => symlinkSync(target, path), error); | | 19 | + symlink(target, path, common.mustCall((err) => { | | 20 | + assert(err); | | 21 | + assert.strictEqual(err.code, error.code); | | 22 | + assert.match(err.message, error.message); | | 23 | + })); | | 24 | + assert.rejects(() => symlinkAsync(target, path), error).then(common.mustCall()); | | 25 | + } | | 26 | + } | | 27 | +} |
|