mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-13 11:46:08 -05:00
Merge branch 'main' into tsgo-port
This commit is contained in:
@@ -327,7 +327,8 @@ export const enum NameValidationResult {
|
||||
NameTooLong,
|
||||
NameStartsWithDot,
|
||||
NameStartsWithUnderscore,
|
||||
NameContainsNonURISafeCharacters,
|
||||
NameContainsInvalidCharacters,
|
||||
NameContainsNonURISafeCharacters = NameContainsInvalidCharacters, // for backward compatibility
|
||||
}
|
||||
|
||||
const maxPackageNameLength = 214;
|
||||
@@ -381,8 +382,8 @@ function validatePackageNameWorker(packageName: string, supportScopedPackage: bo
|
||||
return NameValidationResult.Ok;
|
||||
}
|
||||
}
|
||||
if (encodeURIComponent(packageName) !== packageName) {
|
||||
return NameValidationResult.NameContainsNonURISafeCharacters;
|
||||
if (!/^[\w.-]+$/.test(packageName)) {
|
||||
return NameValidationResult.NameContainsInvalidCharacters;
|
||||
}
|
||||
return NameValidationResult.Ok;
|
||||
}
|
||||
@@ -405,8 +406,8 @@ function renderPackageNameValidationFailureWorker(typing: string, result: NameVa
|
||||
return `'${typing}':: ${kind} name '${name}' cannot start with '.'`;
|
||||
case NameValidationResult.NameStartsWithUnderscore:
|
||||
return `'${typing}':: ${kind} name '${name}' cannot start with '_'`;
|
||||
case NameValidationResult.NameContainsNonURISafeCharacters:
|
||||
return `'${typing}':: ${kind} name '${name}' contains non URI safe characters`;
|
||||
case NameValidationResult.NameContainsInvalidCharacters:
|
||||
return `'${typing}':: ${kind} name '${name}' contains invalid characters`;
|
||||
case NameValidationResult.Ok:
|
||||
return Debug.fail(); // Shouldn't have called this.
|
||||
default:
|
||||
|
||||
4
src/lib/es5.d.ts
vendored
4
src/lib/es5.d.ts
vendored
@@ -400,8 +400,8 @@ interface String {
|
||||
charAt(pos: number): string;
|
||||
|
||||
/**
|
||||
* Returns the Unicode value of the character at the specified location.
|
||||
* @param index The zero-based index of the desired character. If there is no character at the specified index, NaN is returned.
|
||||
* Returns the Unicode value of the character at the specified location, or NaN if the index is out of bounds.
|
||||
* @param index The zero-based index of the desired character.
|
||||
*/
|
||||
charCodeAt(index: number): number;
|
||||
|
||||
|
||||
@@ -56,4 +56,24 @@ describe("unittests:: tsserver:: codeFix::", () => {
|
||||
});
|
||||
baselineTsserverLogs("codeFix", "install package when serialized", session);
|
||||
});
|
||||
|
||||
it("install package rejects invalid package names", () => {
|
||||
const { host, session } = setup();
|
||||
// A client could craft an applyCodeActionCommand with arbitrary package names.
|
||||
// The server must validate and reject names with invalid characters to prevent shell injection.
|
||||
for (const packageName of ["; echo 'hello' #", "react'test", "a/b/c"]) {
|
||||
session.executeCommandSeq<ts.server.protocol.ApplyCodeActionCommandRequest>({
|
||||
command: ts.server.protocol.CommandTypes.ApplyCodeActionCommand,
|
||||
arguments: {
|
||||
command: {
|
||||
type: "install package",
|
||||
file: "/home/src/projects/project/src/file.ts",
|
||||
packageName,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
host.runPendingInstalls();
|
||||
baselineTsserverLogs("codeFix", "install package rejects invalid package names", session);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1524,10 +1524,11 @@ describe("unittests:: tsserver:: typingsInstaller:: Validate package name:", ()
|
||||
it("package name cannot start with underscore", () => {
|
||||
assert.equal(validatePackageName("_foo"), NameValidationResult.NameStartsWithUnderscore);
|
||||
});
|
||||
it("package non URI safe characters are not supported", () => {
|
||||
assert.equal(validatePackageName(" scope "), NameValidationResult.NameContainsNonURISafeCharacters);
|
||||
assert.equal(validatePackageName("; say ‘Hello from TypeScript!’ #"), NameValidationResult.NameContainsNonURISafeCharacters);
|
||||
assert.equal(validatePackageName("a/b/c"), NameValidationResult.NameContainsNonURISafeCharacters);
|
||||
it("package invalid characters are not supported", () => {
|
||||
assert.equal(validatePackageName(" scope "), NameValidationResult.NameContainsInvalidCharacters);
|
||||
assert.equal(validatePackageName("; say ‘Hello from TypeScript!’ #"), NameValidationResult.NameContainsInvalidCharacters);
|
||||
assert.equal(validatePackageName("a/b/c"), NameValidationResult.NameContainsInvalidCharacters);
|
||||
assert.equal(validatePackageName("react'test"), NameValidationResult.NameContainsInvalidCharacters);
|
||||
});
|
||||
it("scoped package name is supported", () => {
|
||||
assert.equal(validatePackageName("@scope/bar"), NameValidationResult.Ok);
|
||||
@@ -1540,10 +1541,10 @@ describe("unittests:: tsserver:: typingsInstaller:: Validate package name:", ()
|
||||
assert.deepEqual(validatePackageName("@_scope/bar"), { name: "_scope", isScopeName: true, result: NameValidationResult.NameStartsWithUnderscore });
|
||||
assert.deepEqual(validatePackageName("@_scope/_bar"), { name: "_scope", isScopeName: true, result: NameValidationResult.NameStartsWithUnderscore });
|
||||
});
|
||||
it("scope name in scoped package name with non URI safe characters are not supported", () => {
|
||||
assert.deepEqual(validatePackageName("@ scope /bar"), { name: " scope ", isScopeName: true, result: NameValidationResult.NameContainsNonURISafeCharacters });
|
||||
assert.deepEqual(validatePackageName("@; say ‘Hello from TypeScript!’ #/bar"), { name: "; say ‘Hello from TypeScript!’ #", isScopeName: true, result: NameValidationResult.NameContainsNonURISafeCharacters });
|
||||
assert.deepEqual(validatePackageName("@ scope / bar "), { name: " scope ", isScopeName: true, result: NameValidationResult.NameContainsNonURISafeCharacters });
|
||||
it("scope name in scoped package name with invalid characters are not supported", () => {
|
||||
assert.deepEqual(validatePackageName("@ scope /bar"), { name: " scope ", isScopeName: true, result: NameValidationResult.NameContainsInvalidCharacters });
|
||||
assert.deepEqual(validatePackageName("@; say ‘Hello from TypeScript!’ #/bar"), { name: "; say ‘Hello from TypeScript!’ #", isScopeName: true, result: NameValidationResult.NameContainsInvalidCharacters });
|
||||
assert.deepEqual(validatePackageName("@ scope / bar "), { name: " scope ", isScopeName: true, result: NameValidationResult.NameContainsInvalidCharacters });
|
||||
});
|
||||
it("package name in scoped package name cannot start with dot", () => {
|
||||
assert.deepEqual(validatePackageName("@scope/.bar"), { name: ".bar", isScopeName: false, result: NameValidationResult.NameStartsWithDot });
|
||||
@@ -1551,9 +1552,9 @@ describe("unittests:: tsserver:: typingsInstaller:: Validate package name:", ()
|
||||
it("package name in scoped package name cannot start with underscore", () => {
|
||||
assert.deepEqual(validatePackageName("@scope/_bar"), { name: "_bar", isScopeName: false, result: NameValidationResult.NameStartsWithUnderscore });
|
||||
});
|
||||
it("package name in scoped package name with non URI safe characters are not supported", () => {
|
||||
assert.deepEqual(validatePackageName("@scope/ bar "), { name: " bar ", isScopeName: false, result: NameValidationResult.NameContainsNonURISafeCharacters });
|
||||
assert.deepEqual(validatePackageName("@scope/; say ‘Hello from TypeScript!’ #"), { name: "; say ‘Hello from TypeScript!’ #", isScopeName: false, result: NameValidationResult.NameContainsNonURISafeCharacters });
|
||||
it("package name in scoped package name with invalid characters are not supported", () => {
|
||||
assert.deepEqual(validatePackageName("@scope/ bar "), { name: " bar ", isScopeName: false, result: NameValidationResult.NameContainsInvalidCharacters });
|
||||
assert.deepEqual(validatePackageName("@scope/; say ‘Hello from TypeScript!’ #"), { name: "; say ‘Hello from TypeScript!’ #", isScopeName: false, result: NameValidationResult.NameContainsInvalidCharacters });
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -239,6 +239,22 @@ export abstract class TypingsInstaller {
|
||||
/** @internal */
|
||||
installPackage(req: InstallPackageRequest): void {
|
||||
const { fileName, packageName, projectName, projectRootPath, id } = req;
|
||||
const validationResult = JsTyping.validatePackageName(packageName);
|
||||
if (validationResult !== JsTyping.NameValidationResult.Ok) {
|
||||
const message = JsTyping.renderPackageNameValidationFailure(validationResult, packageName);
|
||||
if (this.log.isEnabled()) {
|
||||
this.log.writeLine(message);
|
||||
}
|
||||
const response: PackageInstalledResponse = {
|
||||
kind: ActionPackageInstalled,
|
||||
projectName,
|
||||
id,
|
||||
success: false,
|
||||
message,
|
||||
};
|
||||
this.sendResponse(response);
|
||||
return;
|
||||
}
|
||||
const cwd = forEachAncestorDirectory(getDirectoryPath(fileName), directory => {
|
||||
if (this.installTypingHost.fileExists(combinePaths(directory, "package.json"))) {
|
||||
return directory;
|
||||
|
||||
Reference in New Issue
Block a user