samlify 2.12.0 → 2.13.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.
Files changed (84) hide show
  1. package/README.md +1 -1
  2. package/build/src/api.js +41 -3
  3. package/build/src/api.js.map +1 -1
  4. package/build/src/binding-post.js +236 -182
  5. package/build/src/binding-post.js.map +1 -1
  6. package/build/src/binding-redirect.js +303 -215
  7. package/build/src/binding-redirect.js.map +1 -1
  8. package/build/src/binding-simplesign.js +285 -137
  9. package/build/src/binding-simplesign.js.map +1 -1
  10. package/build/src/entity-idp.js +130 -47
  11. package/build/src/entity-idp.js.map +1 -1
  12. package/build/src/entity-sp.js +81 -39
  13. package/build/src/entity-sp.js.map +1 -1
  14. package/build/src/entity.js +100 -62
  15. package/build/src/entity.js.map +1 -1
  16. package/build/src/extractor.js +118 -151
  17. package/build/src/extractor.js.map +1 -1
  18. package/build/src/flow.js +100 -96
  19. package/build/src/flow.js.map +1 -1
  20. package/build/src/libsaml.js +315 -259
  21. package/build/src/libsaml.js.map +1 -1
  22. package/build/src/metadata-idp.js +60 -30
  23. package/build/src/metadata-idp.js.map +1 -1
  24. package/build/src/metadata-sp.js +51 -41
  25. package/build/src/metadata-sp.js.map +1 -1
  26. package/build/src/metadata.js +47 -43
  27. package/build/src/metadata.js.map +1 -1
  28. package/build/src/options.js +73 -0
  29. package/build/src/options.js.map +1 -0
  30. package/build/src/urn.js +28 -1
  31. package/build/src/urn.js.map +1 -1
  32. package/build/src/utility.js +140 -85
  33. package/build/src/utility.js.map +1 -1
  34. package/build/src/validator.js +27 -10
  35. package/build/src/validator.js.map +1 -1
  36. package/package.json +16 -5
  37. package/types/src/api.d.ts +33 -3
  38. package/types/src/binding-post.d.ts +67 -34
  39. package/types/src/binding-redirect.d.ts +58 -31
  40. package/types/src/binding-simplesign.d.ts +77 -21
  41. package/types/src/entity-idp.d.ts +40 -31
  42. package/types/src/entity-sp.d.ts +37 -27
  43. package/types/src/entity.d.ts +71 -77
  44. package/types/src/extractor.d.ts +31 -22
  45. package/types/src/flow.d.ts +24 -2
  46. package/types/src/libsaml.d.ts +172 -118
  47. package/types/src/metadata-idp.d.ts +27 -11
  48. package/types/src/metadata-sp.d.ts +29 -19
  49. package/types/src/metadata.d.ts +59 -34
  50. package/types/src/options.d.ts +37 -0
  51. package/types/src/types.d.ts +250 -24
  52. package/types/src/urn.d.ts +7 -0
  53. package/types/src/utility.d.ts +139 -90
  54. package/types/src/validator.d.ts +21 -0
  55. package/.circleci/config.yml +0 -98
  56. package/.editorconfig +0 -19
  57. package/.github/FUNDING.yml +0 -1
  58. package/.github/workflows/deploy-docs.yml +0 -56
  59. package/.pre-commit.sh +0 -15
  60. package/.snyk +0 -4
  61. package/Makefile +0 -25
  62. package/index.ts +0 -28
  63. package/samlify-2.11.0.tgz +0 -0
  64. package/src/api.ts +0 -48
  65. package/src/binding-post.ts +0 -336
  66. package/src/binding-redirect.ts +0 -335
  67. package/src/binding-simplesign.ts +0 -231
  68. package/src/entity-idp.ts +0 -145
  69. package/src/entity-sp.ts +0 -114
  70. package/src/entity.ts +0 -243
  71. package/src/extractor.ts +0 -399
  72. package/src/flow.ts +0 -469
  73. package/src/libsaml.ts +0 -779
  74. package/src/metadata-idp.ts +0 -146
  75. package/src/metadata-sp.ts +0 -203
  76. package/src/metadata.ts +0 -166
  77. package/src/types.ts +0 -127
  78. package/src/urn.ts +0 -210
  79. package/src/utility.ts +0 -259
  80. package/src/validator.ts +0 -44
  81. package/tsconfig.json +0 -41
  82. package/tslint.json +0 -35
  83. package/types.d.ts +0 -2
  84. package/vitest.config.ts +0 -12
@@ -1,9 +1,21 @@
1
1
  "use strict";
2
2
  /**
3
- * @file SamlLib.js
4
- * @author tngan
5
- * @desc A simple library including some common functions
6
- */
3
+ * @file libsaml.ts
4
+ * @author tngan
5
+ * @desc SAML primitives: templates, XML signing/verification, assertion
6
+ * encryption/decryption, and XPath helpers used by the higher-level flows.
7
+ */
8
+ var __assign = (this && this.__assign) || function () {
9
+ __assign = Object.assign || function(t) {
10
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
11
+ s = arguments[i];
12
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
13
+ t[p] = s[p];
14
+ }
15
+ return t;
16
+ };
17
+ return __assign.apply(this, arguments);
18
+ };
7
19
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
20
  if (k2 === undefined) k2 = k;
9
21
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -91,29 +103,34 @@ Object.defineProperty(exports, "__esModule", { value: true });
91
103
  var utility_1 = __importStar(require("./utility"));
92
104
  var urn_1 = require("./urn");
93
105
  var xpath_1 = require("xpath");
94
- function toNodeArray(result) {
95
- if (Array.isArray(result))
96
- return result;
97
- if (result != null && typeof result === 'object' && 'nodeType' in result)
98
- return [result];
99
- return [];
100
- }
101
106
  var node_rsa_1 = __importDefault(require("node-rsa"));
102
107
  var xml_crypto_1 = require("xml-crypto");
103
108
  var xmlenc = __importStar(require("@authenio/xml-encryption"));
104
- var utility_2 = require("./utility");
105
109
  var api_1 = require("./api");
106
110
  var xml_escape_1 = __importDefault(require("xml-escape"));
111
+ var crypto = __importStar(require("crypto"));
107
112
  var fs = __importStar(require("fs"));
108
113
  var signatureAlgorithms = urn_1.algorithms.signature;
109
114
  var digestAlgorithms = urn_1.algorithms.digest;
110
115
  var certUse = urn_1.wording.certUse;
111
116
  var urlParams = urn_1.wording.urlParams;
