@mitre/hdf-utilities 2.0.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/LICENSE.md +55 -0
- package/README.md +403 -0
- package/dist/csv/index.d.ts +100 -0
- package/dist/csv/index.d.ts.map +1 -0
- package/dist/csv/index.js +251 -0
- package/dist/csv/index.js.map +1 -0
- package/dist/hash/index.d.ts +95 -0
- package/dist/hash/index.d.ts.map +1 -0
- package/dist/hash/index.js +118 -0
- package/dist/hash/index.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/json/index.d.ts +32 -0
- package/dist/json/index.d.ts.map +1 -0
- package/dist/json/index.js +61 -0
- package/dist/json/index.js.map +1 -0
- package/dist/object/index.d.ts +41 -0
- package/dist/object/index.d.ts.map +1 -0
- package/dist/object/index.js +70 -0
- package/dist/object/index.js.map +1 -0
- package/dist/string/index.d.ts +50 -0
- package/dist/string/index.d.ts.map +1 -0
- package/dist/string/index.js +62 -0
- package/dist/string/index.js.map +1 -0
- package/dist/xml/index.d.ts +86 -0
- package/dist/xml/index.d.ts.map +1 -0
- package/dist/xml/index.js +187 -0
- package/dist/xml/index.js.map +1 -0
- package/package.json +82 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/object/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,MAAM,EACX,QAAQ,SAAM,GACb,OAAO,EAAE,CAIX;AAiCD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/B,MAAM,EAAE,MAAM,GACb,OAAO,EAAE,CAEX;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAE3B"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic object query utilities for traversing parsed data structures.
|
|
3
|
+
*
|
|
4
|
+
* These functions work on any plain JS objects — parsed XML, JSON, or CSV rows
|
|
5
|
+
* are all objects by the time converters work with them.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Recursively find all values for a given key anywhere in a nested object tree.
|
|
9
|
+
*
|
|
10
|
+
* Walks the entire tree depth-first. When a property matching `key` is found,
|
|
11
|
+
* its value is collected. Recurse continues into all child values (objects and
|
|
12
|
+
* arrays) regardless of whether the current level matched.
|
|
13
|
+
*
|
|
14
|
+
* @param obj - Object tree to search (parsed XML, JSON, etc.)
|
|
15
|
+
* @param key - Property name to search for
|
|
16
|
+
* @param maxDepth - Maximum recursion depth (default 100). Nodes beyond this depth are silently skipped.
|
|
17
|
+
* @returns Flat array of all matched values
|
|
18
|
+
*/
|
|
19
|
+
export function findValuesByKey(obj, key, maxDepth = 100) {
|
|
20
|
+
const results = [];
|
|
21
|
+
walk(obj, key, results, 0, maxDepth);
|
|
22
|
+
return results;
|
|
23
|
+
}
|
|
24
|
+
function walk(node, key, results, depth, maxDepth) {
|
|
25
|
+
if (depth > maxDepth) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (node === null || node === undefined || typeof node !== 'object') {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (Array.isArray(node)) {
|
|
32
|
+
for (const item of node) {
|
|
33
|
+
walk(item, key, results, depth + 1, maxDepth);
|
|
34
|
+
}
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const record = node;
|
|
38
|
+
if (key in record) {
|
|
39
|
+
results.push(record[key]);
|
|
40
|
+
}
|
|
41
|
+
for (const value of Object.values(record)) {
|
|
42
|
+
walk(value, key, results, depth + 1, maxDepth);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Extract all values for a named field from an array of objects.
|
|
47
|
+
*
|
|
48
|
+
* Rows where the column is `undefined` are skipped.
|
|
49
|
+
*
|
|
50
|
+
* @param rows - Array of objects (e.g. parsed CSV rows)
|
|
51
|
+
* @param column - Property name to extract
|
|
52
|
+
* @returns Array of values for that column
|
|
53
|
+
*/
|
|
54
|
+
export function extractColumn(rows, column) {
|
|
55
|
+
return rows.map((r) => r[column]).filter((v) => v !== undefined);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Filter an array of objects to rows where a column matches a value.
|
|
59
|
+
*
|
|
60
|
+
* Uses strict equality (`===`).
|
|
61
|
+
*
|
|
62
|
+
* @param rows - Array of objects to filter
|
|
63
|
+
* @param column - Property name to check
|
|
64
|
+
* @param value - Value to match against
|
|
65
|
+
* @returns Filtered array of matching rows
|
|
66
|
+
*/
|
|
67
|
+
export function findRows(rows, column, value) {
|
|
68
|
+
return rows.filter((r) => r[column] === value);
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/object/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAC7B,GAAY,EACZ,GAAW,EACX,QAAQ,GAAG,GAAG;IAEd,MAAM,OAAO,GAAc,EAAE,CAAC;IAC9B,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;IACrC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,IAAI,CACX,IAAa,EACb,GAAW,EACX,OAAkB,EAClB,KAAa,EACb,QAAgB;IAEhB,IAAI,KAAK,GAAG,QAAQ,EAAE,CAAC;QACrB,OAAO;IACT,CAAC;IAED,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpE,OAAO;IACT,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;YACxB,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;QAChD,CAAC;QACD,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,IAA+B,CAAC;IAC/C,IAAI,GAAG,IAAI,MAAM,EAAE,CAAC;QAClB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1C,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAC3B,IAA+B,EAC/B,MAAc;IAEd,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;AACnE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,QAAQ,CACtB,IAA+B,EAC/B,MAAc,EACd,KAAc;IAEd,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,KAAK,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String manipulation utilities for HDF converters.
|
|
3
|
+
*
|
|
4
|
+
* These handle common operations on security tool output strings:
|
|
5
|
+
* HTML stripping for STIG descriptions and SonarQube messages,
|
|
6
|
+
* and multi-format timestamp parsing for tool output dates.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Remove HTML tags from a string and normalize whitespace.
|
|
10
|
+
*
|
|
11
|
+
* Strips all HTML/XML tags, collapses multiple whitespace characters
|
|
12
|
+
* into single spaces, and trims leading/trailing whitespace.
|
|
13
|
+
*
|
|
14
|
+
* For XML documents, use {@link extractTextFromXml} from the xml module instead.
|
|
15
|
+
* This function is for inline HTML in non-XML strings (STIG descriptions,
|
|
16
|
+
* SonarQube messages, etc.).
|
|
17
|
+
*
|
|
18
|
+
* @param html - String potentially containing HTML tags
|
|
19
|
+
* @returns Plain text with tags removed and whitespace normalized
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* stripHtml('<p>hello</p> <b>world</b>'); // 'hello world'
|
|
24
|
+
* stripHtml('no tags here'); // 'no tags here'
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function stripHtml(html: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Parse a timestamp string in various common formats.
|
|
30
|
+
*
|
|
31
|
+
* Attempts to parse the input using the JavaScript Date constructor, which
|
|
32
|
+
* supports ISO 8601, RFC 2822, and several other formats natively.
|
|
33
|
+
*
|
|
34
|
+
* Returns `null` if the input is empty or cannot be parsed.
|
|
35
|
+
*
|
|
36
|
+
* Equivalent to the Go `ParseTimestamp` in shared/go/converterutil.go.
|
|
37
|
+
*
|
|
38
|
+
* @param s - Timestamp string to parse
|
|
39
|
+
* @returns Parsed Date or null if unparseable
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* parseTimestamp('2024-01-15T10:30:00Z'); // Date object
|
|
44
|
+
* parseTimestamp('Mon, 15 Jan 2024 10:30:00 UTC'); // Date object
|
|
45
|
+
* parseTimestamp('not a date'); // null
|
|
46
|
+
* parseTimestamp(''); // null
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseTimestamp(s: string): Date | null;
|
|
50
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/string/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG9C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAWrD"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String manipulation utilities for HDF converters.
|
|
3
|
+
*
|
|
4
|
+
* These handle common operations on security tool output strings:
|
|
5
|
+
* HTML stripping for STIG descriptions and SonarQube messages,
|
|
6
|
+
* and multi-format timestamp parsing for tool output dates.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Remove HTML tags from a string and normalize whitespace.
|
|
10
|
+
*
|
|
11
|
+
* Strips all HTML/XML tags, collapses multiple whitespace characters
|
|
12
|
+
* into single spaces, and trims leading/trailing whitespace.
|
|
13
|
+
*
|
|
14
|
+
* For XML documents, use {@link extractTextFromXml} from the xml module instead.
|
|
15
|
+
* This function is for inline HTML in non-XML strings (STIG descriptions,
|
|
16
|
+
* SonarQube messages, etc.).
|
|
17
|
+
*
|
|
18
|
+
* @param html - String potentially containing HTML tags
|
|
19
|
+
* @returns Plain text with tags removed and whitespace normalized
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* stripHtml('<p>hello</p> <b>world</b>'); // 'hello world'
|
|
24
|
+
* stripHtml('no tags here'); // 'no tags here'
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function stripHtml(html) {
|
|
28
|
+
const stripped = html.replace(/<[^>]*>/g, ' ');
|
|
29
|
+
return stripped.replace(/\s+/g, ' ').trim();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parse a timestamp string in various common formats.
|
|
33
|
+
*
|
|
34
|
+
* Attempts to parse the input using the JavaScript Date constructor, which
|
|
35
|
+
* supports ISO 8601, RFC 2822, and several other formats natively.
|
|
36
|
+
*
|
|
37
|
+
* Returns `null` if the input is empty or cannot be parsed.
|
|
38
|
+
*
|
|
39
|
+
* Equivalent to the Go `ParseTimestamp` in shared/go/converterutil.go.
|
|
40
|
+
*
|
|
41
|
+
* @param s - Timestamp string to parse
|
|
42
|
+
* @returns Parsed Date or null if unparseable
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* parseTimestamp('2024-01-15T10:30:00Z'); // Date object
|
|
47
|
+
* parseTimestamp('Mon, 15 Jan 2024 10:30:00 UTC'); // Date object
|
|
48
|
+
* parseTimestamp('not a date'); // null
|
|
49
|
+
* parseTimestamp(''); // null
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export function parseTimestamp(s) {
|
|
53
|
+
if (!s || s.trim().length === 0) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const parsed = new Date(s);
|
|
57
|
+
if (isNaN(parsed.getTime())) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return parsed;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/string/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IAC/C,OAAO,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,cAAc,CAAC,CAAS;IACtC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XML parsing utilities for HDF converters
|
|
3
|
+
* Provides XML parsing with sensible defaults for security tool output
|
|
4
|
+
*/
|
|
5
|
+
import type { X2jOptions, XmlBuilderOptions } from 'fast-xml-parser';
|
|
6
|
+
export { findValuesByKey as findXmlValues } from '../object/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* Parse XML string to JavaScript object
|
|
9
|
+
*
|
|
10
|
+
* @param xml - XML string to parse
|
|
11
|
+
* @param options - Optional parser configuration (merged with defaults)
|
|
12
|
+
* @returns Parsed JavaScript object
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const xmlString = '<root><item id="1">Test</item></root>';
|
|
17
|
+
* const result = parseXml(xmlString);
|
|
18
|
+
* // Returns: { root: { item: { id: '1', '#text': 'Test' } } }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseXml(xml: string, options?: Partial<X2jOptions> & {
|
|
22
|
+
maxSize?: number;
|
|
23
|
+
}): Record<string, unknown>;
|
|
24
|
+
/**
|
|
25
|
+
* Build XML string from JavaScript object
|
|
26
|
+
*
|
|
27
|
+
* @param obj - JavaScript object to convert to XML
|
|
28
|
+
* @param options - Optional builder configuration (merged with defaults)
|
|
29
|
+
* @returns XML string
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const obj = { root: { item: { id: '1', '#text': 'Test' } } };
|
|
34
|
+
* const xml = buildXml(obj);
|
|
35
|
+
* // Returns formatted XML string
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildXml(obj: Record<string, unknown>, options?: Partial<XmlBuilderOptions>): string;
|
|
39
|
+
/**
|
|
40
|
+
* Validate if a string is well-formed XML
|
|
41
|
+
*
|
|
42
|
+
* @param xml - String to validate
|
|
43
|
+
* @returns True if valid XML, false otherwise
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* isValidXml('<root><item>Test</item></root>'); // true
|
|
48
|
+
* isValidXml('<root><item>Test</root>'); // false (mismatched tags)
|
|
49
|
+
* isValidXml('not xml'); // false
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare function isValidXml(xml: string): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Parse XML with array handling for repeated elements
|
|
55
|
+
* Forces specified tag names to always be arrays, even if only one element exists
|
|
56
|
+
*
|
|
57
|
+
* @param xml - XML string to parse
|
|
58
|
+
* @param arrayTags - Array of tag names that should always be parsed as arrays
|
|
59
|
+
* @returns Parsed JavaScript object with forced arrays
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* const xml = '<root><item>Test</item></root>';
|
|
64
|
+
* const result = parseXmlWithArrays(xml, ['item']);
|
|
65
|
+
* // Returns: { root: { item: ['Test'] } }
|
|
66
|
+
* // Without arrayTags, would return: { root: { item: 'Test' } }
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare function parseXmlWithArrays(xml: string, arrayTags: string[], options?: Partial<X2jOptions> & {
|
|
70
|
+
maxSize?: number;
|
|
71
|
+
}): Record<string, unknown>;
|
|
72
|
+
/**
|
|
73
|
+
* Extract text content from XML, stripping all tags
|
|
74
|
+
*
|
|
75
|
+
* @param xml - XML string
|
|
76
|
+
* @returns Plain text content with tags removed
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const xml = '<root><b>Bold</b> and <i>italic</i></root>';
|
|
81
|
+
* const text = extractTextFromXml(xml);
|
|
82
|
+
* // Returns: 'Bold and italic'
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export declare function extractTextFromXml(xml: string): string;
|
|
86
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/xml/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAErE,OAAO,EAAE,eAAe,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAiCtE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACnD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoBzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,OAAO,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GACnC,MAAM,CAQR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAO/C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACnD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqBzB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWtD"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XML parsing utilities for HDF converters
|
|
3
|
+
* Provides XML parsing with sensible defaults for security tool output
|
|
4
|
+
*/
|
|
5
|
+
import { XMLParser, XMLBuilder, XMLValidator } from 'fast-xml-parser';
|
|
6
|
+
export { findValuesByKey as findXmlValues } from '../object/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* Default options for XML parsing optimized for security tool converters.
|
|
9
|
+
*
|
|
10
|
+
* **XXE Safety**: fast-xml-parser v5.x does not process DTD or external entities
|
|
11
|
+
* by default. The `processEntities: false` option is set as defense-in-depth to
|
|
12
|
+
* ensure entity expansion is disabled even if future versions change defaults.
|
|
13
|
+
* See: https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/docs/v5/2.XMLparseOptions.md
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_PARSE_OPTIONS = {
|
|
16
|
+
attributeNamePrefix: '',
|
|
17
|
+
textNodeName: '#text',
|
|
18
|
+
ignoreAttributes: false,
|
|
19
|
+
ignoreDeclaration: true,
|
|
20
|
+
parseAttributeValue: false,
|
|
21
|
+
parseTagValue: false,
|
|
22
|
+
removeNSPrefix: true,
|
|
23
|
+
processEntities: false,
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Default options for XML building
|
|
27
|
+
*/
|
|
28
|
+
const DEFAULT_BUILD_OPTIONS = {
|
|
29
|
+
attributeNamePrefix: '',
|
|
30
|
+
textNodeName: '#text',
|
|
31
|
+
ignoreAttributes: false,
|
|
32
|
+
format: true,
|
|
33
|
+
indentBy: ' ',
|
|
34
|
+
suppressEmptyNode: false,
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Parse XML string to JavaScript object
|
|
38
|
+
*
|
|
39
|
+
* @param xml - XML string to parse
|
|
40
|
+
* @param options - Optional parser configuration (merged with defaults)
|
|
41
|
+
* @returns Parsed JavaScript object
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const xmlString = '<root><item id="1">Test</item></root>';
|
|
46
|
+
* const result = parseXml(xmlString);
|
|
47
|
+
* // Returns: { root: { item: { id: '1', '#text': 'Test' } } }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function parseXml(xml, options) {
|
|
51
|
+
if (options?.maxSize !== undefined && xml.length > options.maxSize) {
|
|
52
|
+
throw new Error(`Input exceeds maximum allowed size: ${xml.length} bytes exceeds limit of ${options.maxSize} bytes`);
|
|
53
|
+
}
|
|
54
|
+
// Validate XML first
|
|
55
|
+
const validation = XMLValidator.validate(xml);
|
|
56
|
+
if (validation !== true) {
|
|
57
|
+
throw new Error(`Invalid XML: ${validation.err.msg}`);
|
|
58
|
+
}
|
|
59
|
+
const mergedOptions = {
|
|
60
|
+
...DEFAULT_PARSE_OPTIONS,
|
|
61
|
+
...options,
|
|
62
|
+
};
|
|
63
|
+
const parser = new XMLParser(mergedOptions);
|
|
64
|
+
return parser.parse(xml);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build XML string from JavaScript object
|
|
68
|
+
*
|
|
69
|
+
* @param obj - JavaScript object to convert to XML
|
|
70
|
+
* @param options - Optional builder configuration (merged with defaults)
|
|
71
|
+
* @returns XML string
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* const obj = { root: { item: { id: '1', '#text': 'Test' } } };
|
|
76
|
+
* const xml = buildXml(obj);
|
|
77
|
+
* // Returns formatted XML string
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export function buildXml(obj, options) {
|
|
81
|
+
const mergedOptions = {
|
|
82
|
+
...DEFAULT_BUILD_OPTIONS,
|
|
83
|
+
...options,
|
|
84
|
+
};
|
|
85
|
+
const builder = new XMLBuilder(mergedOptions);
|
|
86
|
+
return builder.build(obj);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Validate if a string is well-formed XML
|
|
90
|
+
*
|
|
91
|
+
* @param xml - String to validate
|
|
92
|
+
* @returns True if valid XML, false otherwise
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* isValidXml('<root><item>Test</item></root>'); // true
|
|
97
|
+
* isValidXml('<root><item>Test</root>'); // false (mismatched tags)
|
|
98
|
+
* isValidXml('not xml'); // false
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export function isValidXml(xml) {
|
|
102
|
+
if (!xml || xml.trim().length === 0) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
const result = XMLValidator.validate(xml);
|
|
106
|
+
return result === true;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Parse XML with array handling for repeated elements
|
|
110
|
+
* Forces specified tag names to always be arrays, even if only one element exists
|
|
111
|
+
*
|
|
112
|
+
* @param xml - XML string to parse
|
|
113
|
+
* @param arrayTags - Array of tag names that should always be parsed as arrays
|
|
114
|
+
* @returns Parsed JavaScript object with forced arrays
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* const xml = '<root><item>Test</item></root>';
|
|
119
|
+
* const result = parseXmlWithArrays(xml, ['item']);
|
|
120
|
+
* // Returns: { root: { item: ['Test'] } }
|
|
121
|
+
* // Without arrayTags, would return: { root: { item: 'Test' } }
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export function parseXmlWithArrays(xml, arrayTags, options) {
|
|
125
|
+
if (options?.maxSize !== undefined && xml.length > options.maxSize) {
|
|
126
|
+
throw new Error(`Input exceeds maximum allowed size: ${xml.length} bytes exceeds limit of ${options.maxSize} bytes`);
|
|
127
|
+
}
|
|
128
|
+
// Validate XML first (consistency with parseXml)
|
|
129
|
+
const validation = XMLValidator.validate(xml);
|
|
130
|
+
if (validation !== true) {
|
|
131
|
+
throw new Error(`Invalid XML: ${validation.err.msg}`);
|
|
132
|
+
}
|
|
133
|
+
const mergedOptions = {
|
|
134
|
+
...DEFAULT_PARSE_OPTIONS,
|
|
135
|
+
...options,
|
|
136
|
+
isArray: (tagName) => arrayTags.includes(tagName),
|
|
137
|
+
};
|
|
138
|
+
const parser = new XMLParser(mergedOptions);
|
|
139
|
+
return parser.parse(xml);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Extract text content from XML, stripping all tags
|
|
143
|
+
*
|
|
144
|
+
* @param xml - XML string
|
|
145
|
+
* @returns Plain text content with tags removed
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* const xml = '<root><b>Bold</b> and <i>italic</i></root>';
|
|
150
|
+
* const text = extractTextFromXml(xml);
|
|
151
|
+
* // Returns: 'Bold and italic'
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
export function extractTextFromXml(xml) {
|
|
155
|
+
if (!isValidXml(xml)) {
|
|
156
|
+
return '';
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
const parsed = parseXml(xml, { ignoreAttributes: true });
|
|
160
|
+
return extractTextRecursive(parsed).trim();
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return '';
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Recursively extract text from parsed XML object
|
|
168
|
+
* @private
|
|
169
|
+
*/
|
|
170
|
+
function extractTextRecursive(obj) {
|
|
171
|
+
if (typeof obj === 'string' || typeof obj === 'number') {
|
|
172
|
+
return String(obj);
|
|
173
|
+
}
|
|
174
|
+
if (Array.isArray(obj)) {
|
|
175
|
+
return obj.map(extractTextRecursive).join(' ');
|
|
176
|
+
}
|
|
177
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
178
|
+
const record = obj;
|
|
179
|
+
// Recursively extract text from all properties
|
|
180
|
+
return Object.values(record)
|
|
181
|
+
.map(extractTextRecursive)
|
|
182
|
+
.filter(text => text.length > 0)
|
|
183
|
+
.join(' ');
|
|
184
|
+
}
|
|
185
|
+
return '';
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/xml/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mitre/hdf-utilities",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Utility functions for HDF libraries (JSON parsing, validation helpers)",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
},
|
|
16
|
+
"./json": {
|
|
17
|
+
"import": "./dist/json/index.js",
|
|
18
|
+
"types": "./dist/json/index.d.ts"
|
|
19
|
+
},
|
|
20
|
+
"./hash": {
|
|
21
|
+
"import": "./dist/hash/index.js",
|
|
22
|
+
"types": "./dist/hash/index.d.ts"
|
|
23
|
+
},
|
|
24
|
+
"./xml": {
|
|
25
|
+
"import": "./dist/xml/index.js",
|
|
26
|
+
"types": "./dist/xml/index.d.ts"
|
|
27
|
+
},
|
|
28
|
+
"./csv": {
|
|
29
|
+
"import": "./dist/csv/index.js",
|
|
30
|
+
"types": "./dist/csv/index.d.ts"
|
|
31
|
+
},
|
|
32
|
+
"./object": {
|
|
33
|
+
"import": "./dist/object/index.js",
|
|
34
|
+
"types": "./dist/object/index.d.ts"
|
|
35
|
+
},
|
|
36
|
+
"./string": {
|
|
37
|
+
"import": "./dist/string/index.js",
|
|
38
|
+
"types": "./dist/string/index.d.ts"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist"
|
|
43
|
+
],
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "https://github.com/mitre/hdf-libs.git",
|
|
47
|
+
"directory": "hdf-utilities"
|
|
48
|
+
},
|
|
49
|
+
"author": "MITRE Corporation",
|
|
50
|
+
"license": "Apache-2.0",
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"d3-dsv": "^3.0.1",
|
|
53
|
+
"fast-xml-parser": "5.5.7"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=20.0.0"
|
|
57
|
+
},
|
|
58
|
+
"keywords": [
|
|
59
|
+
"hdf",
|
|
60
|
+
"heimdall",
|
|
61
|
+
"utilities",
|
|
62
|
+
"json",
|
|
63
|
+
"parsing"
|
|
64
|
+
],
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@stryker-mutator/core": "^9.5.1",
|
|
67
|
+
"@stryker-mutator/vitest-runner": "^9.5.1",
|
|
68
|
+
"@types/d3-dsv": "^3.0.7"
|
|
69
|
+
},
|
|
70
|
+
"scripts": {
|
|
71
|
+
"build": "pnpm clean && tsc",
|
|
72
|
+
"clean": "rimraf dist",
|
|
73
|
+
"test": "pnpm run test:ts",
|
|
74
|
+
"test:ts": "vitest run",
|
|
75
|
+
"test:go": "echo 'No Go tests in hdf-utilities'",
|
|
76
|
+
"test:watch": "vitest",
|
|
77
|
+
"test:coverage": "vitest run --coverage",
|
|
78
|
+
"type-check": "tsc --noEmit",
|
|
79
|
+
"lint": "eslint src test",
|
|
80
|
+
"lint:fix": "eslint src test --fix"
|
|
81
|
+
}
|
|
82
|
+
}
|