Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 35 additions & 5 deletions dist/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,38 @@ function setStatusBarText(what, docType) {
var text = what + " [" + docType + "] " + date.toLocaleTimeString();
vscode__WEBPACK_IMPORTED_MODULE_0__.window.setStatusBarMessage(text, 1500);
}
function parseShellArgs(input) {
const args = [];
let current = "";
let i = 0;
while (i < input.length) {
const ch = input[i];
if (ch === '"' || ch === "'") {
const quote = ch;
i++;
while (i < input.length && input[i] !== quote) {
current += input[i];
i++;
}
i++; // skip closing quote
}
else if (/\s/.test(ch)) {
if (current.length > 0) {
args.push(current);
current = "";
}
i++;
}
else {
current += ch;
i++;
}
}
if (current.length > 0) {
args.push(current);
}
return args;
}
function getPandocOptions(quickPickLabel) {
var pandocOptions;
switch (quickPickLabel) {
Expand Down Expand Up @@ -332,16 +364,14 @@ function renderDoc(filePath, fileName, fileNameOnly, format, extensionPath) {
args.push(filterPath + ":" + containerPath + ":ro");
});
if (dockerOptions) {
// dockerOptions is expected to be a string of options; split on whitespace.
// This preserves existing behavior while avoiding shell interpolation.
args = args.concat(dockerOptions.split(/\s+/).filter(Boolean));
args = args.concat(parseShellArgs(dockerOptions));
}
args.push(String(dockerImage));
args.push(fileName);
args.push("-o");
args.push(fileNameOnly + "." + format);
if (pandocOptions) {
args = args.concat(pandocOptions.split(/\s+/).filter(Boolean));
args = args.concat(parseShellArgs(pandocOptions));
}
luaFilterPaths.forEach((_filterPath, i) => {
args.push("--lua-filter");
Expand All @@ -354,7 +384,7 @@ function renderDoc(filePath, fileName, fileNameOnly, format, extensionPath) {
args.push("-o");
args.push(outFile);
if (pandocOptions) {
args = args.concat(pandocOptions.split(/\s+/).filter(Boolean));
args = args.concat(parseShellArgs(pandocOptions));
}
luaFilterPaths.forEach((filterPath) => {
args.push("--lua-filter");
Expand Down
39 changes: 34 additions & 5 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,37 @@ function setStatusBarText(what: string, docType: string) {
vscode.window.setStatusBarMessage(text, 1500);
}

function parseShellArgs(input: string): string[] {
const args: string[] = [];
let current = "";
let i = 0;
while (i < input.length) {
const ch = input[i];
if (ch === '"' || ch === "'") {
const quote = ch;
i++;
while (i < input.length && input[i] !== quote) {
current += input[i];
i++;
}
i++; // skip closing quote
Comment thread
ChrisChinchilla marked this conversation as resolved.
Outdated
} else if (/\s/.test(ch)) {
if (current.length > 0) {
args.push(current);
current = "";
}
i++;
} else {
current += ch;
i++;
}
}
if (current.length > 0) {
args.push(current);
}
Comment on lines +16 to +50

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new argument-splitting behavior is central to this change (quoted paths/options), but there are no tests asserting the resulting argv. Please add unit/integration tests that cover common cases like --resource-path="/a b", mixed quoting (e.g. --opt=foo"bar baz"), and Docker options containing quoted values, to prevent regressions across platforms.

Suggested change
let i = 0;
while (i < input.length) {
const ch = input[i];
if (ch === '"' || ch === "'") {
const quote = ch;
i++;
while (i < input.length && input[i] !== quote) {
current += input[i];
i++;
}
i++; // skip closing quote
} else if (/\s/.test(ch)) {
if (current.length > 0) {
args.push(current);
current = "";
}
i++;
} else {
current += ch;
i++;
}
}
if (current.length > 0) {
args.push(current);
}
let inSingleQuote = false;
let inDoubleQuote = false;
let tokenStarted = false;
for (let i = 0; i < input.length; i++) {
const ch = input[i];
if (ch === "\\" && !inSingleQuote) {
const next = input[i + 1];
if (next !== undefined) {
current += next;
tokenStarted = true;
i++;
} else {
current += ch;
tokenStarted = true;
}
continue;
}
if (ch === "'" && !inDoubleQuote) {
inSingleQuote = !inSingleQuote;
tokenStarted = true;
continue;
}
if (ch === '"' && !inSingleQuote) {
inDoubleQuote = !inDoubleQuote;
tokenStarted = true;
continue;
}
if (/\s/.test(ch) && !inSingleQuote && !inDoubleQuote) {
if (tokenStarted) {
args.push(current);
current = "";
tokenStarted = false;
}
continue;
}
current += ch;
tokenStarted = true;
}
if (tokenStarted) {
args.push(current);
}

Copilot uses AI. Check for mistakes.
return args;
}

function getPandocOptions(quickPickLabel: string) {
var pandocOptions;

Expand Down Expand Up @@ -304,16 +335,14 @@ function renderDoc(
args.push(filterPath + ":" + containerPath + ":ro");
});
if (dockerOptions) {
// dockerOptions is expected to be a string of options; split on whitespace.
// This preserves existing behavior while avoiding shell interpolation.
args = args.concat(dockerOptions.split(/\s+/).filter(Boolean));
args = args.concat(parseShellArgs(dockerOptions));
}
args.push(String(dockerImage));
args.push(fileName);
args.push("-o");
args.push(fileNameOnly + "." + format);
if (pandocOptions) {
args = args.concat(pandocOptions.split(/\s+/).filter(Boolean));
args = args.concat(parseShellArgs(pandocOptions));
}
luaFilterPaths.forEach((_filterPath, i) => {
args.push("--lua-filter");
Expand All @@ -325,7 +354,7 @@ function renderDoc(
args.push("-o");
args.push(outFile);
if (pandocOptions) {
args = args.concat(pandocOptions.split(/\s+/).filter(Boolean));
args = args.concat(parseShellArgs(pandocOptions));
}
luaFilterPaths.forEach((filterPath) => {
args.push("--lua-filter");
Expand Down
Loading