xml-crypto-next 7.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/lib/utils.js ADDED
@@ -0,0 +1,271 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BASE64_REGEX = exports.EXTRACT_X509_CERTS = exports.PEM_FORMAT_REGEX = void 0;
4
+ exports.isArrayHasLength = isArrayHasLength;
5
+ exports.findAttr = findAttr;
6
+ exports.findChildren = findChildren;
7
+ exports.findChilds = findChilds;
8
+ exports.encodeSpecialCharactersInAttribute = encodeSpecialCharactersInAttribute;
9
+ exports.encodeSpecialCharactersInText = encodeSpecialCharactersInText;
10
+ exports.normalizePem = normalizePem;
11
+ exports.pemToDer = pemToDer;
12
+ exports.derToPem = derToPem;
13
+ exports.findAncestorNs = findAncestorNs;
14
+ exports.validateDigestValue = validateDigestValue;
15
+ exports.isDescendantOf = isDescendantOf;
16
+ const xpath = require("xpath");
17
+ const isDomNode = require("@xmldom/is-dom-node");
18
+ function isArrayHasLength(array) {
19
+ return Array.isArray(array) && array.length > 0;
20
+ }
21
+ function attrEqualsExplicitly(attr, localName, namespace) {
22
+ return attr.localName === localName && (attr.namespaceURI === namespace || namespace == null);
23
+ }
24
+ function attrEqualsImplicitly(attr, localName, namespace, node) {
25
+ return (attr.localName === localName &&
26
+ ((!attr.namespaceURI && node?.namespaceURI === namespace) || namespace == null));
27
+ }
28
+ function findAttr(element, localName, namespace) {
29
+ for (let i = 0; i < element.attributes.length; i++) {
30
+ const attr = element.attributes[i];
31
+ if (attrEqualsExplicitly(attr, localName, namespace) ||
32
+ attrEqualsImplicitly(attr, localName, namespace, element)) {
33
+ return attr;
34
+ }
35
+ }
36
+ return null;
37
+ }
38
+ function findChildren(node, localName, namespace) {
39
+ const element = node.documentElement ?? node;
40
+ const res = [];
41
+ for (let i = 0; i < element.childNodes.length; i++) {
42
+ const child = element.childNodes[i];
43
+ if (isDomNode.isElementNode(child) &&
44
+ child.localName === localName &&
45
+ (child.namespaceURI === namespace || namespace == null)) {
46
+ res.push(child);
47
+ }
48
+ }
49
+ return res;
50
+ }
51
+ /** @deprecated */
52
+ function findChilds(node, localName, namespace) {
53
+ return findChildren(node, localName, namespace);
54
+ }
55
+ const xml_special_to_encoded_attribute = {
56
+ "&": "&amp;",
57
+ "<": "&lt;",
58
+ '"': "&quot;",
59
+ "\r": "&#xD;",
60
+ "\n": "&#xA;",
61
+ "\t": "&#x9;",
62
+ };
63
+ const xml_special_to_encoded_text = {
64
+ "&": "&amp;",
65
+ "<": "&lt;",
66
+ ">": "&gt;",
67
+ "\r": "&#xD;",
68
+ };
69
+ function encodeSpecialCharactersInAttribute(attributeValue) {
70
+ return attributeValue.replace(/([&<"\r\n\t])/g, function (str, item) {
71
+ /** Special character normalization.
72
+ * @see:
73
+ * - https://www.w3.org/TR/xml-c14n#ProcessingModel (Attribute Nodes)
74
+ * - https://www.w3.org/TR/xml-c14n#Example-Chars
75
+ */
76
+ return xml_special_to_encoded_attribute[item];
77
+ });
78
+ }
79
+ function encodeSpecialCharactersInText(text) {
80
+ return text.replace(/([&<>\r])/g, function (str, item) {
81
+ /** Special character normalization.
82
+ * @see:
83
+ * - https://www.w3.org/TR/xml-c14n#ProcessingModel (Text Nodes)
84
+ * - https://www.w3.org/TR/xml-c14n#Example-Chars
85
+ */
86
+ return xml_special_to_encoded_text[item];
87
+ });
88
+ }
89
+ /**
90
+ * PEM format has wide range of usages, but this library
91
+ * is enforcing RFC7468 which focuses on PKIX, PKCS and CMS.
92
+ *
93
+ * https://www.rfc-editor.org/rfc/rfc7468
94
+ *
95
+ * PEM_FORMAT_REGEX is validating given PEM file against RFC7468 'stricttextualmsg' definition.
96
+ *
97
+ * With few exceptions;
98
+ * - 'posteb' MAY have 'eol', but it is not mandatory.
99
+ * - 'preeb' and 'posteb' lines are limited to 64 characters, but
100
+ * should not cause any issues in context of PKIX, PKCS and CMS.
101
+ */
102
+ exports.PEM_FORMAT_REGEX = new RegExp("^-----BEGIN [A-Z\x20]{1,48}-----([^-]*)-----END [A-Z\x20]{1,48}-----$", "s");
103
+ exports.EXTRACT_X509_CERTS = new RegExp("-----BEGIN CERTIFICATE-----[^-]*-----END CERTIFICATE-----", "g");
104
+ exports.BASE64_REGEX = new RegExp("^(?:[A-Za-z0-9\\+\\/]{4}\\n{0,1})*(?:[A-Za-z0-9\\+\\/]{2}==|[A-Za-z0-9\\+\\/]{3}=)?$", "s");
105
+ /**
106
+ * -----BEGIN [LABEL]-----
107
+ * base64([DATA])
108
+ * -----END [LABEL]-----
109
+ *
110
+ * Above is shown what PEM file looks like. As can be seen, base64 data
111
+ * can be in single line or multiple lines.
112
+ *
113
+ * This function normalizes PEM presentation to;
114
+ * - contain PEM header and footer as they are given
115
+ * - normalize line endings to '\n'
116
+ * - normalize line length to maximum of 64 characters
117
+ * - ensure that 'preeb' has line ending '\n'
118
+ *
119
+ * With a couple of notes:
120
+ * - 'eol' is normalized to '\n'
121
+ *
122
+ * @param pem The PEM string to normalize to RFC7468 'stricttextualmsg' definition
123
+ */
124
+ function normalizePem(pem) {
125
+ return `${(pem
126
+ .trim()
127
+ .replace(/(\r\n|\r)/g, "\n")
128
+ .match(/.{1,64}/g) ?? []).join("\n")}\n`;
129
+ }
130
+ /**
131
+ * @param pem The PEM-encoded base64 certificate to strip headers from
132
+ */
133
+ function pemToDer(pem) {
134
+ if (!exports.PEM_FORMAT_REGEX.test(pem.trim())) {
135
+ throw new Error("Invalid PEM format.");
136
+ }
137
+ return Buffer.from(pem
138
+ .replace(/(\r\n|\r)/g, "")
139
+ .replace(/-----BEGIN [A-Z\x20]{1,48}-----\n?/, "")
140
+ .replace(/-----END [A-Z\x20]{1,48}-----\n?/, ""), "base64");
141
+ }
142
+ /**
143
+ * @param der The DER-encoded base64 certificate to add PEM headers too
144
+ * @param pemLabel The label of the header and footer to add
145
+ */
146
+ function derToPem(der, pemLabel) {
147
+ const base64Der = Buffer.isBuffer(der)
148
+ ? der.toString("base64").trim()
149
+ : der.replace(/(\r\n|\r)/g, "").trim();
150
+ if (exports.PEM_FORMAT_REGEX.test(base64Der)) {
151
+ return normalizePem(base64Der);
152
+ }
153
+ if (exports.BASE64_REGEX.test(base64Der.replace(/ /g, ""))) {
154
+ if (pemLabel == null) {
155
+ throw new Error("PEM label is required when DER is given.");
156
+ }
157
+ const pem = `-----BEGIN ${pemLabel}-----\n${base64Der.replace(/ /g, "")}\n-----END ${pemLabel}-----`;
158
+ return normalizePem(pem);
159
+ }
160
+ throw new Error("Unknown DER format.");
161
+ }
162
+ function collectAncestorNamespaces(node, nsArray = []) {
163
+ if (!isDomNode.isElementNode(node.parentNode)) {
164
+ return nsArray;
165
+ }
166
+ const parent = node.parentNode;
167
+ if (!parent) {
168
+ return nsArray;
169
+ }
170
+ if (parent.attributes && parent.attributes.length > 0) {
171
+ for (let i = 0; i < parent.attributes.length; i++) {
172
+ const attr = parent.attributes[i];
173
+ if (attr && attr.nodeName && attr.nodeName.search(/^xmlns:?/) !== -1) {
174
+ nsArray.push({
175
+ prefix: attr.nodeName.replace(/^xmlns:?/, ""),
176
+ namespaceURI: attr.nodeValue || "",
177
+ });
178
+ }
179
+ }
180
+ }
181
+ return collectAncestorNamespaces(parent, nsArray);
182
+ }
183
+ function findNSPrefix(subset) {
184
+ const subsetAttributes = subset.attributes;
185
+ for (let k = 0; k < subsetAttributes.length; k++) {
186
+ const nodeName = subsetAttributes[k].nodeName;
187
+ if (nodeName.search(/^xmlns:?/) !== -1) {
188
+ return nodeName.replace(/^xmlns:?/, "");
189
+ }
190
+ }
191
+ return subset.prefix || "";
192
+ }
193
+ function isElementSubset(docSubset) {
194
+ return docSubset.every((node) => isDomNode.isElementNode(node));
195
+ }
196
+ /**
197
+ * Extract ancestor namespaces in order to import it to root of document subset
198
+ * which is being canonicalized for non-exclusive c14n.
199
+ *
200
+ * @param doc - Usually a product from `new xmldom.DOMParser().parseFromString()`
201
+ * @param docSubsetXpath - xpath query to get document subset being canonicalized
202
+ * @param namespaceResolver - xpath namespace resolver
203
+ * @returns i.e. [{prefix: "saml", namespaceURI: "urn:oasis:names:tc:SAML:2.0:assertion"}]
204
+ */
205
+ function findAncestorNs(doc, docSubsetXpath, namespaceResolver) {
206
+ if (docSubsetXpath == null) {
207
+ return [];
208
+ }
209
+ const docSubset = xpath.selectWithResolver(docSubsetXpath, doc, namespaceResolver);
210
+ if (!isArrayHasLength(docSubset)) {
211
+ return [];
212
+ }
213
+ if (!isElementSubset(docSubset)) {
214
+ throw new Error("Document subset must be list of elements");
215
+ }
216
+ // Remove duplicate on ancestor namespace
217
+ const ancestorNs = collectAncestorNamespaces(docSubset[0]);
218
+ const ancestorNsWithoutDuplicate = [];
219
+ for (let i = 0; i < ancestorNs.length; i++) {
220
+ let notOnTheList = true;
221
+ for (const v in ancestorNsWithoutDuplicate) {
222
+ if (ancestorNsWithoutDuplicate[v].prefix === ancestorNs[i].prefix) {
223
+ notOnTheList = false;
224
+ break;
225
+ }
226
+ }
227
+ if (notOnTheList) {
228
+ ancestorNsWithoutDuplicate.push(ancestorNs[i]);
229
+ }
230
+ }
231
+ // Remove namespaces which are already declared in the subset with the same prefix
232
+ const returningNs = [];
233
+ const subsetNsPrefix = findNSPrefix(docSubset[0]);
234
+ for (const ancestorNs of ancestorNsWithoutDuplicate) {
235
+ if (ancestorNs.prefix !== subsetNsPrefix) {
236
+ returningNs.push(ancestorNs);
237
+ }
238
+ }
239
+ return returningNs;
240
+ }
241
+ function validateDigestValue(digest, expectedDigest) {
242
+ const buffer = Buffer.from(digest, "base64");
243
+ const expectedBuffer = Buffer.from(expectedDigest, "base64");
244
+ if (typeof buffer.equals === "function") {
245
+ return buffer.equals(expectedBuffer);
246
+ }
247
+ if (buffer.length !== expectedBuffer.length) {
248
+ return false;
249
+ }
250
+ for (let i = 0; i < buffer.length; i++) {
251
+ if (buffer[i] !== expectedBuffer[i]) {
252
+ return false;
253
+ }
254
+ }
255
+ return true;
256
+ }
257
+ // Check if the given node is descendant of the given parent node
258
+ function isDescendantOf(node, parent) {
259
+ if (!node || !parent) {
260
+ return false;
261
+ }
262
+ let currentNode = node.parentNode;
263
+ while (currentNode) {
264
+ if (currentNode === parent) {
265
+ return true;
266
+ }
267
+ currentNode = currentNode.parentNode;
268
+ }
269
+ return false;
270
+ }
271
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import * as xpath from \"xpath\";\r\nimport type { NamespacePrefix } from \"./types\";\r\nimport * as isDomNode from \"@xmldom/is-dom-node\";\r\n\r\nexport function isArrayHasLength(array: unknown): array is unknown[] {\r\n return Array.isArray(array) && array.length > 0;\r\n}\r\n\r\nfunction attrEqualsExplicitly(attr: Attr, localName: string, namespace?: string) {\r\n return attr.localName === localName && (attr.namespaceURI === namespace || namespace == null);\r\n}\r\n\r\nfunction attrEqualsImplicitly(attr: Attr, localName: string, namespace?: string, node?: Element) {\r\n return (\r\n attr.localName === localName &&\r\n ((!attr.namespaceURI && node?.namespaceURI === namespace) || namespace == null)\r\n );\r\n}\r\n\r\nexport function findAttr(element: Element, localName: string, namespace?: string) {\r\n for (let i = 0; i < element.attributes.length; i++) {\r\n const attr = element.attributes[i];\r\n\r\n if (\r\n attrEqualsExplicitly(attr, localName, namespace) ||\r\n attrEqualsImplicitly(attr, localName, namespace, element)\r\n ) {\r\n return attr;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nexport function findChildren(node: Node | Document, localName: string, namespace?: string) {\r\n const element = (node as Document).documentElement ?? node;\r\n const res: Element[] = [];\r\n for (let i = 0; i < element.childNodes.length; i++) {\r\n const child = element.childNodes[i];\r\n if (\r\n isDomNode.isElementNode(child) &&\r\n child.localName === localName &&\r\n (child.namespaceURI === namespace || namespace == null)\r\n ) {\r\n res.push(child);\r\n }\r\n }\r\n return res;\r\n}\r\n\r\n/** @deprecated */\r\nexport function findChilds(node: Node | Document, localName: string, namespace?: string) {\r\n return findChildren(node, localName, namespace);\r\n}\r\n\r\nconst xml_special_to_encoded_attribute = {\r\n \"&\": \"&amp;\",\r\n \"<\": \"&lt;\",\r\n '\"': \"&quot;\",\r\n \"\\r\": \"&#xD;\",\r\n \"\\n\": \"&#xA;\",\r\n \"\\t\": \"&#x9;\",\r\n};\r\n\r\nconst xml_special_to_encoded_text = {\r\n \"&\": \"&amp;\",\r\n \"<\": \"&lt;\",\r\n \">\": \"&gt;\",\r\n \"\\r\": \"&#xD;\",\r\n};\r\n\r\nexport function encodeSpecialCharactersInAttribute(attributeValue) {\r\n return attributeValue.replace(/([&<\"\\r\\n\\t])/g, function (str, item) {\r\n /** Special character normalization.\r\n * @see:\r\n * - https://www.w3.org/TR/xml-c14n#ProcessingModel (Attribute Nodes)\r\n * - https://www.w3.org/TR/xml-c14n#Example-Chars\r\n */\r\n return xml_special_to_encoded_attribute[item];\r\n });\r\n}\r\n\r\nexport function encodeSpecialCharactersInText(text: string): string {\r\n return text.replace(/([&<>\\r])/g, function (str, item) {\r\n /** Special character normalization.\r\n * @see:\r\n * - https://www.w3.org/TR/xml-c14n#ProcessingModel (Text Nodes)\r\n * - https://www.w3.org/TR/xml-c14n#Example-Chars\r\n */\r\n return xml_special_to_encoded_text[item];\r\n });\r\n}\r\n\r\n/**\r\n * PEM format has wide range of usages, but this library\r\n * is enforcing RFC7468 which focuses on PKIX, PKCS and CMS.\r\n *\r\n * https://www.rfc-editor.org/rfc/rfc7468\r\n *\r\n * PEM_FORMAT_REGEX is validating given PEM file against RFC7468 'stricttextualmsg' definition.\r\n *\r\n * With few exceptions;\r\n * - 'posteb' MAY have 'eol', but it is not mandatory.\r\n * - 'preeb' and 'posteb' lines are limited to 64 characters, but\r\n * should not cause any issues in context of PKIX, PKCS and CMS.\r\n */\r\nexport const PEM_FORMAT_REGEX = new RegExp(\r\n \"^-----BEGIN [A-Z\\x20]{1,48}-----([^-]*)-----END [A-Z\\x20]{1,48}-----$\",\r\n \"s\",\r\n);\r\nexport const EXTRACT_X509_CERTS = new RegExp(\r\n \"-----BEGIN CERTIFICATE-----[^-]*-----END CERTIFICATE-----\",\r\n \"g\",\r\n);\r\nexport const BASE64_REGEX = new RegExp(\r\n \"^(?:[A-Za-z0-9\\\\+\\\\/]{4}\\\\n{0,1})*(?:[A-Za-z0-9\\\\+\\\\/]{2}==|[A-Za-z0-9\\\\+\\\\/]{3}=)?$\",\r\n \"s\",\r\n);\r\n\r\n/**\r\n * -----BEGIN [LABEL]-----\r\n * base64([DATA])\r\n * -----END [LABEL]-----\r\n *\r\n * Above is shown what PEM file looks like. As can be seen, base64 data\r\n * can be in single line or multiple lines.\r\n *\r\n * This function normalizes PEM presentation to;\r\n * - contain PEM header and footer as they are given\r\n * - normalize line endings to '\\n'\r\n * - normalize line length to maximum of 64 characters\r\n * - ensure that 'preeb' has line ending '\\n'\r\n *\r\n * With a couple of notes:\r\n * - 'eol' is normalized to '\\n'\r\n *\r\n * @param pem The PEM string to normalize to RFC7468 'stricttextualmsg' definition\r\n */\r\nexport function normalizePem(pem: string): string {\r\n return `${(\r\n pem\r\n .trim()\r\n .replace(/(\\r\\n|\\r)/g, \"\\n\")\r\n .match(/.{1,64}/g) ?? []\r\n ).join(\"\\n\")}\\n`;\r\n}\r\n\r\n/**\r\n * @param pem The PEM-encoded base64 certificate to strip headers from\r\n */\r\nexport function pemToDer(pem: string): Buffer {\r\n if (!PEM_FORMAT_REGEX.test(pem.trim())) {\r\n throw new Error(\"Invalid PEM format.\");\r\n }\r\n\r\n return Buffer.from(\r\n pem\r\n .replace(/(\\r\\n|\\r)/g, \"\")\r\n .replace(/-----BEGIN [A-Z\\x20]{1,48}-----\\n?/, \"\")\r\n .replace(/-----END [A-Z\\x20]{1,48}-----\\n?/, \"\"),\r\n \"base64\",\r\n );\r\n}\r\n\r\n/**\r\n * @param der The DER-encoded base64 certificate to add PEM headers too\r\n * @param pemLabel The label of the header and footer to add\r\n */\r\nexport function derToPem(\r\n der: string | Buffer,\r\n pemLabel?: \"CERTIFICATE\" | \"PRIVATE KEY\" | \"RSA PUBLIC KEY\",\r\n): string {\r\n const base64Der = Buffer.isBuffer(der)\r\n ? der.toString(\"base64\").trim()\r\n : der.replace(/(\\r\\n|\\r)/g, \"\").trim();\r\n\r\n if (PEM_FORMAT_REGEX.test(base64Der)) {\r\n return normalizePem(base64Der);\r\n }\r\n\r\n if (BASE64_REGEX.test(base64Der.replace(/ /g, \"\"))) {\r\n if (pemLabel == null) {\r\n throw new Error(\"PEM label is required when DER is given.\");\r\n }\r\n const pem = `-----BEGIN ${pemLabel}-----\\n${base64Der.replace(\r\n / /g,\r\n \"\",\r\n )}\\n-----END ${pemLabel}-----`;\r\n\r\n return normalizePem(pem);\r\n }\r\n\r\n throw new Error(\"Unknown DER format.\");\r\n}\r\n\r\nfunction collectAncestorNamespaces(\r\n node: Element,\r\n nsArray: NamespacePrefix[] = [],\r\n): NamespacePrefix[] {\r\n if (!isDomNode.isElementNode(node.parentNode)) {\r\n return nsArray;\r\n }\r\n\r\n const parent: Element = node.parentNode;\r\n\r\n if (!parent) {\r\n return nsArray;\r\n }\r\n\r\n if (parent.attributes && parent.attributes.length > 0) {\r\n for (let i = 0; i < parent.attributes.length; i++) {\r\n const attr = parent.attributes[i];\r\n if (attr && attr.nodeName && attr.nodeName.search(/^xmlns:?/) !== -1) {\r\n nsArray.push({\r\n prefix: attr.nodeName.replace(/^xmlns:?/, \"\"),\r\n namespaceURI: attr.nodeValue || \"\",\r\n });\r\n }\r\n }\r\n }\r\n\r\n return collectAncestorNamespaces(parent, nsArray);\r\n}\r\n\r\nfunction findNSPrefix(subset) {\r\n const subsetAttributes = subset.attributes;\r\n for (let k = 0; k < subsetAttributes.length; k++) {\r\n const nodeName = subsetAttributes[k].nodeName;\r\n if (nodeName.search(/^xmlns:?/) !== -1) {\r\n return nodeName.replace(/^xmlns:?/, \"\");\r\n }\r\n }\r\n return subset.prefix || \"\";\r\n}\r\n\r\nfunction isElementSubset(docSubset: Node[]): docSubset is Element[] {\r\n return docSubset.every((node) => isDomNode.isElementNode(node));\r\n}\r\n\r\n/**\r\n * Extract ancestor namespaces in order to import it to root of document subset\r\n * which is being canonicalized for non-exclusive c14n.\r\n *\r\n * @param doc - Usually a product from `new xmldom.DOMParser().parseFromString()`\r\n * @param docSubsetXpath - xpath query to get document subset being canonicalized\r\n * @param namespaceResolver - xpath namespace resolver\r\n * @returns i.e. [{prefix: \"saml\", namespaceURI: \"urn:oasis:names:tc:SAML:2.0:assertion\"}]\r\n */\r\nexport function findAncestorNs(\r\n doc: Document,\r\n docSubsetXpath?: string,\r\n namespaceResolver?: XPathNSResolver,\r\n): NamespacePrefix[] {\r\n if (docSubsetXpath == null) {\r\n return [];\r\n }\r\n\r\n const docSubset = xpath.selectWithResolver(docSubsetXpath, doc, namespaceResolver);\r\n\r\n if (!isArrayHasLength(docSubset)) {\r\n return [];\r\n }\r\n\r\n if (!isElementSubset(docSubset)) {\r\n throw new Error(\"Document subset must be list of elements\");\r\n }\r\n\r\n // Remove duplicate on ancestor namespace\r\n const ancestorNs = collectAncestorNamespaces(docSubset[0]);\r\n const ancestorNsWithoutDuplicate: NamespacePrefix[] = [];\r\n for (let i = 0; i < ancestorNs.length; i++) {\r\n let notOnTheList = true;\r\n for (const v in ancestorNsWithoutDuplicate) {\r\n if (ancestorNsWithoutDuplicate[v].prefix === ancestorNs[i].prefix) {\r\n notOnTheList = false;\r\n break;\r\n }\r\n }\r\n\r\n if (notOnTheList) {\r\n ancestorNsWithoutDuplicate.push(ancestorNs[i]);\r\n }\r\n }\r\n\r\n // Remove namespaces which are already declared in the subset with the same prefix\r\n const returningNs: NamespacePrefix[] = [];\r\n const subsetNsPrefix = findNSPrefix(docSubset[0]);\r\n for (const ancestorNs of ancestorNsWithoutDuplicate) {\r\n if (ancestorNs.prefix !== subsetNsPrefix) {\r\n returningNs.push(ancestorNs);\r\n }\r\n }\r\n\r\n return returningNs;\r\n}\r\n\r\nexport function validateDigestValue(digest, expectedDigest) {\r\n const buffer = Buffer.from(digest, \"base64\");\r\n const expectedBuffer = Buffer.from(expectedDigest, \"base64\");\r\n\r\n if (typeof buffer.equals === \"function\") {\r\n return buffer.equals(expectedBuffer);\r\n }\r\n\r\n if (buffer.length !== expectedBuffer.length) {\r\n return false;\r\n }\r\n\r\n for (let i = 0; i < buffer.length; i++) {\r\n if (buffer[i] !== expectedBuffer[i]) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n\r\n// Check if the given node is descendant of the given parent node\r\nexport function isDescendantOf(node: Node, parent: Node): boolean {\r\n if (!node || !parent) {\r\n return false;\r\n }\r\n\r\n let currentNode: Node | null = node.parentNode;\r\n\r\n while (currentNode) {\r\n if (currentNode === parent) {\r\n return true;\r\n }\r\n currentNode = currentNode.parentNode;\r\n }\r\n\r\n return false;\r\n}\r\n"]}
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "xml-crypto-next",
3
+ "version": "7.0.0",
4
+ "private": false,
5
+ "description": "Xml digital signature and encryption library for Node.js",
6
+ "keywords": [
7
+ "xml",
8
+ "digital signature",
9
+ "xml encryption",
10
+ "x.509 certificate"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/node-saml/xml-crypto.git"
15
+ },
16
+ "license": "MIT",
17
+ "author": "Yaron Naveh <yaronn01@gmail.com> (http://webservices20.blogspot.com/)",
18
+ "contributors": [
19
+ "LoneRifle <LoneRifle@users.noreply.github.com>",
20
+ "Chris Barth <chrisjbarth@hotmail.com>"
21
+ ],
22
+ "main": "./lib",
23
+ "files": [
24
+ "lib",
25
+ "LICENSE",
26
+ "README.md"
27
+ ],
28
+ "scripts": {
29
+ "build": "tsc",
30
+ "changelog": "gren changelog --override --generate",
31
+ "lint": "eslint \"{src,test}/*.ts\" --cache && npm run prettier-check",
32
+ "lint:fix": "eslint --fix \"{src,test}/*.ts\" && npm run prettier-format",
33
+ "prepare": "tsc",
34
+ "prettier-check": "prettier --config .prettierrc.json --check .",
35
+ "prettier-format": "prettier --config .prettierrc.json --write .",
36
+ "prerelease": "git clean -xfd && npm ci && npm test",
37
+ "release": "release-it",
38
+ "test": "nyc mocha"
39
+ },
40
+ "dependencies": {
41
+ "@xmldom/is-dom-node": "^1.0.1",
42
+ "@xmldom/xmldom": "^0.8.10",
43
+ "xpath": "^0.0.33"
44
+ },
45
+ "devDependencies": {
46
+ "@cjbarth/github-release-notes": "^4.2.0",
47
+ "@istanbuljs/nyc-config-typescript": "^1.0.2",
48
+ "@prettier/plugin-xml": "^3.2.2",
49
+ "@types/chai": "^4.3.11",
50
+ "@types/mocha": "^10.0.6",
51
+ "@types/node": "^16.18.69",
52
+ "@typescript-eslint/eslint-plugin": "^6.18.1",
53
+ "@typescript-eslint/parser": "^6.18.1",
54
+ "chai": "^4.3.10",
55
+ "choma": "^1.2.1",
56
+ "ejs": "^3.1.9",
57
+ "eslint": "^8.56.0",
58
+ "eslint-config-prettier": "^9.0.0",
59
+ "eslint-plugin-deprecation": "^2.0.0",
60
+ "lcov": "^1.16.0",
61
+ "mocha": "^10.2.0",
62
+ "nyc": "^15.1.0",
63
+ "prettier": "^3.1.0",
64
+ "prettier-plugin-packagejson": "^2.4.6",
65
+ "release-it": "^16.3.0",
66
+ "source-map-support": "^0.5.21",
67
+ "ts-node": "^10.9.1",
68
+ "typescript": "^5.3.2"
69
+ },
70
+ "engines": {
71
+ "node": ">=16"
72
+ }
73
+ }