117
+ /** Coerce the heterogeneous return of `xpath.select` into a Node array. */
118
+ function toNodeArray(result) {
119
+ if (Array.isArray(result))
120
+ return result;
121
+ if (result != null && typeof result === 'object' && 'nodeType' in result) {
122
+ return [result];
123
+ }
124
+ return [];
125
+ }
112
126
  var libSaml = function () {
113
127
  /**
114
- * @desc helper function to get back the query param for redirect binding for SLO/SSO
115
- * @type {string}
116
- */
128
+ * Map a SAML URL parameter type onto its canonical query-string key
129
+ * (`SAMLRequest` or `SAMLResponse`).
130
+ *
131
+ * @param type SAML URL parameter name
132
+ * @returns `SAMLRequest` or `SAMLResponse`
133
+ */
117
134
  function getQueryParamByType(type) {
118
135
  if ([urlParams.logoutRequest, urlParams.samlRequest].indexOf(type) !== -1) {
119
136
  return 'SAMLRequest';
@@ -124,114 +141,187 @@ var libSaml = function () {
124
141
  throw new Error('ERR_UNDEFINED_QUERY_PARAMS');
125
142
  }
126
143
  /**
144
+ * Mapping from XML-DSig signature algorithm URIs to node-rsa schemes.
127
145
  *
146
+ * The PSS entry covers the redirect-binding detached signature path:
147
+ * `node-rsa` accepts `pss-sha256` directly as a `signingScheme` value.
148
+ * The `xmldsig-more#sha256-rsa-MGF1` URI (W3C Note, 2007-05) is listed
149
+ * in `xmldsig-core §6.4.2` as the recommended successor to PKCS#1 v1.5
150
+ * RSA-SHA256 (`saml-sec-consider §6.5`).
128
151
  */
129
152
  var nrsaAliasMapping = {
130
153
  'http://www.w3.org/2000/09/xmldsig#rsa-sha1': 'pkcs1-sha1',
131
154
  'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256': 'pkcs1-sha256',
132
155
  'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512': 'pkcs1-sha512',
156
+ 'http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1': 'pss-sha256',
157
+ };
158
+ /**
159
+ * RSASSA-PSS plugin class for `xml-crypto`'s `SignedXml.SignatureAlgorithms`
160
+ * registry (`xmldsig-core §6.4.2`).
161
+ *
162
+ * **Temporary shim.** xml-crypto already implements PSS-SHA256 on its
163
+ * master branch (PR node-saml/xml-crypto#488, merged 2025-10-17), but
164
+ * the latest published npm version (6.1.2, 2024-08) predates that
165
+ * commit. Once xml-crypto cuts a release containing #488, delete this
166
+ * class and the `registerPssAlgorithms` helper below — the URI alone
167
+ * in `algorithms.signature` is sufficient and the constructor will
168
+ * seed `SignatureAlgorithms` with the upstream implementation.
169
+ *
170
+ * `RSA_PKCS1_PSS_PADDING` with `RSA_PSS_SALTLEN_DIGEST` matches the
171
+ * MGF1+SHA-256 convention referenced by the `sha256-rsa-MGF1` URI
172
+ * (xmldsig-more, W3C Note 2007-05).
173
+ */
174
+ var pssPaddingOptions = {
175
+ padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
176
+ saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST,
133
177
  };
178
+ var RsaSha256Mgf1 = /** @class */ (function () {
179
+ function RsaSha256Mgf1() {
180
+ this.getSignature = function (signedInfo, privateKey) {
181
+ var signOpts = {
182
+ key: privateKey,
183
+ padding: pssPaddingOptions.padding,
184
+ saltLength: pssPaddingOptions.saltLength,
185
+ };
186
+ return crypto.sign('RSA-SHA256', Buffer.from(signedInfo), signOpts).toString('base64');
187
+ };
188
+ this.verifySignature = function (material, key, signatureValue) {
189
+ var verifyOpts = {
190
+ key: key,
191
+ padding: pssPaddingOptions.padding,
192
+ saltLength: pssPaddingOptions.saltLength,
193
+ };
194
+ return crypto.verify('RSA-SHA256', Buffer.from(material), verifyOpts, Buffer.from(signatureValue, 'base64'));
195
+ };
196
+ this.getAlgorithmName = function () { return 'http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1'; };
197
+ }
198
+ return RsaSha256Mgf1;
199
+ }());
200
+ /**
201
+ * Register the RSASSA-PSS SHA-256 signature class on a fresh `SignedXml`
202
+ * instance so the algorithm-agility surface from `xmldsig-core §6.4` is
203
+ * available for both signing and verification. PKCS#1 v1.5 entries are
204
+ * preserved untouched.
205
+ */
206
+ function registerPssAlgorithms(sig) {
207
+ sig.SignatureAlgorithms = __assign(__assign({}, sig.SignatureAlgorithms), { 'http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1': RsaSha256Mgf1 });
208
+ }
134
209
  /**
135
- * @desc Default login request template
136
- * @type {LoginRequestTemplate}
137
- */
210
+ * Default AuthnRequest XML template.
211
+ *
212
+ * Per saml-core §3.4.1, `ProtocolBinding`, `AssertionConsumerServiceURL`,
213
+ * and `AssertionConsumerServiceIndex` are all `use="optional"` and
214
+ * mutually exclusive — when the index is set, neither URL nor
215
+ * ProtocolBinding may be present. All three are placeholders here so
216
+ * that `replaceTagsByValue` drops whichever the caller leaves
217
+ * undefined (closes #437).
218
+ */
138
219
  var defaultLoginRequestTemplate = {
139
- context: '<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="{AssertionConsumerServiceURL}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:NameIDPolicy Format="{NameIDFormat}" AllowCreate="{AllowCreate}"/></samlp:AuthnRequest>',
220
+ context: '<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" ForceAuthn="{ForceAuthn}" IssueInstant="{IssueInstant}" Destination="{Destination}" ProtocolBinding="{ProtocolBinding}" AssertionConsumerServiceURL="{AssertionConsumerServiceURL}" AssertionConsumerServiceIndex="{AssertionConsumerServiceIndex}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:NameIDPolicy Format="{NameIDFormat}" AllowCreate="{AllowCreate}"/></samlp:AuthnRequest>',
140
221
  };
141
222
  /**
142
- * @desc Default logout request template
143
- * @type {LogoutRequestTemplate}
144
- */
223
+ * Default LogoutRequest XML template.
224
+ *
225
+ * The optional `<samlp:SessionIndex>` element (saml-core §3.7.1) is
226
+ * included with a placeholder body. When the caller leaves
227
+ * `user.sessionIndex` undefined, `replaceTagsByValue` drops the whole
228
+ * element from the rendered XML, matching the schema's
229
+ * `minOccurs="0"` declaration.
230
+ */
145
231
  var defaultLogoutRequestTemplate = {
146
- context: '<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}"><saml:Issuer>{Issuer}</saml:Issuer><saml:NameID Format="{NameIDFormat}">{NameID}</saml:NameID></samlp:LogoutRequest>',
232
+ context: '<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}"><saml:Issuer>{Issuer}</saml:Issuer><saml:NameID Format="{NameIDFormat}">{NameID}</saml:NameID><samlp:SessionIndex>{SessionIndex}</samlp:SessionIndex></samlp:LogoutRequest>',
147
233
  };
148
- /**
149
- * @desc Default AttributeStatement template
150
- * @type {AttributeStatementTemplate}
151
- */
234
+ /** Default AttributeStatement XML fragment template. */
152
235
  var defaultAttributeStatementTemplate = {
153
236
  context: '<saml:AttributeStatement>{Attributes}</saml:AttributeStatement>',
154
237
  };
155
- /**
156
- * @desc Default Attribute template
157
- * @type {AttributeTemplate}
158
- */
238
+ /** Default Attribute XML fragment template. */
159
239
  var defaultAttributeTemplate = {
160
240
  context: '<saml:Attribute Name="{Name}" NameFormat="{NameFormat}"><saml:AttributeValue xmlns:xs="{ValueXmlnsXs}" xmlns:xsi="{ValueXmlnsXsi}" xsi:type="{ValueXsiType}">{Value}</saml:AttributeValue></saml:Attribute>',
161
241
  };
162
- /**
163
- * @desc Default login response template
164
- * @type {LoginResponseTemplate}
165
- */
242
+ /** Default LoginResponse XML template. */
166
243
  var defaultLoginResponseTemplate = {
167
244
  context: '<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" InResponseTo="{InResponseTo}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="{StatusCode}"/></samlp:Status><saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{AssertionID}" Version="2.0" IssueInstant="{IssueInstant}"><saml:Issuer>{Issuer}</saml:Issuer><saml:Subject><saml:NameID Format="{NameIDFormat}">{NameID}</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="{SubjectConfirmationDataNotOnOrAfter}" Recipient="{SubjectRecipient}" InResponseTo="{InResponseTo}"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="{ConditionsNotBefore}" NotOnOrAfter="{ConditionsNotOnOrAfter}"><saml:AudienceRestriction><saml:Audience>{Audience}</saml:Audience></saml:AudienceRestriction></saml:Conditions>{AuthnStatement}{AttributeStatement}</saml:Assertion></samlp:Response>',
168
245
  attributes: [],
169
246
  additionalTemplates: {
170
- 'attributeStatementTemplate': defaultAttributeStatementTemplate,
171
- 'attributeTemplate': defaultAttributeTemplate
172
- }
247
+ attributeStatementTemplate: defaultAttributeStatementTemplate,
248
+ attributeTemplate: defaultAttributeTemplate,
249
+ },
173
250
  };
174
- /**
175
- * @desc Default logout response template
176
- * @type {LogoutResponseTemplate}
177
- */
251
+ /** Default LogoutResponse XML template. */
178
252
  var defaultLogoutResponseTemplate = {
179
253
  context: '<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" InResponseTo="{InResponseTo}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="{StatusCode}"/></samlp:Status></samlp:LogoutResponse>',
180
254
  };
181
255
  /**
182
- * @private
183
- * @desc Get the signing scheme alias by signature algorithms, used by the node-rsa module
184
- * @param {string} sigAlg signature algorithm
185
- * @return {string/null} signing algorithm short-hand for the module node-rsa
186
- */
256
+ * Map a SAML signature algorithm URI to its node-rsa signing scheme.
257
+ *
258
+ * - When `sigAlg` is omitted, the default RSA-SHA256 scheme is used
259
+ * (per `saml-bindings §3.4.4.1` recommendation).
260
+ * - When `sigAlg` is supplied but does not match a known URI, the
261
+ * function throws. Silently downgrading to RSA-SHA1 (the previous
262
+ * behaviour) was a verification-time vulnerability: an attacker
263
+ * could supply an unknown `SigAlg` query parameter to coerce
264
+ * verification onto SHA-1, which is collision-broken
265
+ * (`saml-sec-consider §6.5`, `xmldsig-core §6.4`).
266
+ *
267
+ * @param sigAlg signature algorithm URI
268
+ * @returns node-rsa signing scheme string
269
+ * @throws when `sigAlg` is supplied and does not match a supported URI
270
+ */
187
271
  function getSigningScheme(sigAlg) {
188
- if (sigAlg) {
189
- var algAlias = nrsaAliasMapping[sigAlg];
190
- if (!(algAlias === undefined)) {
191
- return algAlias;
192
- }
272
+ if (sigAlg === undefined) {
273
+ return nrsaAliasMapping[signatureAlgorithms.RSA_SHA256];
193
274
  }
194
- return nrsaAliasMapping[signatureAlgorithms.RSA_SHA1];
275
+ var algAlias = nrsaAliasMapping[sigAlg];
276
+ if (algAlias === undefined) {
277
+ throw new Error('ERR_UNSUPPORTED_SIGNATURE_ALGORITHM');
278
+ }
279
+ return algAlias;
195
280
  }
196
281
  /**
197
- * @private
198
- * @desc Get the digest algorithms by signature algorithms
199
- * @param {string} sigAlg signature algorithm
200
- * @return {string/undefined} digest algorithm
201
- */
282
+ * Return the companion digest URI for a given signature algorithm URI.
283
+ *
284
+ * @param sigAlg signature algorithm URI
285
+ * @returns digest algorithm URI or undefined when unsupported
286
+ */
202
287
  function getDigestMethod(sigAlg) {
203
288
  return digestAlgorithms[sigAlg];
204
289
  }
205
290
  /**
206
- * @public
207
- * @desc Create XPath
208
- * @param {string/object} local parameters to create XPath
209
- * @param {boolean} isExtractAll define whether returns whole content according to the XPath
210
- * @return {string} xpath
211
- */
291
+ * Build an XPath expression that matches either a named element or one of
292
+ * its attributes.
293
+ *
294
+ * @param local element name, or `{ name, attr }` for an attribute selector
295
+ * @param isExtractAll when true the element selector resolves to its text()
296
+ * @returns XPath expression
297
+ */
212
298
  function createXPath(local, isExtractAll) {
213
299
  if ((0, utility_1.isString)(local)) {
214
300
  var escaped = (0, utility_1.escapeXPathValue)(local);
215
- return isExtractAll === true ? "//*[local-name(.)=" + escaped + "]/text()" : "//*[local-name(.)=" + escaped + "]";
301
+ return isExtractAll === true
302
+ ? '//*[local-name(.)=' + escaped + ']/text()'
303
+ : '//*[local-name(.)=' + escaped + ']';
216
304
  }
217
- return "//*[local-name(.)=" + (0, utility_1.escapeXPathValue)(local.name) + "]/@" + local.attr;
305
+ var _a = local, name = _a.name, attr = _a.attr;
306
+ return '//*[local-name(.)=' + (0, utility_1.escapeXPathValue)(name) + ']/@' + attr;
218
307
  }
219
308
  /**
220
- * @private
221
- * @desc Tag normalization
222
- * @param {string} prefix prefix of the tag
223
- * @param {content} content normalize it to capitalized camel case
224
- * @return {string}
309
+ * Capitalise a content string after camel-casing and optionally prefix it.
225
310
  */
226
311
  function tagging(prefix, content) {
227
- var camelContent = (0, utility_2.camelCase)(content);
312
+ var camelContent = (0, utility_1.camelCase)(content);
228
313
  return prefix + camelContent.charAt(0).toUpperCase() + camelContent.slice(1);
229
314
  }
315
+ /**
316
+ * Replacer for {@link replaceTagsByValue}. Always XML-escapes the
317
+ * replacement text, in both attribute and element-text contexts, to
318
+ * prevent SAML attribute/element injection through user-controlled
319
+ * template values.
320
+ */
230
321
  function escapeTag(replacement) {
231
322
  return function (_match, quote) {
232
- var text = (replacement === null || replacement === undefined) ? '' : String(replacement);
233
- // not having a quote means this interpolation isn't for an attribute, and so does not need escaping
234
- return quote ? "".concat(quote).concat((0, xml_escape_1.default)(text)) : text;
323
+ var text = replacement === null || replacement === undefined ? '' : String(replacement);
324
+ return quote ? "".concat(quote).concat((0, xml_escape_1.default)(text)) : (0, xml_escape_1.default)(text);
235
325
  };
236
326
  }
237
327
  return {
@@ -244,24 +334,55 @@ var libSaml = function () {
244
334
  defaultLogoutRequestTemplate: defaultLogoutRequestTemplate,
245
335
  defaultLogoutResponseTemplate: defaultLogoutResponseTemplate,
246
336
  /**
247
- * @desc Replace the tag (e.g. {tag}) inside the raw XML
248
- * @param {string} rawXML raw XML string used to do keyword replacement
249
- * @param {array} tagValues tag values
250
- * @return {string}
251
- */
337
+ * Substitute `{Tag}` placeholders inside an XML template with the given
338
+ * replacement map. Replacement text is XML-escaped in both attribute and
339
+ * element-text positions to prevent SAML element/attribute injection.
340
+ *
341
+ * When a tag's value is `null` or `undefined`:
342
+ * - in **attribute position** (`name="{Tag}"`) the entire attribute is
343
+ * omitted from the rendered XML, per `saml-core §3.4.1` (every
344
+ * `<AuthnRequest>` attribute is `use="optional"`); previously samlify
345
+ * emitted `name=""` or the literal `name="undefined"`, which is
346
+ * invalid for typed attributes (e.g. `AssertionConsumerServiceURL`
347
+ * declared as `xs:anyURI`).
348
+ * - in **element-only-body position** (`<X>{Tag}</X>` or
349
+ * `<X attr="...">{Tag}</X>`) the entire element is dropped — per
350
+ * `saml-core §3.7.1` for `<samlp:SessionIndex>` and any other
351
+ * `minOccurs="0"` element where the body is solely a placeholder.
352
+ * - in **mixed-text position** (`<X>prefix{Tag}suffix</X>`) the
353
+ * placeholder is replaced with the empty string (legacy behaviour).
354
+ *
355
+ * @param rawXML template with `{Tag}` placeholders
356
+ * @param tagValues replacement map keyed by tag name
357
+ * @returns XML with placeholders resolved
358
+ */
252
359
  replaceTagsByValue: function (rawXML, tagValues) {
253
360
  Object.keys(tagValues).forEach(function (t) {
254
- rawXML = rawXML.replace(new RegExp("(\"?)\\{".concat(t, "\\}"), 'g'), escapeTag(tagValues[t]));
361
+ var value = tagValues[t];
362
+ if (value === null || value === undefined) {
363
+ // Drop the entire `\s+name="{Tag}"` (or single-quoted) attribute.
364
+ rawXML = rawXML.replace(new RegExp("\\s+[A-Za-z_:][\\w:.-]*=(\"|')\\{".concat(t, "\\}\\1"), 'g'), '');
365
+ // Drop the entire `<X ...>{Tag}</X>` element when the placeholder
366
+ // is the only body content (allows optional elements to be omitted
367
+ // rather than rendered empty).
368
+ rawXML = rawXML.replace(new RegExp("<([A-Za-z_:][\\w:.-]*)((?:\\s+[^>]*)?)>\\{".concat(t, "\\}</\\1>"), 'g'), '');
369
+ // Replace any remaining `{Tag}` occurrences with empty string.
370
+ rawXML = rawXML.replace(new RegExp("\\{".concat(t, "\\}"), 'g'), '');
371
+ return;
372
+ }
373
+ rawXML = rawXML.replace(new RegExp("(\"?)\\{".concat(t, "\\}"), 'g'), escapeTag(value));
255
374
  });
256
375
  return rawXML;
257
376
  },
258
377
  /**
259
- * @desc Helper function to build the AttributeStatement tag
260
- * @param {LoginResponseAttribute} attributes an array of attribute configuration
261
- * @param {AttributeTemplate} attributeTemplate the attribute tag template to be used
262
- * @param {AttributeStatementTemplate} attributeStatementTemplate the attributeStatement tag template to be used
263
- * @return {string}
264
- */
378
+ * Build a serialized `<AttributeStatement>` from attribute descriptors
379
+ * by applying the attribute and statement templates.
380
+ *
381
+ * @param attributes attribute descriptors (name, format, value)
382
+ * @param attributeTemplate per-attribute template
383
+ * @param attributeStatementTemplate wrapping statement template
384
+ * @returns serialized XML fragment
385
+ */
265
386
  attributeStatementBuilder: function (attributes, attributeTemplate, attributeStatementTemplate) {
266
387
  if (attributeTemplate === void 0) { attributeTemplate = defaultAttributeTemplate; }
267
388
  if (attributeStatementTemplate === void 0) { attributeStatementTemplate = defaultAttributeStatementTemplate; }
@@ -281,37 +402,37 @@ var libSaml = function () {
281
402
  return attributeStatementTemplate.context.replace('{Attributes}', attr);
282
403
  },
283
404
  /**
284
- * @desc Construct the XML signature for POST binding
285
- * @param {string} rawSamlMessage request/response xml string
286
- * @param {string} referenceTagXPath reference uri
287
- * @param {string} privateKey declares the private key
288
- * @param {string} passphrase passphrase of the private key [optional]
289
- * @param {string|buffer} signingCert signing certificate
290
- * @param {string} signatureAlgorithm signature algorithm
291
- * @param {string[]} transformationAlgorithms canonicalization and transformation Algorithms
292
- * @return {string} base64 encoded string
293
- */
405
+ * Compute an XML-DSig signature over the supplied SAML message. Can
406
+ * sign the message root (`isMessageSigned`), a referenced subtree
407
+ * (`referenceTagXPath`), or both.
408
+ *
409
+ * @param opts signature inputs and layout options
410
+ * @returns base64 (default) or raw signed XML string
411
+ */
294
412
  constructSAMLSignature: function (opts) {
295
413
  var rawSamlMessage = opts.rawSamlMessage, referenceTagXPath = opts.referenceTagXPath, privateKey = opts.privateKey, privateKeyPass = opts.privateKeyPass, _a = opts.signatureAlgorithm, signatureAlgorithm = _a === void 0 ? signatureAlgorithms.RSA_SHA256 : _a, _b = opts.transformationAlgorithms, transformationAlgorithms = _b === void 0 ? [
296
414
  'http://www.w3.org/2000/09/xmldsig#enveloped-signature',
297
415
  'http://www.w3.org/2001/10/xml-exc-c14n#',
298
416
  ] : _b, signingCert = opts.signingCert, signatureConfig = opts.signatureConfig, _c = opts.isBase64Output, isBase64Output = _c === void 0 ? true : _c, _d = opts.isMessageSigned, isMessageSigned = _d === void 0 ? false : _d;
299
417
  var sig = new xml_crypto_1.SignedXml();
300
- // Add assertion sections as reference
418
+ // xmldsig-core §6.4.2 make PSS variants available alongside the
419
+ // PKCS#1 v1.5 set built into xml-crypto v6.x. No-op for callers
420
+ // staying on RSA-SHA*; required when `signatureAlgorithm` is one of
421
+ // the `xmldsig-more 2007-05` PSS URIs.
422
+ registerPssAlgorithms(sig);
301
423
  var digestAlgorithm = getDigestMethod(signatureAlgorithm);
302
424
  if (referenceTagXPath) {
303
425
  sig.addReference({
304
426
  xpath: referenceTagXPath,
305
427
  transforms: transformationAlgorithms,
306
- digestAlgorithm: digestAlgorithm
428
+ digestAlgorithm: digestAlgorithm,
307
429
  });
308
430
  }
309
431
  if (isMessageSigned) {
310
432
  sig.addReference({
311
- // reference to the root node
312
433
  xpath: '/*',
313
434
  transforms: transformationAlgorithms,
314
- digestAlgorithm: digestAlgorithm
435
+ digestAlgorithm: digestAlgorithm,
315
436
  });
316
437
  }
317
438
  sig.signatureAlgorithm = signatureAlgorithm;
@@ -325,15 +446,20 @@ var libSaml = function () {
325
446
  else {
326
447
  sig.computeSignature(rawSamlMessage);
327
448
  }
328
- return isBase64Output !== false ? utility_1.default.base64Encode(sig.getSignedXml()) : sig.getSignedXml();
449
+ return isBase64Output !== false
450
+ ? utility_1.default.base64Encode(sig.getSignedXml())
451
+ : sig.getSignedXml();
329
452
  },
330
453
  /**
331
- * @desc Verify the XML signature
332
- * @param {string} xml xml
333
- * @param {SignatureVerifierOptions} opts cert declares the X509 certificate
334
- * @return {[boolean, string | null]} - A tuple where:
335
- * - The first element is `true` if the signature is valid, `false` otherwise.
336
- * - The second element is the cryptographically authenticated assertion node as a string, or `null` if not found.
454
+ * Verify an XML-DSig signature on a SAML payload and, on success, return
455
+ * the cryptographically authenticated assertion node.
456
+ *
457
+ * Defends against classic wrapping attacks by rejecting assertions that
458
+ * appear inside a `SubjectConfirmationData` subtree.
459
+ *
460
+ * @param xml SAML message XML
461
+ * @param opts metadata or key file plus signature algorithm
462
+ * @returns tuple `[verified, authenticatedAssertion | null]`
337
463
  */
338
464
  verifySignature: function (xml, opts) {
339
465
  var e_1, _a;
@@ -342,30 +468,28 @@ var libSaml = function () {
342
468
  var doc = dom.parseFromString(xml);
343
469
  var contextDom = (0, api_1.getContext)().dom;
344
470
  var docParser = contextDom;
345
- // In order to avoid the wrapping attack, we have changed to use absolute xpath instead of naively fetching the signature element
346
- // message signature (logout response / saml response)
471
+ // Absolute XPaths defend against signature-wrapping attacks.
347
472
  var messageSignatureXpath = "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Signature']";
348
- // assertion signature (logout response / saml response)
349
473
  var assertionSignatureXpath = "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']/*[local-name(.)='Signature']";
350
- // check if there is a potential malicious wrapping signature
351
474
  var wrappingElementsXPath = "/*[contains(local-name(), 'Response')]/*[local-name(.)='Assertion']/*[local-name(.)='Subject']/*[local-name(.)='SubjectConfirmation']/*[local-name(.)='SubjectConfirmationData']//*[local-name(.)='Assertion' or local-name(.)='Signature']";
352
- // select the signature node
353
475
  var selection = [];
354
476
  var messageSignatureNode = toNodeArray((0, xpath_1.select)(messageSignatureXpath, doc));
355
477
  var assertionSignatureNode = toNodeArray((0, xpath_1.select)(assertionSignatureXpath, doc));
356
478
  var wrappingElementNode = toNodeArray((0, xpath_1.select)(wrappingElementsXPath, doc));
357
479
  selection = selection.concat(messageSignatureNode);
358
480
  selection = selection.concat(assertionSignatureNode);
359
- // try to catch potential wrapping attack
360
481
  if (wrappingElementNode.length !== 0) {
361
482
  throw new Error('ERR_POTENTIAL_WRAPPING_ATTACK');
362
483
  }
363
- // guarantee to have a signature in saml response
364
484
  if (selection.length === 0) {
365
- return [false, null]; // we return false now
485
+ return [false, null];
366
486
  }
367
487
  var _loop_1 = function (signatureNode) {
368
488
  var sig = new xml_crypto_1.SignedXml();
489
+ // Register PSS plugins on the verifier instance so the algorithm
490
+ // declared inside the signed XML can resolve to a SignatureAlgorithm
491
+ // class (xmldsig-core §6.4.2; see `registerPssAlgorithms`).
492
+ registerPssAlgorithms(sig);
369
493
  var verified = false;
370
494
  sig.signatureAlgorithm = opts.signatureAlgorithm;
371
495
  if (!opts.keyFile && !opts.metadata) {
@@ -376,79 +500,59 @@ var libSaml = function () {
376
500
  }
377
501
  if (opts.metadata) {
378
502
  var certificateNode = toNodeArray((0, xpath_1.select)(".//*[local-name(.)='X509Certificate']", signatureNode));
379
- // certificate in metadata
380
503
  var metadataCert = opts.metadata.getX509Certificate(certUse.signing);
381
- // flattens the nested array of Certificates from each KeyDescriptor
382
504
  if (Array.isArray(metadataCert)) {
383
505
  metadataCert = (0, utility_1.flattenDeep)(metadataCert);
384
506
  }
385
507
  else if (typeof metadataCert === 'string') {
386
508
  metadataCert = [metadataCert];
387
509
  }
388
- // normalise the certificate string
389
510
  metadataCert = metadataCert.map(utility_1.default.normalizeCerString);
390
- // no certificate in node response nor metadata
391
511
  if (certificateNode.length === 0 && metadataCert.length === 0) {
392
512
  throw new Error('NO_SELECTED_CERTIFICATE');
393
513
  }
394
- // certificate node in response
395
514
  if (certificateNode.length !== 0) {
396
515
  var certEl = certificateNode[0];
397
516
  var x509CertificateData = (_b = certEl.textContent) !== null && _b !== void 0 ? _b : '';
398
517
  var x509Certificate_1 = utility_1.default.normalizeCerString(x509CertificateData);
399
518
  if (metadataCert.length >= 1 &&
400
519
  !metadataCert.find(function (cert) { return cert.trim() === x509Certificate_1.trim(); })) {
401
- // keep this restriction for rolling certificate usage
402
- // to make sure the response certificate is one of those specified in metadata
403
520
  throw new Error('ERROR_UNMATCH_CERTIFICATE_DECLARATION_IN_METADATA');
404
521
  }
405
522
  sig.publicCert = this_1.getKeyInfo(x509Certificate_1).getKey();
406
523
  }
407
524
  else {
408
- // Select first one from metadata
409
525
  sig.publicCert = this_1.getKeyInfo(metadataCert[0]).getKey();
410
526
  }
411
527
  }
412
528
  sig.loadSignature(signatureNode);
413
529
  verified = sig.checkSignature(doc.toString());
414
- // immediately throw error when any one of the signature is failed to get verified
415
530
  if (!verified) {
416
531
  return "continue";
417
- // throw new Error('ERR_FAILED_TO_VERIFY_SIGNATURE');
418
532
  }
419
- // Require there to be at least one reference that was signed
420
533
  if (!(sig.getSignedReferences().length >= 1)) {
421
534
  throw new Error('NO_SIGNATURE_REFERENCES');
422
535
  }
423
536
  var signedVerifiedXML = sig.getSignedReferences()[0];
424
537
  var rootNode = docParser.parseFromString(signedVerifiedXML, 'text/xml').documentElement;
425
- // process the verified signature:
426
- // case 1, rootSignedDoc is a response:
427
538
  if (rootNode.localName === 'Response') {
428
- // try getting the Xml from the first assertion
429
539
  var assertions = toNodeArray((0, xpath_1.select)("./*[local-name()='Assertion']", rootNode));
430
540
  var encryptedAssertions = toNodeArray((0, xpath_1.select)("./*[local-name()='EncryptedAssertion']", rootNode));
431
- // now we can process the assertion as an assertion
432
541
  if (assertions.length === 1) {
433
542
  return { value: [true, assertions[0].toString()] };
434
543
  }
435
544
  else if (encryptedAssertions.length >= 1) {
436
545
  return { value: [true, rootNode.toString()] };
437
546
  }
438
- else {
439
- return { value: [true, null] };
440
- }
547
+ return { value: [true, null] };
441
548
  }
442
549
  else if (rootNode.localName === 'Assertion') {
443
550
  return { value: [true, rootNode.toString()] };
444
551
  }
445
- else {
446
- return { value: [true, null] };
447
- }
552
+ return { value: [true, null] };
448
553
  };
449
554
  var this_1 = this;
450
555
  try {
451
- // need to refactor later on
452
556
  for (var selection_1 = __values(selection), selection_1_1 = selection_1.next(); !selection_1_1.done; selection_1_1 = selection_1.next()) {
453
557
  var signatureNode = selection_1_1.value;
454
558
  var state_1 = _loop_1(signatureNode);
@@ -463,108 +567,65 @@ var libSaml = function () {
463
567
  }
464
568
  finally { if (e_1) throw e_1.error; }
465
569
  }
466
- ;
467
- return [false, null]; // we didn't verify anything, none of the signatures are valid
468
- /*
469
- // response must be signed, either entire document or assertion
470
- // default we will take the assertion section under root
471
- if (messageSignatureNode.length === 1) {
472
- const node = select("/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']", doc);
473
- if (node.length === 1) {
474
- assertionNode = node[0].toString();
475
- }
476
- }
477
-
478
- if (assertionSignatureNode.length === 1) {
479
- const verifiedAssertionInfo = extract(assertionSignatureNode[0].toString(), [{
480
- key: 'refURI',
481
- localPath: ['Signature', 'SignedInfo', 'Reference'],
482
- attributes: ['URI']
483
- }]);
484
- // get the assertion supposed to be the one should be verified
485
- const desiredAssertionInfo = extract(doc.toString(), [{
486
- key: 'id',
487
- localPath: ['~Response', 'Assertion'],
488
- attributes: ['ID']
489
- }]);
490
- // 5.4.2 References
491
- // SAML assertions and protocol messages MUST supply a value for the ID attribute on the root element of
492
- // the assertion or protocol message being signed. The assertion’s or protocol message's root element may
493
- // or may not be the root element of the actual XML document containing the signed assertion or protocol
494
- // message (e.g., it might be contained within a SOAP envelope).
495
- // Signatures MUST contain a single <ds:Reference> containing a same-document reference to the ID
496
- // attribute value of the root element of the assertion or protocol message being signed. For example, if the
497
- // ID attribute value is "foo", then the URI attribute in the <ds:Reference> element MUST be "#foo".
498
- if (verifiedAssertionInfo.refURI !== `#${desiredAssertionInfo.id}`) {
499
- throw new Error('ERR_POTENTIAL_WRAPPING_ATTACK');
500
- }
501
- const verifiedDoc = extract(doc.toString(), [{
502
- key: 'assertion',
503
- localPath: ['~Response', 'Assertion'],
504
- attributes: [],
505
- context: true
506
- }]);
507
- assertionNode = verifiedDoc.assertion.toString();
508
- }
509
-
510
- return [verified, assertionNode];*/
570
+ return [false, null];
511
571
  },
512
572
  /**
513
- * @desc Helper function to create the key section in metadata (abstraction for signing and encrypt use)
514
- * @param {string} use type of certificate (e.g. signing, encrypt)
515
- * @param {string} certString declares the certificate String
516
- * @return {object} object used in xml module
517
- */
573
+ * Build the metadata `<KeyDescriptor>` fragment for a certificate use.
574
+ *
575
+ * @param use `signing` or `encryption`
576
+ * @param certString PEM certificate body or Buffer
577
+ * @returns element tree consumable by the `xml` module
578
+ */
518
579
  createKeySection: function (use, certString) {
519
- var _a, _b, _c;
520
- return _a = {},
521
- _a['KeyDescriptor'] = [
580
+ return {
581
+ KeyDescriptor: [
522
582
  {
523
583
  _attr: { use: use },
524
584
  },
525
- (_b = {},
526
- _b['ds:KeyInfo'] = [
585
+ {
586
+ 'ds:KeyInfo': [
527
587
  {
528
588
  _attr: {
529
589
  'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
530
590
  },
531
591
  },
532
- (_c = {},
533
- _c['ds:X509Data'] = [{
592
+ {
593
+ 'ds:X509Data': [{
534
594
  'ds:X509Certificate': utility_1.default.normalizeCerString(certString),
535
595
  }],
536
- _c),
596
+ },
537
597
  ],
538
- _b)
598
+ },
539
599
  ],
540
- _a;
600
+ };
541
601
  },
542
602
  /**
543
- * @desc Constructs SAML message
544
- * @param {string} octetString see "Bindings for the OASIS Security Assertion Markup Language (SAML V2.0)" P.17/46
545
- * @param {string} key declares the pem-formatted private key
546
- * @param {string} passphrase passphrase of private key [optional]
547
- * @param {string} signingAlgorithm signing algorithm
548
- * @return {string} message signature
549
- */
603
+ * Produce a detached RSA signature over a SAML redirect-binding octet
604
+ * string. See SAML bindings spec §3.4.4.1.
605
+ *
606
+ * @param octetString canonical query-string to sign
607
+ * @param key PEM private key
608
+ * @param passphrase optional passphrase for the key
609
+ * @param isBase64 when true (default), base64-encode the signature
610
+ * @param signingAlgorithm signature algorithm URI
611
+ * @returns base64 string (default) or raw Buffer signature
612
+ */
550
613
  constructMessageSignature: function (octetString, key, passphrase, isBase64, signingAlgorithm) {
551
- // Default returning base64 encoded signature
552
- // Embed with node-rsa module
553
614
  var decryptedKey = new node_rsa_1.default(utility_1.default.readPrivateKey(key, passphrase), undefined, {
554
615
  signingScheme: getSigningScheme(signingAlgorithm),
555
616
  });
556
617
  var signature = decryptedKey.sign(octetString);
557
- // Use private key to sign data
558
618
  return isBase64 !== false ? signature.toString('base64') : signature;
559
619
  },
560
620
  /**
561
- * @desc Verifies message signature
562
- * @param {Metadata} metadata metadata object of identity provider or service provider
563
- * @param {string} octetString see "Bindings for the OASIS Security Assertion Markup Language (SAML V2.0)" P.17/46
564
- * @param {string} signature context of XML signature
565
- * @param {string} verifyAlgorithm algorithm used to verify
566
- * @return {boolean} verification result
567
- */
621
+ * Verify a detached RSA signature over a redirect-binding octet string.
622
+ *
623
+ * @param metadata peer metadata carrying the signing certificate
624
+ * @param octetString canonical query-string that was signed
625
+ * @param signature signature bytes
626
+ * @param verifyAlgorithm signature algorithm URI (optional)
627
+ * @returns true when the signature verifies
628
+ */
568
629
  verifyMessageSignature: function (metadata, octetString, signature, verifyAlgorithm) {
569
630
  var signCert = metadata.getX509Certificate(certUse.signing);
570
631
  var signingScheme = getSigningScheme(verifyAlgorithm);
@@ -572,10 +633,11 @@ var libSaml = function () {
572
633
  return key.verify(Buffer.from(octetString), Buffer.from(signature));
573
634
  },
574
635
  /**
575
- * @desc Get the public key in string format
576
- * @param {string} x509Certificate certificate
577
- * @return {string} public key
578
- */
636
+ * Build the KeyInfo XML fragment and PEM public key for a certificate.
637
+ *
638
+ * @param x509Certificate certificate body (no PEM wrappers)
639
+ * @param signatureConfig optional prefix/location for the KeyInfo element
640
+ */
579
641
  getKeyInfo: function (x509Certificate, signatureConfig) {
580
642
  if (signatureConfig === void 0) { signatureConfig = {}; }
581
643
  var prefix = signatureConfig.prefix ? "".concat(signatureConfig.prefix, ":") : '';
@@ -589,14 +651,16 @@ var libSaml = function () {
589
651
  };
590
652
  },
591
653
  /**
592
- * @desc Encrypt the assertion section in Response
593
- * @param {Entity} sourceEntity source entity
594
- * @param {Entity} targetEntity target entity
595
- * @param {string} xml response in xml string format
596
- * @return {Promise} a promise to resolve the finalized xml
597
- */
654
+ * Encrypt the `<Assertion>` inside a SAML response using the target
655
+ * entity's encryption certificate. Returns the base64-encoded XML
656
+ * containing the `<EncryptedAssertion>` element in place of the plaintext.
657
+ *
658
+ * @param sourceEntity entity initiating the encryption (its settings drive the algorithms)
659
+ * @param targetEntity entity whose certificate is used
660
+ * @param xml response XML containing a single `<Assertion>`
661
+ * @returns promise resolving to base64-encoded XML
662
+ */
598
663
  encryptAssertion: function (sourceEntity, targetEntity, xml) {
599
- // Implement encryption after signature if it has
600
664
  return new Promise(function (resolve, reject) {
601
665
  if (!xml) {
602
666
  return reject(new Error('ERR_UNDEFINED_ASSERTION'));
@@ -613,16 +677,16 @@ var libSaml = function () {
613
677
  throw new Error('ERR_MULTIPLE_ASSERTION');
614
678
  }
615
679
  var rawAssertionNode = assertions[0];
616
- // Perform encryption depends on the setting, default is false
617
680
  if (sourceEntitySetting.isAssertionEncrypted) {
618
- var publicKeyPem = utility_1.default.getPublicKeyPemFromCertificate(targetEntityMetadata.getX509Certificate(certUse.encrypt));
681
+ var encryptCert = targetEntityMetadata.getX509Certificate(certUse.encrypt);
682
+ var publicKeyPem = utility_1.default.getPublicKeyPemFromCertificate(encryptCert);
619
683
  xmlenc.encrypt(rawAssertionNode.toString(), {
620
- // use xml-encryption module
621
- rsa_pub: Buffer.from(publicKeyPem), // public key from certificate
622
- pem: Buffer.from("-----BEGIN CERTIFICATE-----".concat(targetEntityMetadata.getX509Certificate(certUse.encrypt), "-----END CERTIFICATE-----")),
684
+ rsa_pub: Buffer.from(publicKeyPem),
685
+ pem: Buffer.from("-----BEGIN CERTIFICATE-----".concat(encryptCert, "-----END CERTIFICATE-----")),
623
686
  encryptionAlgorithm: sourceEntitySetting.dataEncryptionAlgorithm,
624
687
  keyEncryptionAlgorithm: sourceEntitySetting.keyEncryptionAlgorithm,
625
688
  }, function (err, res) {
689
+ /* v8 ignore start */
626
690
  if (err) {
627
691
  console.error(err);
628
692
  return reject(new Error('ERR_EXCEPTION_OF_ASSERTION_ENCRYPTION'));
@@ -630,6 +694,7 @@ var libSaml = function () {
630
694
  if (!res) {
631
695
  return reject(new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION'));
632
696
  }
697
+ /* v8 ignore stop */
633
698
  var encAssertionPrefix = sourceEntitySetting.tagPrefix.encryptedAssertion;
634
699
  var encryptAssertionDoc = dom.parseFromString("<".concat(encAssertionPrefix, ":EncryptedAssertion xmlns:").concat(encAssertionPrefix, "=\"").concat(urn_1.namespace.names.assertion, "\">").concat(res, "</").concat(encAssertionPrefix, ":EncryptedAssertion>"));
635
700
  doc.documentElement.replaceChild(encryptAssertionDoc.documentElement, rawAssertionNode);
@@ -637,25 +702,24 @@ var libSaml = function () {
637
702
  });
638
703
  }
639
704
  else {
640
- return resolve(utility_1.default.base64Encode(xml)); // No need to do encryption
705
+ return resolve(utility_1.default.base64Encode(xml));
641
706
  }
642
707
  });
643
708
  },
644
709
  /**
645
- * @desc Decrypt the assertion section in Response
646
- * @param {string} type only accept SAMLResponse to proceed decryption
647
- * @param {Entity} here this entity
648
- * @param {Entity} from from the entity where the message is sent
649
- * @param {string} entireXML response in xml string format
650
- * @return {function} a promise to get back the entire xml with decrypted assertion
651
- */
710
+ * Decrypt the `<EncryptedAssertion>` inside a SAML response using the
711
+ * local entity's private key. Returns both the decrypted document XML
712
+ * and the raw assertion fragment for downstream extraction.
713
+ *
714
+ * @param here local entity performing decryption
715
+ * @param entireXML SAML response XML containing `<EncryptedAssertion>`
716
+ * @returns tuple `[decryptedDocumentXml, rawAssertionXml]`
717
+ */
652
718
  decryptAssertion: function (here, entireXML) {
653
719
  return new Promise(function (resolve, reject) {
654
- // Implement decryption first then check the signature
655
720
  if (!entireXML) {
656
721
  return reject(new Error('ERR_UNDEFINED_ASSERTION'));
657
722
  }
658
- // Perform encryption depends on the setting of where the message is sent, default is false
659
723
  var hereSetting = here.entitySetting;
660
724
  var dom = (0, api_1.getContext)().dom;
661
725
  var doc = dom.parseFromString(entireXML);
@@ -670,6 +734,7 @@ var libSaml = function () {
670
734
  return xmlenc.decrypt(encAssertionNode.toString(), {
671
735
  key: utility_1.default.readPrivateKey(hereSetting.encPrivateKey, hereSetting.encPrivateKeyPass),
672
736
  }, function (err, res) {
737
+ /* v8 ignore start */
673
738
  if (err) {
674
739
  console.error(err);
675
740
  return reject(new Error('ERR_EXCEPTION_OF_ASSERTION_DECRYPTION'));
@@ -677,6 +742,7 @@ var libSaml = function () {
677
742
  if (!res) {
678
743
  return reject(new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION'));
679
744
  }
745
+ /* v8 ignore stop */
680
746
  var rawAssertionDoc = dom.parseFromString(res);
681
747
  doc.documentElement.replaceChild(rawAssertionDoc.documentElement, encAssertionNode);
682
748
  return resolve([doc.toString(), res]);
@@ -684,34 +750,24 @@ var libSaml = function () {
684
750
  });
685
751
  },
686
752
  /**
687
- * @desc Check if the xml string is valid and bounded
753
+ * Validate the SAML XML against the registered schema validator. Throws
754
+ * when no validator has been configured via {@link setSchemaValidator}
755
+ * so consumers can't silently ship without schema checks.
756
+ *
757
+ * @param input SAML XML string
688
758
  */
689
759
  isValidXml: function (input) {
690
760
  return __awaiter(this, void 0, void 0, function () {
691
- var validate, e_2;
761
+ var validate;
692
762
  return __generator(this, function (_a) {
693
763
  switch (_a.label) {
694
764
  case 0:
695
765
  validate = (0, api_1.getContext)().validate;
696
- /**
697
- * user can write a validate function that always returns
698
- * a resolved promise and skip the validator even in
699
- * production, user will take the responsibility if
700
- * they intend to skip the validation
701
- */
702
766
  if (!validate) {
703
- // otherwise, an error will be thrown
704
- return [2 /*return*/, Promise.reject('Your application is potentially vulnerable because no validation function found. Please read the documentation on how to setup the validator. (https://github.com/tngan/samlify#installation)')];
767
+ return [2 /*return*/, Promise.reject(new Error('Your application is potentially vulnerable because no validation function found. Please read the documentation on how to setup the validator. (https://github.com/tngan/samlify#installation)'))];
705
768
  }
706
- _a.label = 1;
707
- case 1:
708
- _a.trys.push([1, 3, , 4]);
709
769
  return [4 /*yield*/, validate(input)];
710
- case 2: return [2 /*return*/, _a.sent()];
711
- case 3:
712
- e_2 = _a.sent();
713
- throw e_2;
714
- case 4: return [2 /*return*/];
770
+ case 1: return [2 /*return*/, _a.sent()];
715
771
  }
716
772
  });
717
773
  });