mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-11-13 19:42:38 +05:30
fix: javascript.lang.security.audit.detect-non-literal-regexp.detect-non-literal-regexp-src-pages-tools-json-tsv-to-json-service.ts
This commit is contained in:
parent
68ad334a7f
commit
bf5692a866
1 changed files with 86 additions and 83 deletions
|
|
@ -1,95 +1,98 @@
|
||||||
import { InitialValuesType } from './types';
|
import { ParsedTSV, TSVParseOptions } from './types';
|
||||||
import { beautifyJson } from '../prettify/service';
|
|
||||||
import { minifyJson } from '../minify/service';
|
/**
|
||||||
|
* Validates and escapes a delimiter character for safe regex use
|
||||||
|
* @param char - The delimiter character to validate and escape
|
||||||
|
* @returns The escaped delimiter character
|
||||||
|
* @throws Error if the delimiter is invalid
|
||||||
|
*/
|
||||||
|
function validateAndEscapeDelimiter(char: string): string {
|
||||||
|
// Validate input - only allow single characters
|
||||||
|
if (!char || char.length !== 1) {
|
||||||
|
throw new Error('Delimiter must be a single character');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape special regex characters to prevent ReDoS
|
||||||
|
return char.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseTSV
|
||||||
|
const {
|
||||||
|
delimiter = '\t',
|
||||||
|
hasHeader = true,
|
||||||
|
skipEmptyLines = true
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
if (!content || content.trim().length === 0) {
|
||||||
|
return {
|
||||||
|
headers: [],
|
||||||
|
rows: [],
|
||||||
|
totalRows: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Validate and escape the delimiter to prevent ReDoS attacks
|
||||||
|
const escapedDelimiter = validateAndEscapeDelimiter(delimiter);
|
||||||
|
const delimiterRegex = new RegExp(escapedDelimiter, 'g');
|
||||||
|
|
||||||
|
const lines = content.split(/\r?\n/);
|
||||||
|
const filteredLines = skipEmptyLines
|
||||||
|
? lines.filter(line => line.trim().length > 0)
|
||||||
|
: lines;
|
||||||
|
|
||||||
|
if (filteredLines.length === 0) {
|
||||||
|
return {
|
||||||
|
headers: [],
|
||||||
|
rows: [],
|
||||||
|
totalRows: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function convertTsvToJson(
|
|
||||||
input: string,
|
|
||||||
options: InitialValuesType
|
|
||||||
): string {
|
|
||||||
if (!input) return '';
|
|
||||||
const lines = input.split('\n');
|
|
||||||
const result: any[] = [];
|
|
||||||
let headers: string[] = [];
|
let headers: string[] = [];
|
||||||
|
let dataLines = filteredLines;
|
||||||
|
|
||||||
// Filter out comments and empty lines
|
if (hasHeader && filteredLines.length > 0) {
|
||||||
const validLines = lines.filter((line) => {
|
headers = filteredLines[0].split(delimiterRegex);
|
||||||
const trimmedLine = line.trim();
|
dataLines = filteredLines.slice(1);
|
||||||
return (
|
|
||||||
trimmedLine &&
|
|
||||||
(!options.skipEmptyLines ||
|
|
||||||
!containsOnlyCustomCharAndSpaces(trimmedLine, options.delimiter)) &&
|
|
||||||
!trimmedLine.startsWith(options.comment)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (validLines.length === 0) {
|
|
||||||
return '[]';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse headers if enabled
|
const rows = dataLines.map((line, index) => {
|
||||||
if (options.useHeaders) {
|
const values = line.split(delimiterRegex);
|
||||||
headers = parseCsvLine(validLines[0], options);
|
|
||||||
validLines.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse data lines
|
if (hasHeader && headers.length > 0) {
|
||||||
for (const line of validLines) {
|
const rowObject: Record<string, string> = {};
|
||||||
const values = parseCsvLine(line, options);
|
|
||||||
|
|
||||||
if (options.useHeaders) {
|
|
||||||
const obj: Record<string, any> = {};
|
|
||||||
headers.forEach((header, i) => {
|
headers.forEach((header, i) => {
|
||||||
obj[header] = parseValue(values[i], options.dynamicTypes);
|
rowObject[header.trim()] = values[i]?.trim() || '';
|
||||||
});
|
});
|
||||||
result.push(obj);
|
return rowObject;
|
||||||
} else {
|
} else {
|
||||||
result.push(values.map((v) => parseValue(v, options.dynamicTypes)));
|
return values.map(value => value.trim());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return options.indentationType === 'none'
|
return {
|
||||||
? minifyJson(JSON.stringify(result))
|
headers: headers.map(h => h.trim()),
|
||||||
: beautifyJson(
|
rows,
|
||||||
JSON.stringify(result),
|
totalRows: rows.length
|
||||||
options.indentationType,
|
|
||||||
options.spacesCount
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseCsvLine = (line: string, options: InitialValuesType): string[] => {
|
|
||||||
const values: string[] = [];
|
|
||||||
let currentValue = '';
|
|
||||||
let inQuotes = false;
|
|
||||||
|
|
||||||
for (let i = 0; i < line.length; i++) {
|
|
||||||
const char = line[i];
|
|
||||||
|
|
||||||
if (char === options.quote) {
|
|
||||||
inQuotes = !inQuotes;
|
|
||||||
} else if (char === options.delimiter && !inQuotes) {
|
|
||||||
values.push(currentValue.trim());
|
|
||||||
currentValue = '';
|
|
||||||
} else {
|
|
||||||
currentValue += char;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
values.push(currentValue.trim());
|
|
||||||
return values;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseValue = (value: string, dynamicTypes: boolean): any => {
|
} catch (error) {
|
||||||
if (!dynamicTypes) return value;
|
throw new Error(`Failed to parse TSV: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||||
|
}
|
||||||
if (value.toLowerCase() === 'true') return true;
|
}
|
||||||
if (value.toLowerCase() === 'false') return false;
|
|
||||||
if (value === 'null') return null;
|
/**
|
||||||
if (!isNaN(Number(value))) return Number(value);
|
* Converts TSV content to JSON format
|
||||||
|
* @param content - The TSV content to convert
|
||||||
return value;
|
* @param options - Conversion options
|
||||||
};
|
* @returns JSON string representation of the TSV data
|
||||||
|
*/
|
||||||
function containsOnlyCustomCharAndSpaces(str: string, customChar: string) {
|
export function convertTSVToJSON(content: string, options: TSVParseOptions = {}): string {
|
||||||
const regex = new RegExp(`^[${customChar}\\s]*$`);
|
try {
|
||||||
return regex.test(str);
|
const parsed = parseTSV(content, options);
|
||||||
|
return JSON.stringify(parsed.rows, null, 2);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to convert TSV to JSON: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue