CVE-2021-21298 (JavaScript, published 2021-02-26T17:15Z)
Description: value: Node-Red is a low-code programming for event-driven applications built using nodejs. Node-RED 1.2.7 and earlier has a vulnerability which allows arbitrary path traversal via the Projects API. If the Projects feature is enabled, a user with `projects.read` permission is able to access any file via the Projects API. The issue has been patched in Node-RED 1.2.8. The vulnerability applies only to the Projects feature which is not enabled by default in Node-RED. The primary workaround is not give untrusted users read access to the Node-RED editor.
Severity: LOW (CVSS v3 base score: 6.5)
Weakness: CWE-22 \u2014 Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
Repository: https://github.com/node-red/node-red
Files changed:
packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/Project.js
packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/index.js
Commit message: Restrict project file access to inside the project directory


Vulnerable code:
    projectsDir = fspath.join(settings.userDir,"projects");
                    projectsDir = fspath.join(settings.userDir,"projects");
        projectPath = fspath.join(projectsDir,name);

Fixed code:
            if (/^\.\./.test(fspath.relative(this.path,fspath.join(this.path,data.files.package)))) {
                return Promise.reject("Invalid package file: "+data.files.package)
            }
            if (/^\.\./.test(fspath.relative(this.path,fspath.join(this.path,data.files.flow)))) {
                return Promise.reject("Invalid flow file: "+data.files.flow)
            }
            if (/^\.\./.test(fspath.relative(this.path,fspath.join(this.path,data.files.credentials)))) {
                return Promise.reject("Invalid credentials file: "+data.files.credentials)
            }
        let fullPath = fspath.join(this.path,filePath);
        if (/^\.\./.test(fspath.relative(this.path,fullPath))) {
            throw new Error("Invalid file name")
        }

    if (/^\.\./.test(fspath.relative(this.path,filePath))) {
        throw new Error("Invalid file name")
    }

    projectsDir = fspath.resolve(fspath.join(settings.userDir,"projects"));
                    projectsDir = fspath.resolve(fspath.join(settings.userDir,"projects"));
    let fullPath = fspath.resolve(fspath.join(projectsDir,name));
        projectPath = fullPath;
    } else {
        // Ensure this project dir is under projectsDir;
        let relativePath = fspath.relative(projectsDir,fullPath);
        if (/^\.\./.test(relativePath)) {
            throw new Error("Invalid project name")
        }
    let relativePath = fspath.relative(projectsDir,projectPath);
    if (/^\.\./.test(relativePath)) {
        throw new Error("Invalid project name")
    }
    if (/^\.\./.test(fspath.relative(projectsDir,metadata.path))) {
        throw new Error("Invalid project name")
    }