Skip to content

Commit

Permalink
feat: character encoding is now only applied in the output formatter …
Browse files Browse the repository at this point in the history
…when strings are wrapped with `\"\"` double quotes

fix: fixed raw escape character encoding on `outputFormatter`

fix: encoding of quotes in `outputFormatter` is now correctly escaped

[ci-skip]
  • Loading branch information
amydevs committed Nov 8, 2023
1 parent ec8d459 commit 4015fcf
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 17 deletions.
2 changes: 1 addition & 1 deletion src/secrets/CommandGet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class CommandGet extends CommandPolykey {
const secretContent = Buffer.from(response.secretContent, 'binary');
const outputFormatted = binUtils.outputFormatter({
type: 'raw',
data: secretContent,
data: `"${binUtils.encodeQuotes(secretContent.toString('utf-8'))}"`, // Encodes any secret content with escape characters
});
process.stdout.write(outputFormatted);
} finally {
Expand Down
46 changes: 37 additions & 9 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,22 @@ function standardErrorReplacer(key: string, value: any) {
return value;
}

/**
* This function calls encodeNonPrintable on substrings within the input enclosed with `""`.
* Any usage of `\\"` within `""` will escape it.
*
* @param str
* @see {@link encodeNonPrintable}
* @returns
*/
function encodeWrappedStrings(str: string) {
return str.replace(/"(\\.|[^"])*"/g, encodeNonPrintable);
}

function encodeQuotes(str: string): string {
return str.replace(/[`"']/g, (char) => `\\` + char);
}

/**
* This function:
* 1. Keeps regular spaces, only ' ', as they are.
Expand Down Expand Up @@ -100,6 +116,7 @@ function encodeNonPrintable(str: string): string {
*
* @param msg - The msg that needs to be formatted.
* @see {@link outputFormatterTable} for information regarding usage where `msg.type === 'table'`.
* @see {@link encodeWrappedStrings} for information regarding wrapping strings with `""` for encoding escaped characters
* @returns
*/
function outputFormatter(msg: OutputObject): string {
Expand All @@ -108,8 +125,9 @@ function outputFormatter(msg: OutputObject): string {
case 'raw':
if (ArrayBuffer.isView(data)) {
const td = new TextDecoder('utf-8');
data = encodeNonPrintable(td.decode(data));
data = td.decode(data);
}
data = encodeWrappedStrings(data);
return data;
case 'list':
return outputFormatterList(data);
Expand All @@ -128,7 +146,7 @@ function outputFormatterList(items: Array<string>): string {
let output = '';
for (const elem of items) {
// Convert null or undefined to empty string
output += `${elem != null ? encodeNonPrintable(elem) : ''}\n`;
output += `${elem != null ? encodeWrappedStrings(elem) : ''}\n`;
}
return output;
}
Expand Down Expand Up @@ -180,13 +198,17 @@ function outputFormatterTable(
for (const row of rows) {
for (const column in options?.columns ?? row) {
if (row[column] != null) {
row[column] = encodeNonPrintable(row[column].toString());
if (typeof row[column] === 'string') {
row[column] = encodeQuotes(row[column]);
row[column] = `"${row[column]}"`;
} else {
row[column] = JSON.stringify(row[column]);
}
row[column] = encodeWrappedStrings(row[column]);
}
// Row[key] is definitely a string or null after this point due to encodeNonPrintable
const cellValue: string | null = row[column];
// Null or '' will both cause cellLength to be 3
const cellLength =
cellValue == null || cellValue.length === 0 ? 3 : cellValue.length; // 3 is length of 'N/A'
row[column] == null || row[column] === '""' ? 3 : row[column].length; // 3 is length of 'N/A'
maxColumnLengths[column] = Math.max(
maxColumnLengths[column] || 0,
cellLength, // Use the length of the encoded value
Expand Down Expand Up @@ -248,10 +270,14 @@ function outputFormatterDict(data: POJO): string {
value = '';
}

value = JSON.stringify(value);
value = encodeNonPrintable(value);
if (typeof value === 'string') {
value = encodeQuotes(value);
value = `"${value}"`;
} else {
value = JSON.stringify(value);
}
value = encodeWrappedStrings(value);

// Re-introduce value.replace logic from old code
value = value.replace(/(?:\r\n|\n)$/, '');
value = value.replace(/(\r\n|\n)/g, '$1\t');

Expand Down Expand Up @@ -397,6 +423,8 @@ export {
outputFormatterError,
retryAuthentication,
remoteErrorCause,
encodeWrappedStrings,
encodeQuotes,
encodeNonPrintable,
};

Expand Down
24 changes: 17 additions & 7 deletions tests/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ describe('bin/utils', () => {
includeHeaders: true,
},
});
expect(tableOutput).toBe('value1\tvalue2\ndata1 \tdata2\nN/A \tN/A\n');
expect(tableOutput).toBe(
'"value1"\t"value2"\n"data1" \t"data2"\nN/A \tN/A\n',
);

// JSON
const jsonOutput = binUtils.outputFormatter({
Expand All @@ -72,7 +74,7 @@ describe('bin/utils', () => {
async () => {
let tableOutput = '';
const keys = {
key1: 7,
key1: 10,
key2: 4,
};
const generator = function* () {
Expand All @@ -93,11 +95,11 @@ describe('bin/utils', () => {
i++;
}
expect(keys).toStrictEqual({
key1: 7,
key2: 6,
key1: 10,
key2: 8,
});
expect(tableOutput).toBe(
'key1 \tkey2 \nvalue1 \tvalue2\ndata1 \tdata2\nN/A \tN/A\n',
'key1 \tkey2 \n"value1" \t"value2"\n"data1" \t"data2"\nN/A \tN/A\n',
);
},
);
Expand Down Expand Up @@ -146,17 +148,17 @@ describe('bin/utils', () => {
});

// Construct the expected output
let expectedValue = JSON.stringify(value);
let expectedValue = binUtils.encodeQuotes(value);
expectedValue = binUtils.encodeNonPrintable(expectedValue);
expectedValue = expectedValue.replace(/(?:\r\n|\n)$/, '');
expectedValue = expectedValue.replace(/(\r\n|\n)/g, '$1\t');
expectedValue = `"${expectedValue}"`;

const maxKeyLength = Math.max(
...Object.keys({ [key]: value }).map((k) => k.length),
);
const padding = ' '.repeat(maxKeyLength - key.length);
const expectedOutput = `${key}${padding}\t${expectedValue}\n`;

// Assert that the formatted output matches the expected output
expect(formattedOutput).toBe(expectedOutput);
},
Expand Down Expand Up @@ -279,4 +281,12 @@ describe('bin/utils', () => {
);
},
);
testUtils.testIf(testUtils.isTestPlatformEmpty)(
'encoding non-printable strings works',
() => {
expect(binUtils.encodeWrappedStrings('\n"\n"')).toBe('\n"\\n"');
expect(binUtils.encodeWrappedStrings('"\\""')).toBe('"\\""');
expect(binUtils.encodeWrappedStrings('"\\"\n\\""')).toBe('"\\"\\n\\""');
},
);
});

0 comments on commit 4015fcf

Please sign in to comment.