samlesa 2.16.6 → 2.17.1

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.

Potentially problematic release.


This version of samlesa might be problematic. Click here for more details.

Files changed (82) hide show
  1. package/README.md +30 -50
  2. package/build/index.js +2 -1
  3. package/build/src/binding-artifact.js +330 -146
  4. package/build/src/binding-post.js +45 -31
  5. package/build/src/binding-redirect.js +0 -10
  6. package/build/src/binding-simplesign.js +0 -1
  7. package/build/src/entity-idp.js +1 -5
  8. package/build/src/entity-sp.js +21 -96
  9. package/build/src/extractor.js +48 -4
  10. package/build/src/flow.js +24 -166
  11. package/build/src/libsaml.js +468 -264
  12. package/build/src/libsamlSoap.js +115 -0
  13. package/build/src/schema/xml.xsd +88 -88
  14. package/build/src/schemaValidator.js +5 -13
  15. package/build/src/soap.js +123 -3
  16. package/build/src/utility.js +12 -7
  17. package/package.json +77 -81
  18. package/types/api.d.ts +15 -0
  19. package/types/api.d.ts.map +1 -0
  20. package/types/binding-post.d.ts +48 -0
  21. package/types/binding-post.d.ts.map +1 -0
  22. package/types/binding-redirect.d.ts +54 -0
  23. package/types/binding-redirect.d.ts.map +1 -0
  24. package/types/binding-simplesign.d.ts +41 -0
  25. package/types/binding-simplesign.d.ts.map +1 -0
  26. package/types/entity-idp.d.ts +38 -0
  27. package/types/entity-idp.d.ts.map +1 -0
  28. package/types/entity-sp.d.ts +38 -0
  29. package/types/entity-sp.d.ts.map +1 -0
  30. package/types/entity.d.ts +100 -0
  31. package/types/entity.d.ts.map +1 -0
  32. package/types/extractor.d.ts +26 -0
  33. package/types/extractor.d.ts.map +1 -0
  34. package/types/flow.d.ts +7 -0
  35. package/types/flow.d.ts.map +1 -0
  36. package/types/index.d.ts +2 -1
  37. package/types/index.d.ts.map +1 -1
  38. package/types/libsaml.d.ts +208 -0
  39. package/types/libsaml.d.ts.map +1 -0
  40. package/types/metadata-idp.d.ts +25 -0
  41. package/types/metadata-idp.d.ts.map +1 -0
  42. package/types/metadata-sp.d.ts +37 -0
  43. package/types/metadata-sp.d.ts.map +1 -0
  44. package/types/metadata.d.ts +58 -0
  45. package/types/metadata.d.ts.map +1 -0
  46. package/types/src/api.d.ts +3 -3
  47. package/types/src/api.d.ts.map +1 -1
  48. package/types/src/binding-artifact.d.ts +24 -29
  49. package/types/src/binding-artifact.d.ts.map +1 -1
  50. package/types/src/binding-post.d.ts +22 -22
  51. package/types/src/binding-post.d.ts.map +1 -1
  52. package/types/src/binding-redirect.d.ts.map +1 -1
  53. package/types/src/binding-simplesign.d.ts.map +1 -1
  54. package/types/src/entity-idp.d.ts +3 -4
  55. package/types/src/entity-idp.d.ts.map +1 -1
  56. package/types/src/entity-sp.d.ts +13 -24
  57. package/types/src/entity-sp.d.ts.map +1 -1
  58. package/types/src/entity.d.ts.map +1 -1
  59. package/types/src/extractor.d.ts +22 -0
  60. package/types/src/extractor.d.ts.map +1 -1
  61. package/types/src/flow.d.ts +1 -0
  62. package/types/src/flow.d.ts.map +1 -1
  63. package/types/src/libsaml.d.ts +16 -7
  64. package/types/src/libsaml.d.ts.map +1 -1
  65. package/types/src/libsamlSoap.d.ts +7 -0
  66. package/types/src/libsamlSoap.d.ts.map +1 -0
  67. package/types/src/schemaValidator.d.ts +1 -1
  68. package/types/src/schemaValidator.d.ts.map +1 -1
  69. package/types/src/soap.d.ts +33 -0
  70. package/types/src/soap.d.ts.map +1 -1
  71. package/types/src/utility.d.ts.map +1 -1
  72. package/types/src/validator.d.ts.map +1 -1
  73. package/types/types.d.ts +128 -0
  74. package/types/types.d.ts.map +1 -0
  75. package/types/urn.d.ts +195 -0
  76. package/types/urn.d.ts.map +1 -0
  77. package/types/utility.d.ts +133 -0
  78. package/types/utility.d.ts.map +1 -0
  79. package/types/validator.d.ts +4 -0
  80. package/types/validator.d.ts.map +1 -0
  81. package/build/src/schema/XMLSchema.dtd +0 -402
  82. package/build/src/schema/datatypes.dtd +0 -203
