Skip to content

Commit eeae9dd

Browse files
🤖 Pick PR #63401 (Also check package name validity in...) into release-6.0 (#63407)
Co-authored-by: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
1 parent ad1c695 commit eeae9dd

File tree

3 files changed

+474
-0
lines changed

3 files changed

+474
-0
lines changed

‎src/testRunner/unittests/tsserver/codeFix.ts‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,24 @@ describe("unittests:: tsserver:: codeFix::", () => {
5656
});
5757
baselineTsserverLogs("codeFix", "install package when serialized", session);
5858
});
59+
60+
it("install package rejects invalid package names", () => {
61+
const { host, session } = setup();
62+
// A client could craft an applyCodeActionCommand with arbitrary package names.
63+
// The server must validate and reject names with invalid characters to prevent shell injection.
64+
for (const packageName of ["; echo 'hello' #", "react'test", "a/b/c"]) {
65+
session.executeCommandSeq<ts.server.protocol.ApplyCodeActionCommandRequest>({
66+
command: ts.server.protocol.CommandTypes.ApplyCodeActionCommand,
67+
arguments: {
68+
command: {
69+
type: "install package",
70+
file: "/home/src/projects/project/src/file.ts",
71+
packageName,
72+
},
73+
},
74+
});
75+
}
76+
host.runPendingInstalls();
77+
baselineTsserverLogs("codeFix", "install package rejects invalid package names", session);
78+
});
5979
});

‎src/typingsInstallerCore/typingsInstaller.ts‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,22 @@ export abstract class TypingsInstaller {
239239
/** @internal */
240240
installPackage(req: InstallPackageRequest): void {
241241
const { fileName, packageName, projectName, projectRootPath, id } = req;
242+
const validationResult = JsTyping.validatePackageName(packageName);
243+
if (validationResult !== JsTyping.NameValidationResult.Ok) {
244+
const message = JsTyping.renderPackageNameValidationFailure(validationResult, packageName);
245+
if (this.log.isEnabled()) {
246+
this.log.writeLine(message);
247+
}
248+
const response: PackageInstalledResponse = {
249+
kind: ActionPackageInstalled,
250+
projectName,
251+
id,
252+
success: false,
253+
message,
254+
};
255+
this.sendResponse(response);
256+
return;
257+
}
242258
const cwd = forEachAncestorDirectory(getDirectoryPath(fileName), directory => {
243259
if (this.installTypingHost.fileExists(combinePaths(directory, "package.json"))) {
244260
return directory;

0 commit comments

Comments
 (0)