@mitre/hdf-utilities 3.6.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/csv/index.d.ts +18 -13
- package/dist/csv/index.d.ts.map +1 -1
- package/dist/csv/index.js +12 -6
- package/dist/csv/index.js.map +1 -1
- package/dist/hash/index.d.ts +8 -9
- package/dist/hash/index.d.ts.map +1 -1
- package/dist/index.d.ts +41 -29
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -5
- package/dist/index.js.map +1 -1
- package/dist/object/index.d.ts +3 -4
- package/dist/object/index.d.ts.map +1 -1
- package/dist/string/index.d.ts +8 -9
- package/dist/string/index.d.ts.map +1 -1
- package/dist/xml/index.d.ts +8 -8
- package/dist/xml/index.d.ts.map +1 -1
- package/dist/xml/index.js +9 -6
- package/dist/xml/index.js.map +1 -1
- package/package.json +1 -1
package/dist/xml/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["record"],"sources":["../../src/xml/index.ts"],"sourcesContent":["/**\n * XML parsing utilities for HDF converters\n * Provides XML parsing with sensible defaults for security tool output\n */\n\nimport { XMLParser, XMLBuilder, XMLValidator } from 'fast-xml-parser';\nimport type { X2jOptions, XmlBuilderOptions } from 'fast-xml-parser';\n\nexport { findValuesByKey as findXmlValues } from '../object/index.js';\n\n/**\n * Default options for XML parsing optimized for security tool converters.\n *\n * **XXE Safety**: fast-xml-parser v5.x does not process DTD or external entities\n * by default. The `processEntities: false` option is set as defense-in-depth to\n * ensure entity expansion is disabled even if future versions change defaults.\n * See: https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/docs/v5/2.XMLparseOptions.md\n */\nconst DEFAULT_PARSE_OPTIONS: Partial<X2jOptions> = {\n attributeNamePrefix: '',\n textNodeName: '#text',\n ignoreAttributes: false,\n ignoreDeclaration: true,\n parseAttributeValue: false,\n parseTagValue: false,\n removeNSPrefix: true,\n processEntities: false,\n};\n\n/**\n * Default options for XML building\n */\nconst DEFAULT_BUILD_OPTIONS: Partial<XmlBuilderOptions> = {\n attributeNamePrefix: '',\n textNodeName: '#text',\n ignoreAttributes: false,\n format: true,\n indentBy: ' ',\n suppressEmptyNode: false,\n};\n\n/**\n * Parse XML string to JavaScript object\n *\n * @param xml - XML string to parse\n * @param options - Optional parser configuration (merged with defaults)\n * @returns Parsed JavaScript object\n *\n * @example\n * ```typescript\n * const xmlString = '<root><item id=\"1\">Test</item></root>';\n * const result = parseXml(xmlString);\n * // Returns: { root: { item: { id: '1', '#text': 'Test' } } }\n * ```\n */\nexport function parseXml(\n xml: string,\n options?: Partial<X2jOptions> & { maxSize?: number }\n): Record<string, unknown> {\n if (options?.maxSize !== undefined && xml.length > options.maxSize) {\n throw new Error(\n `Input exceeds maximum allowed size: ${xml.length} bytes exceeds limit of ${options.maxSize} bytes`\n );\n }\n\n // Validate XML first\n const validation = XMLValidator.validate(xml);\n if (validation !== true) {\n throw new Error(`Invalid XML: ${validation.err.msg}`);\n }\n\n const mergedOptions = {\n ...DEFAULT_PARSE_OPTIONS,\n ...options,\n };\n\n const parser = new XMLParser(mergedOptions);\n return parser.parse(xml) as Record<string, unknown>;\n}\n\n/**\n * Build XML string from JavaScript object\n *\n * @param obj - JavaScript object to convert to XML, or a preserveOrder node list.\n * An array is only meaningful with `preserveOrder: true`; without it the builder\n * indexes the array and emits elements named by its numeric keys.\n * @param options - Optional builder configuration (merged with defaults)\n * @returns XML string\n *\n * @example\n * ```typescript\n * const obj = { root: { item: { id: '1', '#text': 'Test' } } };\n * const xml = buildXml(obj);\n * // Returns formatted XML string\n * ```\n */\nexport function buildXml(\n obj: Record<string, unknown> | unknown[],\n options?: Partial<XmlBuilderOptions>\n): string {\n const mergedOptions = {\n ...DEFAULT_BUILD_OPTIONS,\n ...options,\n };\n\n const builder = new XMLBuilder(mergedOptions);\n return builder.build(obj) as string;\n}\n\n/**\n * Validate if a string is well-formed XML\n *\n * @param xml - String to validate\n * @returns True if valid XML, false otherwise\n *\n * @example\n * ```typescript\n * isValidXml('<root><item>Test</item></root>'); // true\n * isValidXml('<root><item>Test</root>'); // false (mismatched tags)\n * isValidXml('not xml'); // false\n * ```\n */\n/**\n * Whether a character is legal in an XML 1.0 document, per the Char production.\n * The one home for this rule: an emitter deciding what to replace and a\n * validator deciding what to reject must not answer it differently.\n */\nexport function isXmlChar(ch: string): boolean {\n const c = ch.codePointAt(0) as number;\n return (\n c === 0x9 ||\n c === 0xa ||\n c === 0xd ||\n (c >= 0x20 && c <= 0xd7ff) ||\n (c >= 0xe000 && c <= 0xfffd) ||\n c >= 0x10000\n );\n}\n\n/**\n * Replace every character XML 1.0 forbids with U+FFFD, which is what Go's\n * encoding/xml does. XML defines no escape for these — a numeric reference to a\n * C0 control is itself illegal — so the choice is a replacement character or a\n * document no conforming parser will read.\n */\nexport function xmlSafeText(value: string): string {\n let out = '';\n for (const ch of value) out += isXmlChar(ch) ? ch : '\\ufffd';\n return out;\n}\n\nexport function isValidXml(xml: string): boolean {\n if (!xml || xml.trim().length === 0) {\n return false;\n }\n\n // The structural validator does not enforce XML 1.0's Char production, so it\n // called a document well-formed that no conforming parser will read — which is\n // how a converter emitting a raw ESC passed its own well-formedness check.\n for (const ch of xml) {\n if (!isXmlChar(ch)) return false;\n }\n\n const result = XMLValidator.validate(xml);\n return result === true;\n}\n\n/**\n * Parse XML with array handling for repeated elements\n * Forces specified tag names to always be arrays, even if only one element exists\n *\n * @param xml - XML string to parse\n * @param arrayTags - Array of tag names that should always be parsed as arrays\n * @returns Parsed JavaScript object with forced arrays\n *\n * @example\n * ```typescript\n * const xml = '<root><item>Test</item></root>';\n * const result = parseXmlWithArrays(xml, ['item']);\n * // Returns: { root: { item: ['Test'] } }\n * // Without arrayTags, would return: { root: { item: 'Test' } }\n * ```\n */\nexport function parseXmlWithArrays(\n xml: string,\n arrayTags: string[],\n options?: Partial<X2jOptions> & { maxSize?: number }\n): Record<string, unknown> {\n if (options?.maxSize !== undefined && xml.length > options.maxSize) {\n throw new Error(\n `Input exceeds maximum allowed size: ${xml.length} bytes exceeds limit of ${options.maxSize} bytes`\n );\n }\n\n // Validate XML first (consistency with parseXml)\n const validation = XMLValidator.validate(xml);\n if (validation !== true) {\n throw new Error(`Invalid XML: ${validation.err.msg}`);\n }\n\n const mergedOptions: Partial<X2jOptions> = {\n ...DEFAULT_PARSE_OPTIONS,\n ...options,\n isArray: (tagName: string) => arrayTags.includes(tagName),\n };\n\n const parser = new XMLParser(mergedOptions);\n return parser.parse(xml) as Record<string, unknown>;\n}\n\n/**\n * Extract text content from XML, stripping all tags\n *\n * @param xml - XML string\n * @returns Plain text content with tags removed\n *\n * @example\n * ```typescript\n * const xml = '<root><b>Bold</b> and <i>italic</i></root>';\n * const text = extractTextFromXml(xml);\n * // Returns: 'Bold and italic'\n * ```\n */\nexport function extractTextFromXml(xml: string): string {\n if (!isValidXml(xml)) {\n return '';\n }\n\n try {\n const parsed = parseXml(xml, { ignoreAttributes: true });\n return extractTextRecursive(parsed).trim();\n } catch {\n return '';\n }\n}\n\n/**\n * Recursively extract text from parsed XML object\n * @private\n */\nfunction extractTextRecursive(obj: unknown): string {\n if (typeof obj === 'string' || typeof obj === 'number') {\n return String(obj);\n }\n\n if (Array.isArray(obj)) {\n return obj.map(extractTextRecursive).join(' ');\n }\n\n if (typeof obj === 'object' && obj !== null) {\n const record = obj as Record<string, unknown>;\n\n // Recursively extract text from all properties\n return Object.values(record)\n .map(extractTextRecursive)\n .filter(text => text.length > 0)\n .join(' ');\n }\n\n return '';\n}\n"],"mappings":";;;;;;;;;;;;;;;AAkBA,MAAM,wBAA6C;CACjD,qBAAqB;CACrB,cAAc;CACd,kBAAkB;CAClB,mBAAmB;CACnB,qBAAqB;CACrB,eAAe;CACf,gBAAgB;CAChB,iBAAiB;AACnB;;;;AAKA,MAAM,wBAAoD;CACxD,qBAAqB;CACrB,cAAc;CACd,kBAAkB;CAClB,QAAQ;CACR,UAAU;CACV,mBAAmB;AACrB;;;;;;;;;;;;;;;AAgBA,SAAgB,SACd,KACA,SACyB;CACzB,IAAI,SAAS,YAAY,KAAA,KAAa,IAAI,SAAS,QAAQ,SACzD,MAAM,IAAI,MACR,uCAAuC,IAAI,OAAO,0BAA0B,QAAQ,QAAQ,OAC9F;CAIF,MAAM,aAAa,aAAa,SAAS,GAAG;CAC5C,IAAI,eAAe,MACjB,MAAM,IAAI,MAAM,gBAAgB,WAAW,IAAI,KAAK;
|
|
1
|
+
{"version":3,"file":"index.js","names":["record"],"sources":["../../src/xml/index.ts"],"sourcesContent":["/**\n * XML parsing utilities for HDF converters\n * Provides XML parsing with sensible defaults for security tool output\n */\n\nimport { XMLParser, XMLBuilder, XMLValidator } from 'fast-xml-parser';\nimport type { X2jOptions, XmlBuilderOptions } from 'fast-xml-parser';\n\nexport { findValuesByKey as findXmlValues } from '../object/index.js';\n\n/**\n * Default options for XML parsing optimized for security tool converters.\n *\n * **XXE Safety**: fast-xml-parser v5.x does not process DTD or external entities\n * by default. The `processEntities: false` option is set as defense-in-depth to\n * ensure entity expansion is disabled even if future versions change defaults.\n * See: https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/docs/v5/2.XMLparseOptions.md\n */\nconst DEFAULT_PARSE_OPTIONS: Partial<X2jOptions> = {\n attributeNamePrefix: '',\n textNodeName: '#text',\n ignoreAttributes: false,\n ignoreDeclaration: true,\n parseAttributeValue: false,\n parseTagValue: false,\n removeNSPrefix: true,\n processEntities: false,\n};\n\n/**\n * Default options for XML building\n */\nconst DEFAULT_BUILD_OPTIONS: Partial<XmlBuilderOptions> = {\n attributeNamePrefix: '',\n textNodeName: '#text',\n ignoreAttributes: false,\n format: true,\n indentBy: ' ',\n suppressEmptyNode: false,\n};\n\n/**\n * Parse XML string to JavaScript object\n *\n * @param xml - XML string to parse\n * @param options - Optional parser configuration (merged with defaults)\n * @returns Parsed JavaScript object\n *\n * @example\n * ```typescript\n * const xmlString = '<root><item id=\"1\">Test</item></root>';\n * const result = parseXml(xmlString);\n * // Returns: { root: { item: { id: '1', '#text': 'Test' } } }\n * ```\n */\nexport function parseXml(\n xml: string,\n options?: Partial<X2jOptions> & { maxSize?: number }\n): Record<string, unknown> {\n if (options?.maxSize !== undefined && xml.length > options.maxSize) {\n throw new Error(\n `Input exceeds maximum allowed size: ${xml.length} bytes exceeds limit of ${options.maxSize} bytes`\n );\n }\n\n // Validate XML first\n const validation = XMLValidator.validate(xml);\n if (validation !== true) {\n throw new Error(`Invalid XML: ${validation.err.msg}`);\n }\n\n const mergedOptions = {\n ...DEFAULT_PARSE_OPTIONS,\n ...options,\n };\n\n const parser = new XMLParser(mergedOptions);\n return parser.parse(xml) as Record<string, unknown>;\n}\n\n/**\n * Build XML string from JavaScript object\n *\n * @param obj - JavaScript object to convert to XML, or a preserveOrder node list.\n * An array is only meaningful with `preserveOrder: true`; without it the builder\n * indexes the array and emits elements named by its numeric keys.\n * @param options - Optional builder configuration (merged with defaults)\n * @returns XML string\n *\n * @example\n * ```typescript\n * const obj = { root: { item: { id: '1', '#text': 'Test' } } };\n * const xml = buildXml(obj);\n * // Returns formatted XML string\n * ```\n */\nexport function buildXml(\n obj: Record<string, unknown> | unknown[],\n options?: Partial<XmlBuilderOptions>\n): string {\n const mergedOptions = {\n ...DEFAULT_BUILD_OPTIONS,\n ...options,\n };\n\n const builder = new XMLBuilder(mergedOptions);\n return builder.build(obj) as string;\n}\n\n/**\n * Validate if a string is well-formed XML\n *\n * @param xml - String to validate\n * @returns True if valid XML, false otherwise\n *\n * @example\n * ```typescript\n * isValidXml('<root><item>Test</item></root>'); // true\n * isValidXml('<root><item>Test</root>'); // false (mismatched tags)\n * isValidXml('not xml'); // false\n * ```\n */\n/**\n * Whether a character is legal in an XML 1.0 document, per the Char production.\n * The one home for this rule: an emitter deciding what to replace and a\n * validator deciding what to reject must not answer it differently.\n */\nexport function isXmlChar(ch: string): boolean {\n const c = ch.codePointAt(0) as number;\n return (\n c === 0x9 ||\n c === 0xa ||\n c === 0xd ||\n (c >= 0x20 && c <= 0xd7ff) ||\n (c >= 0xe000 && c <= 0xfffd) ||\n c >= 0x10000\n );\n}\n\n/**\n * Replace every character XML 1.0 forbids with U+FFFD, which is what Go's\n * encoding/xml does. XML defines no escape for these — a numeric reference to a\n * C0 control is itself illegal — so the choice is a replacement character or a\n * document no conforming parser will read.\n */\nexport function xmlSafeText(value: string): string {\n let out = '';\n for (const ch of value) out += isXmlChar(ch) ? ch : '\\ufffd';\n return out;\n}\n\nexport function isValidXml(xml: string): boolean {\n if (!xml || xml.trim().length === 0) {\n return false;\n }\n\n // The structural validator does not enforce XML 1.0's Char production, so it\n // called a document well-formed that no conforming parser will read — which is\n // how a converter emitting a raw ESC passed its own well-formedness check.\n for (const ch of xml) {\n if (!isXmlChar(ch)) return false;\n }\n\n const result = XMLValidator.validate(xml);\n return result === true;\n}\n\n/**\n * Parse XML with array handling for repeated elements\n * Forces specified tag names to always be arrays, even if only one element exists\n *\n * @param xml - XML string to parse\n * @param arrayTags - Array of tag names that should always be parsed as arrays\n * @returns Parsed JavaScript object with forced arrays\n *\n * @example\n * ```typescript\n * const xml = '<root><item>Test</item></root>';\n * const result = parseXmlWithArrays(xml, ['item']);\n * // Returns: { root: { item: ['Test'] } }\n * // Without arrayTags, would return: { root: { item: 'Test' } }\n * ```\n */\nexport function parseXmlWithArrays(\n xml: string,\n arrayTags: string[],\n options?: Partial<X2jOptions> & { maxSize?: number }\n): Record<string, unknown> {\n if (options?.maxSize !== undefined && xml.length > options.maxSize) {\n throw new Error(\n `Input exceeds maximum allowed size: ${xml.length} bytes exceeds limit of ${options.maxSize} bytes`\n );\n }\n\n // Validate XML first (consistency with parseXml)\n const validation = XMLValidator.validate(xml);\n if (validation !== true) {\n throw new Error(`Invalid XML: ${validation.err.msg}`);\n }\n\n const mergedOptions: Partial<X2jOptions> = {\n ...DEFAULT_PARSE_OPTIONS,\n ...options,\n isArray: (tagName: string) => arrayTags.includes(tagName),\n };\n\n const parser = new XMLParser(mergedOptions);\n return parser.parse(xml) as Record<string, unknown>;\n}\n\n/**\n * Extract text content from XML, stripping all tags\n *\n * @param xml - XML string\n * @returns Plain text content with tags removed\n *\n * @example\n * ```typescript\n * const xml = '<root><b>Bold</b> and <i>italic</i></root>';\n * const text = extractTextFromXml(xml);\n * // Returns: 'Bold and italic'\n * ```\n */\nexport function extractTextFromXml(xml: string): string {\n if (!isValidXml(xml)) {\n return '';\n }\n\n try {\n const parsed = parseXml(xml, { ignoreAttributes: true });\n return extractTextRecursive(parsed).trim();\n } catch {\n return '';\n }\n}\n\n/**\n * Recursively extract text from parsed XML object\n * @private\n */\nfunction extractTextRecursive(obj: unknown): string {\n if (typeof obj === 'string' || typeof obj === 'number') {\n return String(obj);\n }\n\n if (Array.isArray(obj)) {\n return obj.map(extractTextRecursive).join(' ');\n }\n\n if (typeof obj === 'object' && obj !== null) {\n const record = obj as Record<string, unknown>;\n\n // Recursively extract text from all properties\n return Object.values(record)\n .map(extractTextRecursive)\n .filter(text => text.length > 0)\n .join(' ');\n }\n\n return '';\n}\n"],"mappings":"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"}
|