@@ -0,0 +1,115 @@
1
+ import { getContext } from "./api.js";
2
+ import { select } from "xpath";
3
+ import { SignedXml } from "xml-crypto";
4
+ import fs from "fs";
5
+ import utility, { flattenDeep } from "./utility.js";
6
+ import libsaml from "./libsaml.js";
7
+ import { wording } from "./urn.js";
8
+ import { DOMParser } from '@xmldom/xmldom';
9
+ const certUse = wording.certUse;
10
+ const docParser = new DOMParser();
11
+ async function verifyAndDecryptSoapMessage(xml, opts) {
12
+ const { dom } = getContext();
13
+ const doc = dom.parseFromString(xml, 'application/xml');
14
+ const docParser = new DOMParser();
15
+ let type = '';
16
+ // 为 SOAP 消息定义 XPath
17
+ const artifactResolveXpath = "/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='ArtifactResolve']";
18
+ const artifactResponseXpath = "/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='ArtifactResponse']";
19
+ // 检测 ArtifactResolve 或 ArtifactResponse 的存在
20
+ // @ts-expect-error
21
+ const artifactResolveNodes = select(artifactResolveXpath, doc);
22
+ // @ts-expect-error
23
+ const artifactResponseNodes = select(artifactResponseXpath, doc);
24
+ // 根据消息类型选择合适的 XPath
25
+ let basePath = "";
26
+ if (artifactResolveNodes?.length > 0) {
27
+ type = 'artifactResolve';
28
+ basePath = "/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='ArtifactResolve']";
29
+ }
30
+ else if (artifactResponseNodes?.length > 0) {
31
+ type = 'artifactResponse';
32
+ basePath = "/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='ArtifactResponse']";
33
+ }
34
+ else {
35
+ throw new Error('ERR_UNSUPPORTED_SOAP_MESSAGE_TYPE');
36
+ }
37
+ // 基于 SOAP 结构重新定义 XPath
38
+ const messageSignatureXpath = `${basePath}/*[local-name(.)='Signature']`;
39
+ // @ts-expect-error
40
+ const messageSignatureNode = select(messageSignatureXpath, doc);
41
+ let selection = [];
42
+ if (messageSignatureNode?.length > 0) {
43
+ selection = selection.concat(messageSignatureNode);
44
+ }
45
+ if (selection.length === 0) {
46
+ throw new Error('ERR_ZERO_SIGNATURE');
47
+ }
48
+ return verifySignature(xml, selection, opts);
49
+ }
50
+ function verifySignature(xml, selection, opts) {
51
+ // 尝试所有签名节点
52
+ for (const signatureNode of selection) {
53
+ const sig = new SignedXml();
54
+ let verified = false;
55
+ sig.signatureAlgorithm = opts.signatureAlgorithm;
56
+ if (!opts.keyFile && !opts.metadata) {
57
+ throw new Error('ERR_UNDEFINED_SIGNATURE_VERIFIER_OPTIONS');
58
+ }
59
+ if (opts.keyFile) {
60
+ sig.publicCert = fs.readFileSync(opts.keyFile);
61
+ }
62
+ if (opts.metadata) {
63
+ const certificateNode = select(".//*[local-name(.)='X509Certificate']", signatureNode);
64
+ // 证书处理逻辑
65
+ let metadataCert = opts.metadata.getX509Certificate(certUse.signing);
66
+ if (Array.isArray(metadataCert)) {
67
+ metadataCert = flattenDeep(metadataCert);
68
+ }
69
+ else if (typeof metadataCert === 'string') {
70
+ metadataCert = [metadataCert];
71
+ }
72
+ metadataCert = metadataCert.map(utility.normalizeCerString);
73
+ // 没有证书的情况
74
+ if (certificateNode.length === 0 && metadataCert.length === 0) {
75
+ throw new Error('NO_SELECTED_CERTIFICATE');
76
+ }
77
+ if (certificateNode.length !== 0) {
78
+ const x509CertificateData = certificateNode[0].firstChild.data;
79
+ const x509Certificate = utility.normalizeCerString(x509CertificateData);
80
+ if (metadataCert.length >= 1 && !metadataCert.includes(x509Certificate)) {
81
+ throw new Error('ERROR_UNMATCH_CERTIFICATE_DECLARATION_IN_METADATA');
82
+ }
83
+ sig.publicCert = libsaml.getKeyInfo(x509Certificate).getKey();
84
+ }
85
+ else {
86
+ sig.publicCert = libsaml.getKeyInfo(metadataCert[0]).getKey();
87
+ }
88
+ }
89
+ sig.loadSignature(signatureNode);
90
+ verified = sig.checkSignature(xml); // 使用原始XML验证
91
+ if (!verified) {
92
+ throw new Error('ERR_FAILED_TO_VERIFY_SIGNATURE');
93
+ }
94
+ if (sig.getSignedReferences().length < 1) {
95
+ throw new Error('NO_SIGNATURE_REFERENCES');
96
+ }
97
+ const signedVerifiedXML = sig.getSignedReferences()[0];
98
+ const rootNode = docParser.parseFromString(signedVerifiedXML, 'application/xml').documentElement;
99
+ // 处理签名的内容
100
+ switch (rootNode?.localName) {
101
+ case 'ArtifactResolve':
102
+ return [true, rootNode.toString(), false, false];
103
+ case 'ArtifactResponse':
104
+ // @ts-expect-error
105
+ const Response = select("/*[local-name()='ArtifactResponse']/*[local-name()='Response']", rootNode);
106
+ return [true, Response?.[0].toString(), false, false]; // 签名验证成功但未找到断言
107
+ default:
108
+ return [true, null, false, true]; // 签名验证成功但未找到可识别的内容
109
+ }
110
+ }
111
+ return [false, null, false, true];
112
+ }
113
+ export default {
114
+ verifyAndDecryptSoapMessage
115
+ };
@@ -1,88 +1,88 @@
1
- <?xml version="1.0"?>
2
- <!-- DOCTYPE xs:schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN" "XMLSchema.dtd" -->
3
- <xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
4
- xmlns:xs="http://www.w3.org/2001/XMLSchema"
5
- xml:lang="en">
6
-
7
- <xs:annotation>
8
- <xs:documentation>
9
- See http://www.w3.org/XML/1998/namespace.html and
10
- http://www.w3.org/TR/REC-xml for information about this namespace.
11
- </xs:documentation>
12
- </xs:annotation>
13
-
14
- <xs:annotation>
15
- <xs:documentation>
16
- This schema defines attributes and an attribute group
17
- suitable for use by schemas wishing to allow xml:base,
18
- xml:lang or xml:space attributes on elements they define.
19
- To enable this, such a schema must import this schema
20
- for the XML namespace, e.g. as follows:
21
- &lt;schema . . .>
22
- . . .
23
- &lt;import namespace="http://www.w3.org/XML/1998/namespace"
24
- schemaLocation="http://www.w3.org/2001/03/xml.xsd"/>
25
-
26
- Subsequently, qualified reference to any of the attributes
27
- or the group defined below will have the desired effect, e.g.
28
-
29
- &lt;type . . .>
30
- . . .
31
- &lt;attributeGroup ref="xml:specialAttrs"/>
32
-
33
- will define a type which will schema-validate an instance
34
- element with any of those attributes
35
- </xs:documentation>
36
- </xs:annotation>
37
-
38
- <xs:annotation>
39
- <xs:documentation>
40
- In keeping with the XML Schema WG's standard versioning
41
- policy, this schema document will persist at
42
- http://www.w3.org/2001/03/xml.xsd.
43
- At the date of issue it can also be found at
44
- http://www.w3.org/2001/xml.xsd.
45
- The schema document at that URI may however change in the future,
46
- in order to remain compatible with the latest version of XML Schema
47
- itself. In other words, if the XML Schema namespace changes, the version
48
- of this document at
49
- http://www.w3.org/2001/xml.xsd will change
50
- accordingly; the version at
51
- http://www.w3.org/2001/03/xml.xsd will not change.
52
- </xs:documentation>
53
- </xs:annotation>
54
-
55
- <xs:attribute name="lang" type="xs:language">
56
- <xs:annotation>
57
- <xs:documentation>
58
- In due course, we should install the relevant ISO 2- and 3-letter
59
- codes as the enumerated possible values . . .
60
- </xs:documentation>
61
- </xs:annotation>
62
- </xs:attribute>
63
-
64
- <xs:attribute name="space" default="preserve">
65
- <xs:simpleType>
66
- <xs:restriction base="xs:NCName">
67
- <xs:enumeration value="default"/>
68
- <xs:enumeration value="preserve"/>
69
- </xs:restriction>
70
- </xs:simpleType>
71
- </xs:attribute>
72
-
73
- <xs:attribute name="base" type="xs:anyURI">
74
- <xs:annotation>
75
- <xs:documentation>
76
- See http://www.w3.org/TR/xmlbase/ for
77
- information about this attribute.
78
- </xs:documentation>
79
- </xs:annotation>
80
- </xs:attribute>
81
-
82
- <xs:attributeGroup name="specialAttrs">
83
- <xs:attribute ref="xml:base"/>
84
- <xs:attribute ref="xml:lang"/>
85
- <xs:attribute ref="xml:space"/>
86
- </xs:attributeGroup>
87
-
88
- </xs:schema>
1
+ <?xml version="1.0"?>
2
+ <!-- DOCTYPE xs:schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN" "XMLSchema.dtd" -->
3
+ <xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
4
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
5
+ xml:lang="en">
6
+
7
+ <xs:annotation>
8
+ <xs:documentation>
9
+ See http://www.w3.org/XML/1998/namespace.html and
10
+ http://www.w3.org/TR/REC-xml for information about this namespace.
11
+ </xs:documentation>
12
+ </xs:annotation>
13
+
14
+ <xs:annotation>
15
+ <xs:documentation>
16
+ This schema defines attributes and an attribute group
17
+ suitable for use by schemas wishing to allow xml:base,
18
+ xml:lang or xml:space attributes on elements they define.
19
+ To enable this, such a schema must import this schema
20
+ for the XML namespace, e.g. as follows:
21
+ &lt;schema . . .>
22
+ . . .
23
+ &lt;import namespace="http://www.w3.org/XML/1998/namespace"
24
+ schemaLocation="http://www.w3.org/2001/03/xml.xsd"/>
25
+
26
+ Subsequently, qualified reference to any of the attributes
27
+ or the group defined below will have the desired effect, e.g.
28
+
29
+ &lt;type . . .>
30
+ . . .
31
+ &lt;attributeGroup ref="xml:specialAttrs"/>
32
+
33
+ will define a type which will schema-validate an instance
34
+ element with any of those attributes
35
+ </xs:documentation>
36
+ </xs:annotation>
37
+
38
+ <xs:annotation>
39
+ <xs:documentation>
40
+ In keeping with the XML Schema WG's standard versioning
41
+ policy, this schema document will persist at
42
+ http://www.w3.org/2001/03/xml.xsd.
43
+ At the date of issue it can also be found at
44
+ http://www.w3.org/2001/xml.xsd.
45
+ The schema document at that URI may however change in the future,
46
+ in order to remain compatible with the latest version of XML Schema
47
+ itself. In other words, if the XML Schema namespace changes, the version
48
+ of this document at
49
+ http://www.w3.org/2001/xml.xsd will change
50
+ accordingly; the version at
51
+ http://www.w3.org/2001/03/xml.xsd will not change.
52
+ </xs:documentation>
53
+ </xs:annotation>
54
+
55
+ <xs:attribute name="lang" type="xs:language">
56
+ <xs:annotation>
57
+ <xs:documentation>
58
+ In due course, we should install the relevant ISO 2- and 3-letter
59
+ codes as the enumerated possible values . . .
60
+ </xs:documentation>
61
+ </xs:annotation>
62
+ </xs:attribute>
63
+
64
+ <xs:attribute name="space" default="preserve">
65
+ <xs:simpleType>
66
+ <xs:restriction base="xs:NCName">
67
+ <xs:enumeration value="default"/>
68
+ <xs:enumeration value="preserve"/>
69
+ </xs:restriction>
70
+ </xs:simpleType>
71
+ </xs:attribute>
72
+
73
+ <xs:attribute name="base" type="xs:anyURI">
74
+ <xs:annotation>
75
+ <xs:documentation>
76
+ See http://www.w3.org/TR/xmlbase/ for
77
+ information about this attribute.
78
+ </xs:documentation>
79
+ </xs:annotation>
80
+ </xs:attribute>
81
+
82
+ <xs:attributeGroup name="specialAttrs">
83
+ <xs:attribute ref="xml:base"/>
84
+ <xs:attribute ref="xml:lang"/>
85
+ <xs:attribute ref="xml:space"/>
86
+ </xs:attributeGroup>
87
+
88
+ </xs:schema>
@@ -4,9 +4,7 @@ import * as path from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
- let obj = [
8
- 'soap-envelope.xsd',
9
- 'xml.xsd',
7
+ let normal = [
10
8
  'saml-schema-protocol-2.0.xsd',
11
9
  'saml-schema-assertion-2.0.xsd',
12
10
  'xmldsig-core-schema.xsd',
@@ -15,7 +13,7 @@ let obj = [
15
13
  'saml-schema-ecp-2.0.xsd',
16
14
  'saml-schema-dce-2.0.xsd'
17
15
  ];
18
- let normal = [
16
+ let soapSchema = [
19
17
  'soap-envelope.xsd',
20
18
  'xml.xsd',
21
19
  // 2. SOAP核心模式(所有SOAP消息的基础)
@@ -32,7 +30,7 @@ let normal = [
32
30
  'saml-schema-ecp-2.0.xsd', // ECP扩展
33
31
  'saml-schema-dce-2.0.xsd' // DCE扩展
34
32
  ];
35
- const schemas = obj;
33
+ let schemas = normal;
36
34
  function detectXXEIndicators(samlString) {
37
35
  const xxePatterns = [
38
36
  /<!DOCTYPE\s[^>]*>/i,
@@ -55,12 +53,12 @@ function detectXXEIndicators(samlString) {
55
53
  });
56
54
  return Object.keys(matches).length > 0 ? matches : null;
57
55
  }
58
- export const validate = async (xml) => {
56
+ export const validate = async (xml, isSoap = false) => {
59
57
  const indicators = detectXXEIndicators(xml);
60
58
  if (indicators) {
61
- console.error('XXE风险特征:', indicators);
62
59
  throw new Error('ERR_EXCEPTION_VALIDATE_XML');
63
60
  }
61
+ schemas = isSoap ? soapSchema : normal;
64
62
  const schemaPath = path.resolve(__dirname, 'schema');
65
63
  const [xmlParse, ...preload] = await Promise.all(schemas.map(async (file) => ({
66
64
  fileName: file,
@@ -79,17 +77,11 @@ export const validate = async (xml) => {
79
77
  preload: [xmlParse, ...preload],
80
78
  });
81
79
  if (validationResult.valid) {
82
- console.log("---------------------验证通过--------------------");
83
- console.log("---------------------验证通过--------------------");
84
80
  return true;
85
81
  }
86
- console.log('-----------------------没验证通过-----------------------');
87
- console.debug(validationResult);
88
82
  throw validationResult.errors;
89
83
  }
90
84
  catch (error) {
91
- console.log('-----------------------没验证通过error-----------------------');
92
- console.error('[ERROR] validateXML', error);
93
85
  throw new Error('ERR_EXCEPTION_VALIDATE_XML');
94
86
  }
95
87
  };
package/build/src/soap.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import axios from 'axios';
2
2
  import https from 'node:https';
3
+ import crypto from "node:crypto";
4
+ import { Builder } from 'xml2js';
5
+ import iconv from 'iconv-lite';
3
6
  // 2. 配置 Axios 实例(处理自签名证书)
4
7
  const axiosInstance = axios.create({
5
8
  httpsAgent: new https.Agent({
@@ -10,16 +13,133 @@ export async function sendArtifactResolve(url, soapRequest) {
10
13
  try {
11
14
  const response = await axiosInstance.post(url, soapRequest, {
12
15
  headers: {
13
- 'Content-Type': 'application/soap+xml; charset=utf-8',
16
+ 'Content-Type': 'text/xml',
14
17
  'SOAPAction': '"ArtifactResolve"'
15
18
  },
16
19
  timeout: 5000 // 5秒超时
17
20
  });
18
- console.log('✅ Resolve请求成功');
19
21
  return response.data;
20
22
  }
21
23
  catch (error) {
22
- console.error('❌ Resolve请求失败');
23
24
  throw error.response.data;
24
25
  }
25
26
  }
27
+ export async function sendArtifactResponse(url, soapRequest) {
28
+ try {
29
+ const response = await axiosInstance.post(url, soapRequest, {
30
+ headers: {
31
+ 'Content-Type': 'text/xml',
32
+ 'SOAPAction': '"ArtifactResponse"'
33
+ },
34
+ timeout: 5000 // 5秒超时
35
+ });
36
+ return response.data;
37
+ }
38
+ catch (error) {
39
+ throw error.response.data;
40
+ }
41
+ }
42
+ /**
43
+ * @desc generate Art id
44
+ *
45
+ * @param entityIDString
46
+ * @param endpointIndex
47
+ */
48
+ export function createArt(entityIDString, endpointIndex = 0) {
49
+ // 安全获取 sourceEntityId
50
+ let sourceEntityId;
51
+ if (typeof entityIDString === "string") {
52
+ sourceEntityId = entityIDString;
53
+ }
54
+ else {
55
+ // 确保只在非字符串类型上访问 entityMeta
56
+ sourceEntityId = entityIDString.entityMeta.getEntityID();
57
+ }
58
+ // 1. 固定类型代码 (0x0004 - 2字节)
59
+ const typeCode = Buffer.from([0x00, 0x04]);
60
+ // 2. 端点索引 (2字节,大端序)
61
+ if (endpointIndex < 0 || endpointIndex > 65535) {
62
+ throw new Error("Endpoint index must be between 0 and 65535");
63
+ }
64
+ const endpointBuf = Buffer.alloc(2);
65
+ endpointBuf.writeUInt16BE(endpointIndex);
66
+ // 3. Source ID - 实体ID的SHA-1哈希 (20字节)
67
+ const sourceId = crypto
68
+ .createHash("sha1")
69
+ .update(sourceEntityId)
70
+ .digest();
71
+ // 4. Message Handler - 20字节随机值
72
+ const messageHandler = crypto.randomBytes(20);
73
+ // 组合所有组件 (2+2+20+20 = 44字节)
74
+ const artifact = Buffer.concat([
75
+ typeCode,
76
+ endpointBuf,
77
+ sourceId,
78
+ messageHandler,
79
+ ]);
80
+ // 返回Base64编码的Artifact
81
+ return {
82
+ artifact: artifact.toString("base64"),
83
+ origin: {
84
+ typeCode: typeCode.readUInt16BE(0), // 改为整数值
85
+ endpointIndex: endpointIndex, // 修复字段名并赋正确的值
86
+ sourceId: sourceId.toString("hex"), // 转为十六进制
87
+ messageHandle: messageHandler.toString("hex"), // 转为十六进制
88
+ },
89
+ };
90
+ }
91
+ /**
92
+ * @desc generate Art id
93
+ * @param artifact
94
+ */
95
+ export function parseArt(artifact) {
96
+ // 解码 Base64
97
+ console.log(Object.prototype.toString.call(artifact));
98
+ if (Object.prototype.toString.call(artifact) !== '[object String]') {
99
+ return;
100
+ }
101
+ const decoded = Buffer.from(artifact, 'base64');
102
+ // 确保长度正确(SAML 工件固定为 44 字节)
103
+ if (decoded.length !== 44) {
104
+ throw new Error(`Invalid artifact length: ${decoded.length}, expected 44 bytes`);
105
+ }
106
+ // 读取前 4 字节(TypeCode + EndpointIndex)
107
+ const typeCode = decoded.readUInt16BE(0);
108
+ const endpointIndex = decoded.readUInt16BE(2);
109
+ // 使用 Buffer.from() 替代 slice()
110
+ const sourceId = Buffer.from(decoded.buffer, // 底层 ArrayBuffer
111
+ decoded.byteOffset + 4, // 起始偏移量
112
+ 20 // 长度
113
+ ).toString('hex');
114
+ const messageHandle = Buffer.from(decoded.buffer, // 底层 ArrayBuffer
115
+ decoded.byteOffset + 24, // 起始偏移量
116
+ 20 // 长度
117
+ ).toString('hex');
118
+ return { typeCode, endpointIndex, sourceId, messageHandle };
119
+ }
120
+ /**
121
+ * 将对象转换为 ISO-8859-1 编码的 XML 字符串
122
+ * @param {Object} data - 要转换的数据对象
123
+ * @returns {Buffer} - ISO-8859-1 编码的 XML 数据 (Buffer)
124
+ */
125
+ export function encodeXmlToIso88591(data) {
126
+ try {
127
+ // 1. 创建 XML 构建器
128
+ const builder = new Builder({
129
+ headless: false, // 包含 XML 声明
130
+ renderOpts: { 'pretty': false }, // 紧凑格式
131
+ xmldec: {
132
+ version: '1.0',
133
+ encoding: 'ISO-8859-1',
134
+ standalone: true
135
+ }
136
+ });
137
+ // 2. 构建 XML 字符串 (UTF-8 格式)
138
+ const utf8Xml = builder.buildObject(data);
139
+ // 3. 转换为 ISO-8859-1 编码的 Buffer
140
+ return iconv.encode(utf8Xml, 'iso-8859-1');
141
+ }
142
+ catch (error) {
143
+ throw new Error(`XML 编码失败: ${error.message}`);
144
+ }
145
+ }
@@ -3,8 +3,8 @@
3
3
  * @author tngan
4
4
  * @desc Library for some common functions (e.g. de/inflation, en/decoding)
5
5
  */
6
- import { X509Certificate, createPrivateKey } from 'node:crypto';
7
- import { inflate, deflateRaw } from 'pako';
6
+ import { createPrivateKey, X509Certificate } from 'node:crypto';
7
+ import { deflateRaw, inflateRaw } from 'pako';
8
8
  const BASE64_STR = 'base64';
9
9
  /**
10
10
  * @desc Mimic lodash.zipObject
@@ -107,11 +107,16 @@ function deflateString(message) {
107
107
  * @return {string} decompressed string
108
108
  */
109
109
  export function inflateString(compressedString) {
110
- const inputBuffer = Buffer.from(compressedString, BASE64_STR);
111
- const input = Array.prototype.map.call(inputBuffer.toString('binary'), char => char.charCodeAt(0));
112
- return Array.from(inflate(input, { raw: true }))
113
- .map((byte) => String.fromCharCode(byte))
114
- .join('');
110
+ const base64Encoded = decodeURIComponent(compressedString);
111
+ // 2. Base64解码为Uint8Array
112
+ const binaryStr = atob(base64Encoded) ?? base64Encoded;
113
+ const data = Uint8Array.from(binaryStr, (c) => c.charCodeAt(0));
114
+ try {
115
+ return inflateRaw(data, { to: 'string' });
116
+ }
117
+ catch (e) {
118
+ return e.message;
119
+ }
115
120
  }
116
121
  /**
117
122
  * @desc Abstract the normalizeCerString and normalizePemString