@gi.ts/parser 4.0.0-beta.40 → 4.0.0-beta.41

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gi.ts/parser",
3
- "version": "4.0.0-beta.40",
3
+ "version": "4.0.0-beta.41",
4
4
  "description": "Parser for GObject Introspection XML files",
5
5
  "main": "src/index.ts",
6
6
  "module": "src/index.ts",
@@ -29,9 +29,10 @@
29
29
  "check": "tsc --noEmit"
30
30
  },
31
31
  "dependencies": {
32
- "fast-xml-parser": "^5.4.2"
32
+ "fast-xml-parser": "^5.5.7"
33
33
  },
34
34
  "devDependencies": {
35
+ "@ts-for-gir/tsconfig": "^4.0.0-beta.41",
35
36
  "typescript": "^5.9.3"
36
37
  }
37
38
  }
package/src/gir-types.ts CHANGED
@@ -842,6 +842,8 @@ export interface GirCallbackElement extends PartOfModule, GirInfoElements {
842
842
  "c:type"?: string;
843
843
  /** Binary attribute, true if the callback can throw an error */
844
844
  throws?: GirBoolean;
845
+ /** GObject compatible type name of the callback */
846
+ "glib:type-name"?: string;
845
847
  };
846
848
 
847
849
  /* Other elements a property can contain */
package/src/parser.ts CHANGED
@@ -1,6 +1,19 @@
1
1
  import { XMLParser } from "fast-xml-parser";
2
2
  import type { GirXML } from "./gir-types.ts";
3
3
 
4
+ /**
5
+ * fast-xml-parser v5.5.5+ blocks "constructor" as a tag name to prevent
6
+ * prototype pollution. GIR files legitimately use <constructor> elements,
7
+ * so we rename it during parsing and restore it in post-processing.
8
+ */
9
+ const CRITICAL_TAG_REPLACEMENTS: Record<string, string> = {
10
+ constructor: "__gir_constructor__",
11
+ };
12
+
13
+ const CRITICAL_TAG_RESTORATIONS: Record<string, string> = Object.fromEntries(
14
+ Object.entries(CRITICAL_TAG_REPLACEMENTS).map(([k, v]) => [v, k]),
15
+ );
16
+
4
17
  // TODO: Treat properties that contain only one element like `repository`, 'namespace', 'package', ... as an object instead of an array
5
18
  const isArrayProperty = [
6
19
  "type",
@@ -11,7 +24,7 @@ const isArrayProperty = [
11
24
  "parameters",
12
25
  "return-value",
13
26
  "class",
14
- "constructor",
27
+ "__gir_constructor__",
15
28
  "constructors",
16
29
  "method",
17
30
  "virtual-method",
@@ -64,6 +77,11 @@ const parser = new XMLParser({
64
77
  parseTagValue: true,
65
78
  parseAttributeValue: false,
66
79
  trimValues: true,
80
+ processEntities: {
81
+ enabled: true,
82
+ maxTotalExpansions: 100_000,
83
+ },
84
+ transformTagName: (tagName: string) => CRITICAL_TAG_REPLACEMENTS[tagName] ?? tagName,
67
85
  isArray: (name, _jpath, isLeafNode, _isAttribute) => {
68
86
  // Restore previous behaviour...
69
87
  if (isArrayProperty.includes(name)) {
@@ -74,17 +92,17 @@ const parser = new XMLParser({
74
92
  });
75
93
 
76
94
  /**
77
- * Recursively transforms numeric string attributes to actual numbers.
78
- * This ensures type safety while maintaining clean separation of concerns:
79
- * the parser handles data transformation, not the consuming lib.
95
+ * Recursively post-processes the parsed XML tree:
96
+ * - Converts numeric string attributes to actual numbers
97
+ * - Restores tag names that were renamed to bypass fast-xml-parser's security checks
80
98
  */
81
- function transformNumericAttributes(obj: unknown): unknown {
99
+ function postProcessParsedXml(obj: unknown): unknown {
82
100
  if (obj === null || typeof obj !== "object") {
83
101
  return obj;
84
102
  }
85
103
 
86
104
  if (Array.isArray(obj)) {
87
- return obj.map(transformNumericAttributes);
105
+ return obj.map(postProcessParsedXml);
88
106
  }
89
107
 
90
108
  const result = { ...obj } as Record<string, unknown>;
@@ -102,10 +120,15 @@ function transformNumericAttributes(obj: unknown): unknown {
102
120
  }
103
121
  }
104
122
 
105
- // Recursively transform nested objects
123
+ // Recursively transform nested objects and restore renamed tag keys
106
124
  for (const key in result) {
107
125
  if (key !== "$" && result[key] !== null && typeof result[key] === "object") {
108
- result[key] = transformNumericAttributes(result[key]);
126
+ result[key] = postProcessParsedXml(result[key]);
127
+ }
128
+ const restoredKey = CRITICAL_TAG_RESTORATIONS[key];
129
+ if (restoredKey) {
130
+ result[restoredKey] = result[key];
131
+ delete result[key];
109
132
  }
110
133
  }
111
134
 
@@ -114,5 +137,5 @@ function transformNumericAttributes(obj: unknown): unknown {
114
137
 
115
138
  export function parseGir(contents: string): GirXML {
116
139
  const parsed = parser.parse(contents);
117
- return transformNumericAttributes(parsed) as GirXML;
140
+ return postProcessParsedXml(parsed) as GirXML;
118
141
  }