module: refine package name validation
PR-URL: https://github.com/nodejs/node/pull/28965 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Jan Krems <jan.krems@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
parent
71c28a4d2b
commit
0e03c449e3
@ -700,15 +700,17 @@ _isMain_ is **true** when resolving the Node.js application entry point.
|
|||||||
> 1. Throw an _Invalid Specifier_ error.
|
> 1. Throw an _Invalid Specifier_ error.
|
||||||
> 1. Set _packageName_ to the substring of _packageSpecifier_
|
> 1. Set _packageName_ to the substring of _packageSpecifier_
|
||||||
> until the second _"/"_ separator or the end of the string.
|
> until the second _"/"_ separator or the end of the string.
|
||||||
> 1. Let _packageSubpath_ be the substring of _packageSpecifier_ from the
|
> 1. If _packageName_ starts with _"."_ or contains _"\\"_ or _"%"_, then
|
||||||
> position at the length of _packageName_ plus one, if any.
|
> 1. Throw an _Invalid Specifier_ error.
|
||||||
> 1. Assert: _packageName_ is a valid package name or scoped package name.
|
> 1. Let _packageSubpath_ be _undefined_.
|
||||||
> 1. Assert: _packageSubpath_ is either empty, or a path without a leading
|
> 1. If the length of _packageSpecifier_ is greater than the length of
|
||||||
> separator.
|
> _packageName_, then
|
||||||
|
> 1. Set _packageSubpath_ to _"."_ concatenated with the substring of
|
||||||
|
> _packageSpecifier_ from the position at the length of _packageName_.
|
||||||
> 1. If _packageSubpath_ contains any _"."_ or _".."_ segments or percent
|
> 1. If _packageSubpath_ contains any _"."_ or _".."_ segments or percent
|
||||||
> encoded strings for _"/"_ or _"\\"_ then,
|
> encoded strings for _"/"_ or _"\\"_ then,
|
||||||
> 1. Throw an _Invalid Specifier_ error.
|
> 1. Throw an _Invalid Specifier_ error.
|
||||||
> 1. If _packageSubpath_ is empty and _packageName_ is a Node.js builtin
|
> 1. If _packageSubpath_ is _undefined_ and _packageName_ is a Node.js builtin
|
||||||
> module, then
|
> module, then
|
||||||
> 1. Return the string _"node:"_ concatenated with _packageSpecifier_.
|
> 1. Return the string _"node:"_ concatenated with _packageSpecifier_.
|
||||||
> 1. While _parentURL_ is not the file system root,
|
> 1. While _parentURL_ is not the file system root,
|
||||||
@ -719,7 +721,7 @@ _isMain_ is **true** when resolving the Node.js application entry point.
|
|||||||
> 1. Set _parentURL_ to the parent URL path of _parentURL_.
|
> 1. Set _parentURL_ to the parent URL path of _parentURL_.
|
||||||
> 1. Continue the next loop iteration.
|
> 1. Continue the next loop iteration.
|
||||||
> 1. Let _pjson_ be the result of **READ_PACKAGE_JSON**(_packageURL_).
|
> 1. Let _pjson_ be the result of **READ_PACKAGE_JSON**(_packageURL_).
|
||||||
> 1. If _packageSubpath_ is empty, then
|
> 1. If _packageSubpath_ is _undefined__, then
|
||||||
> 1. Return the result of **PACKAGE_MAIN_RESOLVE**(_packageURL_,
|
> 1. Return the result of **PACKAGE_MAIN_RESOLVE**(_packageURL_,
|
||||||
> _pjson_).
|
> _pjson_).
|
||||||
> 1. Otherwise,
|
> 1. Otherwise,
|
||||||
@ -727,8 +729,8 @@ _isMain_ is **true** when resolving the Node.js application entry point.
|
|||||||
> 1. Let _exports_ be _pjson.exports_.
|
> 1. Let _exports_ be _pjson.exports_.
|
||||||
> 1. If _exports_ is not **null** or **undefined**, then
|
> 1. If _exports_ is not **null** or **undefined**, then
|
||||||
> 1. Return **PACKAGE_EXPORTS_RESOLVE**(_packageURL_,
|
> 1. Return **PACKAGE_EXPORTS_RESOLVE**(_packageURL_,
|
||||||
> _packagePath_, _pjson.exports_).
|
> _packageSubpath_, _pjson.exports_).
|
||||||
> 1. Return the URL resolution of _packagePath_ in _packageURL_.
|
> 1. Return the URL resolution of _packageSubpath_ in _packageURL_.
|
||||||
> 1. Throw a _Module Not Found_ error.
|
> 1. Throw a _Module Not Found_ error.
|
||||||
|
|
||||||
**PACKAGE_MAIN_RESOLVE**(_packageURL_, _pjson_)
|
**PACKAGE_MAIN_RESOLVE**(_packageURL_, _pjson_)
|
||||||
|
@ -336,7 +336,7 @@ function findLongestRegisteredExtension(filename) {
|
|||||||
// This only applies to requests of a specific form:
|
// This only applies to requests of a specific form:
|
||||||
// 1. name/.*
|
// 1. name/.*
|
||||||
// 2. @scope/name/.*
|
// 2. @scope/name/.*
|
||||||
const EXPORTS_PATTERN = /^((?:@[^./@\\][^/@\\]*\/)?[^@./\\][^/\\]*)(\/.*)$/;
|
const EXPORTS_PATTERN = /^((?:@[^/\\%]+\/)?[^./\\%][^/\\%]*)(\/.*)$/;
|
||||||
function resolveExports(nmPath, request, absoluteRequest) {
|
function resolveExports(nmPath, request, absoluteRequest) {
|
||||||
// The implementation's behavior is meant to mirror resolution in ESM.
|
// The implementation's behavior is meant to mirror resolution in ESM.
|
||||||
if (experimentalExports && !absoluteRequest) {
|
if (experimentalExports && !absoluteRequest) {
|
||||||
|
@ -867,20 +867,35 @@ Maybe<URL> PackageResolve(Environment* env,
|
|||||||
const std::string& specifier,
|
const std::string& specifier,
|
||||||
const URL& base) {
|
const URL& base) {
|
||||||
size_t sep_index = specifier.find('/');
|
size_t sep_index = specifier.find('/');
|
||||||
if (specifier[0] == '@' && (sep_index == std::string::npos ||
|
bool valid_package_name = true;
|
||||||
specifier.length() == 0)) {
|
bool scope = false;
|
||||||
|
if (specifier[0] == '@') {
|
||||||
|
scope = true;
|
||||||
|
if (sep_index == std::string::npos || specifier.length() == 0) {
|
||||||
|
valid_package_name = false;
|
||||||
|
} else {
|
||||||
|
sep_index = specifier.find('/', sep_index + 1);
|
||||||
|
}
|
||||||
|
} else if (specifier[0] == '.') {
|
||||||
|
valid_package_name = false;
|
||||||
|
}
|
||||||
|
std::string pkg_name = specifier.substr(0,
|
||||||
|
sep_index == std::string::npos ? std::string::npos : sep_index);
|
||||||
|
// Package name cannot have leading . and cannot have percent-encoding or
|
||||||
|
// separators.
|
||||||
|
for (size_t i = 0; i < pkg_name.length(); i++) {
|
||||||
|
char c = pkg_name[i];
|
||||||
|
if (c == '%' || c == '\\') {
|
||||||
|
valid_package_name = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!valid_package_name) {
|
||||||
std::string msg = "Invalid package name '" + specifier +
|
std::string msg = "Invalid package name '" + specifier +
|
||||||
"' imported from " + base.ToFilePath();
|
"' imported from " + base.ToFilePath();
|
||||||
node::THROW_ERR_INVALID_MODULE_SPECIFIER(env, msg.c_str());
|
node::THROW_ERR_INVALID_MODULE_SPECIFIER(env, msg.c_str());
|
||||||
return Nothing<URL>();
|
return Nothing<URL>();
|
||||||
}
|
}
|
||||||
bool scope = false;
|
|
||||||
if (specifier[0] == '@') {
|
|
||||||
scope = true;
|
|
||||||
sep_index = specifier.find('/', sep_index + 1);
|
|
||||||
}
|
|
||||||
std::string pkg_name = specifier.substr(0,
|
|
||||||
sep_index == std::string::npos ? std::string::npos : sep_index);
|
|
||||||
std::string pkg_subpath;
|
std::string pkg_subpath;
|
||||||
if ((sep_index == std::string::npos ||
|
if ((sep_index == std::string::npos ||
|
||||||
sep_index == specifier.length() - 1)) {
|
sep_index == specifier.length() - 1)) {
|
||||||
|
18
test/es-module/test-esm-pkgname.mjs
Normal file
18
test/es-module/test-esm-pkgname.mjs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Flags: --experimental-modules
|
||||||
|
|
||||||
|
import { mustCall } from '../common/index.mjs';
|
||||||
|
import { strictEqual } from 'assert';
|
||||||
|
|
||||||
|
import { importFixture } from '../fixtures/pkgexports.mjs';
|
||||||
|
|
||||||
|
importFixture('as%2Ff').catch(mustCall((err) => {
|
||||||
|
strictEqual(err.code, 'ERR_INVALID_MODULE_SPECIFIER');
|
||||||
|
}));
|
||||||
|
|
||||||
|
importFixture('as\\df').catch(mustCall((err) => {
|
||||||
|
strictEqual(err.code, 'ERR_INVALID_MODULE_SPECIFIER');
|
||||||
|
}));
|
||||||
|
|
||||||
|
importFixture('@as@df').catch(mustCall((err) => {
|
||||||
|
strictEqual(err.code, 'ERR_INVALID_MODULE_SPECIFIER');
|
||||||
|
}));
|
Loading…
x
Reference in New Issue
Block a user