mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-08-23 20:04:05 +02:00
fix(copy-as): emit shell-safe arguments in the curl and gRPCurl exporters (#593)
This commit is contained in:
@@ -83,7 +83,9 @@ export async function convertToCurl(request: Partial<HttpRequest>) {
|
||||
if (p.file) {
|
||||
let v = `${p.name}=@${p.file}`;
|
||||
v += p.contentType ? `;type=${p.contentType}` : "";
|
||||
xs.push(flag, v);
|
||||
// A bare `;` separates commands and a path can hold spaces, so this
|
||||
// argument needs quoting like every other one.
|
||||
xs.push(flag, quote(v));
|
||||
} else {
|
||||
xs.push(flag, quote(`${p.name}=${p.value}`));
|
||||
}
|
||||
@@ -157,7 +159,10 @@ export async function convertToCurl(request: Partial<HttpRequest>) {
|
||||
}
|
||||
|
||||
function quote(arg: string): string {
|
||||
const escaped = arg.replace(/'/g, "\\'");
|
||||
// A single-quoted POSIX string takes no escapes, so `\'` does not close it:
|
||||
// the string ends one character early and the rest of the command is left
|
||||
// dangling. Step out of the quotes, emit an escaped quote, step back in.
|
||||
const escaped = arg.replace(/'/g, `'\\''`);
|
||||
return `'${escaped}'`;
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ describe("exporter-curl", () => {
|
||||
`curl -X PUT 'https://yaak.app'`,
|
||||
`--form 'a=aaa'`,
|
||||
`--form 'b=bbb'`,
|
||||
"--form f=@/foo/bar.png;type=image/png",
|
||||
"--form 'f=@/foo/bar.png;type=image/png'",
|
||||
].join(" \\\n "),
|
||||
);
|
||||
});
|
||||
@@ -140,11 +140,48 @@ describe("exporter-curl", () => {
|
||||
[
|
||||
`curl -X POST 'https://yaak.app'`,
|
||||
`--header 'Content-Type: application/json'`,
|
||||
`--data '{"foo":"bar\\'s"}'`,
|
||||
`--data '{"foo":"bar'\\''s"}'`,
|
||||
].join(" \\\n "),
|
||||
);
|
||||
});
|
||||
|
||||
test("Quotes an apostrophe so the command still parses", async () => {
|
||||
// POSIX single quotes take no escapes: `\'` ends the string one
|
||||
// character early and everything after it is left dangling, so the copied
|
||||
// command is a syntax error rather than a request.
|
||||
const command = await convertToCurl({
|
||||
url: "https://yaak.app/it's",
|
||||
method: "POST",
|
||||
bodyType: "application/json",
|
||||
body: { text: `{"note":"don't stop"}` },
|
||||
headers: [{ name: "X-Note", value: "it's fine" }],
|
||||
});
|
||||
|
||||
expect(command).toEqual(
|
||||
[
|
||||
`curl -X POST 'https://yaak.app/it'\\''s'`,
|
||||
`--header 'X-Note: it'\\''s fine'`,
|
||||
`--data '{"note":"don'\\''t stop"}'`,
|
||||
].join(" \\\n "),
|
||||
);
|
||||
});
|
||||
|
||||
test("Quotes a file form field so its type suffix survives", async () => {
|
||||
// A bare `;` separates commands, so an unquoted `f=@x.png;type=image/png`
|
||||
// reaches curl as `f=@x.png` and the rest runs as its own command.
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: "https://yaak.app",
|
||||
method: "POST",
|
||||
bodyType: "multipart/form-data",
|
||||
body: { form: [{ name: "f", file: "/my files/a.png", contentType: "image/png" }] },
|
||||
}),
|
||||
).toEqual(
|
||||
[`curl -X POST 'https://yaak.app'`, `--form 'f=@/my files/a.png;type=image/png'`].join(
|
||||
" \\\n ",
|
||||
),
|
||||
);
|
||||
});
|
||||
test("Exports multi-line JSON body", async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
|
||||
@@ -129,7 +129,10 @@ export async function convert(request: Partial<GrpcRequest>, allProtoFiles: stri
|
||||
}
|
||||
|
||||
function quote(arg: string): string {
|
||||
const escaped = arg.replace(/'/g, "\\'");
|
||||
// A single-quoted POSIX string takes no escapes, so `\'` does not close it:
|
||||
// the string ends one character early and the rest of the command is left
|
||||
// dangling. Step out of the quotes, emit an escaped quote, step back in.
|
||||
const escaped = arg.replace(/'/g, `'\\''`);
|
||||
return `'${escaped}'`;
|
||||
}
|
||||
|
||||
|
||||
@@ -175,4 +175,21 @@ describe("exporter-curl", () => {
|
||||
].join(" \\\n "),
|
||||
);
|
||||
});
|
||||
|
||||
test("Quotes an apostrophe so the command still parses", async () => {
|
||||
// POSIX single quotes take no escapes: `\'` ends the string one
|
||||
// character early and leaves the rest of the command dangling.
|
||||
const command = await convert(
|
||||
{
|
||||
url: "https://yaak.app",
|
||||
service: "Service",
|
||||
method: "Method",
|
||||
message: `{"note":"don't stop"}`,
|
||||
metadata: [{ name: "x-note", value: "it's fine" }],
|
||||
},
|
||||
[],
|
||||
);
|
||||
expect(command).toContain(`'{"note":"don'\\''t stop"}'`);
|
||||
expect(command).toContain(`'x-note: it'\\''s fine'`);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user