@vizamodo/aws-sts-core 0.1.43 → 0.1.47

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.
@@ -1,9 +1,6 @@
1
1
  import type { AwsCredentialResult } from "../types";
2
2
  /**
3
3
  * Issue short-lived AWS credentials via Roles Anywhere.
4
- * Preserve:
5
- * - TTL per profile
6
- * - isolate-level signing material cache
7
4
  */
8
5
  export declare function issueAwsCredentials(input: {
9
6
  roleArn: string;
package/dist/sts/issue.js CHANGED
@@ -23,7 +23,7 @@ async function getSigningMaterial(input) {
23
23
  function resolveSessionTtlByProfile(profile) {
24
24
  switch (profile) {
25
25
  case "HubConsoleReadOnly":
26
- return 1 * 60 * 60; // 12h
26
+ return 1 * 60 * 60; // 1h
27
27
  case "HubBillingReadonly":
28
28
  case "HubBillingAdmin":
29
29
  return 2 * 60 * 60; // 2h
@@ -36,14 +36,10 @@ function resolveSessionTtlByProfile(profile) {
36
36
  }
37
37
  /**
38
38
  * Issue short-lived AWS credentials via Roles Anywhere.
39
- * Preserve:
40
- * - TTL per profile
41
- * - isolate-level signing material cache
42
39
  */
43
40
  export async function issueAwsCredentials(input) {
44
41
  const { roleArn, profileArn, trustAnchorArn, region, certBase64, privateKeyPkcs8Base64, profile, } = input;
45
42
  // 1. Kiểm tra đầu vào & Fix TTL an toàn
46
- // AWS mặc định chỉ cho phép 3600s, hãy fix cứng 1h để loại trừ lỗi IAM
47
43
  const sessionTtl = Math.min(resolveSessionTtlByProfile(profile), 3600);
48
44
  const { signingKey } = await getSigningMaterial({ certBase64, privateKeyPkcs8Base64 });
49
45
  // 2. Setup constants
@@ -53,12 +49,46 @@ export async function issueAwsCredentials(input) {
53
49
  const amzDate = now.toISOString().replace(/\.\d{3}Z$/, "Z").replace(/[:-]/g, "");
54
50
  const dateStamp = amzDate.slice(0, 8);
55
51
  const service = "rolesanywhere";
56
- // 3. Chuẩn bị Body & Cert (base64-encoded DER, không marker, không xuống dòng)
52
+ // 3. Chuẩn bị Body & Cert
57
53
  const body = JSON.stringify({ trustAnchorArn, profileArn, roleArn, durationSeconds: sessionTtl });
58
54
  const payloadHash = await sha256Hex(body);
59
- // AWS Roles Anywhere requires base64-encoded DER (NO PEM markers, NO newlines)
60
- const normalizedCert = certBase64.replace(/\s+/g, "");
61
- // 4. Tính toán Signature (CẦN Host trong Canonical Request)
55
+ // --- REFACTOR: Làm sạch chứng chỉ chặt chẽ ---
56
+ const normalizedCert = certBase64
57
+ .replace(/-----BEGIN CERTIFICATE-----/g, "")
58
+ .replace(/-----END CERTIFICATE-----/g, "")
59
+ .replace(/\s+/g, "");
60
+ // --- REFACTOR: Parser ASN.1 an toàn để lấy Serial Number (DECIMAL) ---
61
+ let certSerialDec;
62
+ try {
63
+ const certDer = base64ToArrayBuffer(normalizedCert);
64
+ const derBytes = new Uint8Array(certDer);
65
+ let offset = 0;
66
+ // X509v3 structure check
67
+ if (derBytes[offset++] !== 0x30)
68
+ throw new Error("Not a sequence");
69
+ offset += derBytes[offset] & 0x80 ? (derBytes[offset] & 0x7f) + 1 : 1;
70
+ if (derBytes[offset++] !== 0x30)
71
+ throw new Error("Not a sequence");
72
+ offset += derBytes[offset] & 0x80 ? (derBytes[offset] & 0x7f) + 1 : 1;
73
+ // INTEGER (Serial Number)
74
+ if (derBytes[offset++] !== 0x02)
75
+ throw new Error("Not an integer");
76
+ const serialLength = derBytes[offset++];
77
+ // Bounds check
78
+ if (offset + serialLength > derBytes.length)
79
+ throw new Error("Bounds error");
80
+ const serialBytes = derBytes.slice(offset, offset + serialLength);
81
+ // Convert to Decimal string
82
+ certSerialDec = BigInt("0x" + Array.from(serialBytes)
83
+ .map((b) => b.toString(16).padStart(2, "0"))
84
+ .join(""))
85
+ .toString();
86
+ }
87
+ catch (e) {
88
+ console.error("[issueAwsCredentials] Failed to parse cert serial", e);
89
+ throw new InternalError("invalid_cert_der");
90
+ }
91
+ // 4. Tính toán Signature
62
92
  const baseHeaders = {
63
93
  "content-type": "application/json",
64
94
  "host": host,
@@ -82,14 +112,14 @@ export async function issueAwsCredentials(input) {
82
112
  canonicalRequestHash: await sha256Hex(canonicalRequest),
83
113
  });
84
114
  const signatureHex = await signStringToSign(stringToSign, signingKey);
85
- // 5. Build Headers gửi đi (KHÔNG gửi Host header thủ công trong fetch)
115
+ // 5. Build Authorization Header với số Serial (DECIMAL)
86
116
  const finalHeaders = new Headers({
87
117
  "Content-Type": "application/json",
88
118
  "X-Amz-Date": amzDate,
89
119
  "X-Amz-X509": normalizedCert,
90
- "Authorization": `AWS4-X509-ECDSA-SHA256 Credential=${profileArn}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signatureHex}`
120
+ "Authorization": `AWS4-X509-ECDSA-SHA256 Credential=${certSerialDec}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signatureHex}`
91
121
  });
92
- // 6. Execution (fetch)
122
+ // 6. Execution
93
123
  try {
94
124
  const res = await fetch(`https://${host}${path}`, {
95
125
  method: "POST",
@@ -98,51 +128,11 @@ export async function issueAwsCredentials(input) {
98
128
  });
99
129
  if (!res.ok) {
100
130
  const errorBody = await res.text();
101
- // ---- Local curl debug command (FULL MATERIAL - ROTATE AFTER DEBUG) ----
102
- const localCurlCommand = [
103
- `curl -X POST "https://${host}${path}" \\`,
104
- ` -H "Content-Type: application/json" \\`,
105
- ` -H "X-Amz-Date: ${amzDate}" \\`,
106
- ` -H "X-Amz-X509: ${normalizedCert}" \\`,
107
- ` -H "Authorization: AWS4-X509-ECDSA-SHA256 Credential=${profileArn}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signatureHex}" \\`,
108
- ` -d '${body}'`
109
- ].join("\n");
110
- console.error("[aws-rejected][LOCAL-CURL-COMMAND]", localCurlCommand);
111
- console.error("[aws-rejected][RAW-SIGNING-MATERIAL]", {
112
- certBase64,
113
- privateKeyPkcs8Base64
114
- });
115
- console.error("[aws-rejected][FULL-REQUEST-DUMP]", {
131
+ // Debug logs
132
+ console.error("[aws-rejected] Request details", {
116
133
  status: res.status,
117
- statusText: res.statusText,
118
- awsResponse: errorBody,
119
- // ---- Input parameters ----
120
- roleArn,
121
- profileArn,
122
- trustAnchorArn,
123
- region,
124
- profile,
125
- // ---- Derived signing values ----
126
- host,
127
- path,
128
- amzDate,
129
- dateStamp,
130
- credentialScope,
131
- signedHeaders,
132
- signatureHex,
133
- // ---- Body & payload ----
134
- requestBody: body,
135
- payloadHash,
136
- // ---- Canonical ----
137
- canonicalRequest,
138
- stringToSign,
139
- // ---- Headers actually sent ----
140
- sentHeaders: {
141
- "Content-Type": "application/json",
142
- "X-Amz-Date": amzDate,
143
- "X-Amz-X509": normalizedCert,
144
- "Authorization": `AWS4-X509-ECDSA-SHA256 Credential=${profileArn}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signatureHex}`
145
- }
134
+ response: errorBody,
135
+ serial: certSerialDec
146
136
  });
147
137
  throw new InternalError("aws_rejected");
148
138
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vizamodo/aws-sts-core",
3
- "version": "0.1.43",
3
+ "version": "0.1.47",
4
4
  "description": "Pure AWS STS + SigV4 (X509 Roles Anywhere) core logic",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",