@molecule/api-emails-inbound-ses 1.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/LICENSE +115 -0
- package/dist/browser-guard.d.ts +2 -0
- package/dist/browser-guard.d.ts.map +1 -0
- package/dist/browser-guard.js +19 -0
- package/dist/browser-guard.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/provider.d.ts +92 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +378 -0
- package/dist/provider.js.map +1 -0
- package/dist/secrets.d.ts +16 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +51 -0
- package/dist/secrets.js.map +1 -0
- package/dist/types.d.ts +121 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -0
- package/dist/utilities.d.ts +121 -0
- package/dist/utilities.d.ts.map +1 -0
- package/dist/utilities.js +324 -0
- package/dist/utilities.js.map +1 -0
- package/package.json +58 -0
package/dist/provider.js
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS SES inbound-email provider implementation.
|
|
3
|
+
*
|
|
4
|
+
* SES Inbound parses received mail upstream and either delivers the message
|
|
5
|
+
* to S3 or publishes a notification (with the parsed `mail` metadata and
|
|
6
|
+
* optional base64-encoded raw RFC 822 `content`) to an SNS topic. This
|
|
7
|
+
* bond handles the SNS-notification path: an HTTPS endpoint subscribed to
|
|
8
|
+
* the topic receives JSON, validates the SNS signature, and parses the
|
|
9
|
+
* embedded `content` (RFC 822) into a normalized `InboundEmail`.
|
|
10
|
+
*
|
|
11
|
+
* Outbound replies compose onto the bonded `@molecule/api-emails`
|
|
12
|
+
* transport (typically `@molecule/api-emails-ses`); we never reimplement
|
|
13
|
+
* SMTP / SES SendEmail here.
|
|
14
|
+
*
|
|
15
|
+
* @see https://docs.aws.amazon.com/ses/latest/dg/receiving-email-notifications.html
|
|
16
|
+
* @see https://docs.aws.amazon.com/sns/latest/dg/sns-verify-signature-of-message.html
|
|
17
|
+
*
|
|
18
|
+
* @module
|
|
19
|
+
*/
|
|
20
|
+
import { Buffer } from 'node:buffer';
|
|
21
|
+
import { createPublicKey, createVerify } from 'node:crypto';
|
|
22
|
+
// Side-effect import: registers this bond's secret definitions so the
|
|
23
|
+
// runtime registry is populated even when provider.js is imported directly
|
|
24
|
+
// (not through the package barrel).
|
|
25
|
+
import './secrets.js';
|
|
26
|
+
import { sendMail } from '@molecule/api-emails';
|
|
27
|
+
import { base64ToBuffer, buildSnsCanonicalString, isAllowedSigningCertUrl, parseJsonBody, parseRawMimeContent, splitReferences, unwrapMessageId, } from './utilities.js';
|
|
28
|
+
/**
|
|
29
|
+
* Cache of fetched SNS signing certificate PEM bodies, keyed by
|
|
30
|
+
* `SigningCertURL`. AWS rotates these infrequently and serves them from a
|
|
31
|
+
* CloudFront-fronted endpoint, so caching reduces per-request latency
|
|
32
|
+
* without compromising security (the URL itself is allowlisted).
|
|
33
|
+
*/
|
|
34
|
+
const certCache = new Map();
|
|
35
|
+
/**
|
|
36
|
+
* Timeout (ms) for the (uncached) signing-certificate fetch. Bounds the
|
|
37
|
+
* worst case so a hanging/slow cert endpoint fails fast into a `false`
|
|
38
|
+
* verification result instead of stalling the inbound webhook handler for
|
|
39
|
+
* `fetch`'s much longer default timeout — which otherwise makes inbound
|
|
40
|
+
* email processing look "frozen" rather than failing (see
|
|
41
|
+
* integration-audit-findings.md → [email] hostile-default).
|
|
42
|
+
*/
|
|
43
|
+
const CERT_FETCH_TIMEOUT_MS = 5000;
|
|
44
|
+
/**
|
|
45
|
+
* Resets the cached signing certificates. Exposed for tests.
|
|
46
|
+
*/
|
|
47
|
+
export const _resetSigningCertCache = () => {
|
|
48
|
+
certCache.clear();
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Fetches an SNS signing certificate. Validates the URL against the
|
|
52
|
+
* allowlist before performing any network I/O.
|
|
53
|
+
*
|
|
54
|
+
* @param url - The `SigningCertURL` from the SNS payload.
|
|
55
|
+
* @returns The PEM-encoded certificate body, or `undefined` when the URL
|
|
56
|
+
* is not allowlisted, the fetch fails, or it does not complete within
|
|
57
|
+
* {@link CERT_FETCH_TIMEOUT_MS}.
|
|
58
|
+
*/
|
|
59
|
+
const fetchSigningCert = async (url) => {
|
|
60
|
+
if (!isAllowedSigningCertUrl(url))
|
|
61
|
+
return undefined;
|
|
62
|
+
const cached = certCache.get(url);
|
|
63
|
+
if (cached !== undefined)
|
|
64
|
+
return cached;
|
|
65
|
+
try {
|
|
66
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(CERT_FETCH_TIMEOUT_MS) });
|
|
67
|
+
if (!response.ok)
|
|
68
|
+
return undefined;
|
|
69
|
+
const pem = await response.text();
|
|
70
|
+
if (!pem.includes('BEGIN CERTIFICATE'))
|
|
71
|
+
return undefined;
|
|
72
|
+
certCache.set(url, pem);
|
|
73
|
+
return pem;
|
|
74
|
+
}
|
|
75
|
+
catch (_error) {
|
|
76
|
+
// Best-effort fetch — network failures, including our own abort-on-timeout,
|
|
77
|
+
// are surfaced as `undefined`; the caller treats a missing cert as a
|
|
78
|
+
// signature-verification failure (false), never a hang.
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Type guard for the subset of {@link SnsNotificationPayload} fields we
|
|
84
|
+
* rely on. Returns `false` for malformed payloads without throwing.
|
|
85
|
+
*
|
|
86
|
+
* @param value - Candidate parsed JSON body.
|
|
87
|
+
* @returns `true` when `value` looks like an SNS payload.
|
|
88
|
+
*/
|
|
89
|
+
const isSnsPayload = (value) => {
|
|
90
|
+
if (!value || typeof value !== 'object')
|
|
91
|
+
return false;
|
|
92
|
+
const v = value;
|
|
93
|
+
return (typeof v.Type === 'string' &&
|
|
94
|
+
typeof v.MessageId === 'string' &&
|
|
95
|
+
typeof v.Message === 'string' &&
|
|
96
|
+
typeof v.Timestamp === 'string' &&
|
|
97
|
+
typeof v.Signature === 'string' &&
|
|
98
|
+
typeof v.SigningCertURL === 'string' &&
|
|
99
|
+
typeof v.SignatureVersion === 'string');
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Verifies the signature of an SNS notification payload. Implements the
|
|
103
|
+
* AWS SNS signature-verification flow:
|
|
104
|
+
*
|
|
105
|
+
* 1. Parse the JSON body.
|
|
106
|
+
* 2. Reject if `SigningCertURL` is not from an allowlisted host.
|
|
107
|
+
* 3. Fetch the X.509 certificate from `SigningCertURL`.
|
|
108
|
+
* 4. Build the canonical string per AWS docs (field order varies by
|
|
109
|
+
* `Type`).
|
|
110
|
+
* 5. Verify the base64-decoded `Signature` against the canonical string
|
|
111
|
+
* using SHA1 (`SignatureVersion === '1'`) or SHA256
|
|
112
|
+
* (`SignatureVersion === '2'`).
|
|
113
|
+
* 6. When `AWS_SES_INBOUND_TOPIC_ARN` is set, also verify the payload's
|
|
114
|
+
* `TopicArn` matches.
|
|
115
|
+
*
|
|
116
|
+
* Errors NEVER leak signing material; failures simply return `false`.
|
|
117
|
+
*
|
|
118
|
+
* @param _headers - HTTP headers (unused — SNS signs the body).
|
|
119
|
+
* @param body - Raw HTTP request body (JSON).
|
|
120
|
+
* @returns `true` when the signature is valid, `false` otherwise.
|
|
121
|
+
*/
|
|
122
|
+
export const verifySignature = async (_headers, body) => {
|
|
123
|
+
let parsed;
|
|
124
|
+
try {
|
|
125
|
+
parsed = parseJsonBody(body);
|
|
126
|
+
}
|
|
127
|
+
catch (_error) {
|
|
128
|
+
// Malformed body — not a valid SNS notification; return false (no signature).
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
if (!isSnsPayload(parsed))
|
|
132
|
+
return false;
|
|
133
|
+
const expectedTopic = process.env.AWS_SES_INBOUND_TOPIC_ARN;
|
|
134
|
+
if (expectedTopic && parsed.TopicArn !== expectedTopic)
|
|
135
|
+
return false;
|
|
136
|
+
if (!isAllowedSigningCertUrl(parsed.SigningCertURL))
|
|
137
|
+
return false;
|
|
138
|
+
const pem = await fetchSigningCert(parsed.SigningCertURL);
|
|
139
|
+
if (!pem)
|
|
140
|
+
return false;
|
|
141
|
+
const algorithm = parsed.SignatureVersion === '2'
|
|
142
|
+
? 'RSA-SHA256'
|
|
143
|
+
: parsed.SignatureVersion === '1'
|
|
144
|
+
? 'RSA-SHA1'
|
|
145
|
+
: undefined;
|
|
146
|
+
if (!algorithm)
|
|
147
|
+
return false;
|
|
148
|
+
let canonical;
|
|
149
|
+
try {
|
|
150
|
+
canonical = buildSnsCanonicalString(parsed);
|
|
151
|
+
}
|
|
152
|
+
catch (_error) {
|
|
153
|
+
// Malformed payload fields — cannot build the canonical string; treat as invalid.
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
let signatureBuffer;
|
|
157
|
+
try {
|
|
158
|
+
signatureBuffer = Buffer.from(parsed.Signature, 'base64');
|
|
159
|
+
}
|
|
160
|
+
catch (_error) {
|
|
161
|
+
// Invalid base64 signature value — cannot decode; treat as invalid.
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
if (signatureBuffer.length === 0)
|
|
165
|
+
return false;
|
|
166
|
+
try {
|
|
167
|
+
const publicKey = createPublicKey(pem);
|
|
168
|
+
const verifier = createVerify(algorithm);
|
|
169
|
+
verifier.update(canonical, 'utf8');
|
|
170
|
+
verifier.end();
|
|
171
|
+
return verifier.verify(publicKey, signatureBuffer);
|
|
172
|
+
}
|
|
173
|
+
catch (_error) {
|
|
174
|
+
// Crypto errors (bad PEM, unsupported key, verify failure) — treat as invalid signature.
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Maps an SES `commonHeaders` block onto the normalized header dict.
|
|
180
|
+
*
|
|
181
|
+
* @param mail - The SES `mail` object.
|
|
182
|
+
* @returns A `Record` of lowercased header names to values.
|
|
183
|
+
*/
|
|
184
|
+
const headersFromSesMail = (mail) => {
|
|
185
|
+
const out = {};
|
|
186
|
+
if (Array.isArray(mail.headers)) {
|
|
187
|
+
for (const h of mail.headers) {
|
|
188
|
+
if (!h || typeof h.name !== 'string' || typeof h.value !== 'string')
|
|
189
|
+
continue;
|
|
190
|
+
const lower = h.name.toLowerCase();
|
|
191
|
+
const existing = out[lower];
|
|
192
|
+
if (existing === undefined) {
|
|
193
|
+
out[lower] = h.value;
|
|
194
|
+
}
|
|
195
|
+
else if (Array.isArray(existing)) {
|
|
196
|
+
existing.push(h.value);
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
out[lower] = [existing, h.value];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const ch = mail.commonHeaders;
|
|
204
|
+
if (ch) {
|
|
205
|
+
if (typeof ch.subject === 'string' && out.subject === undefined)
|
|
206
|
+
out.subject = ch.subject;
|
|
207
|
+
if (typeof ch.messageId === 'string' && out['message-id'] === undefined)
|
|
208
|
+
out['message-id'] = ch.messageId;
|
|
209
|
+
if (typeof ch.inReplyTo === 'string' && out['in-reply-to'] === undefined)
|
|
210
|
+
out['in-reply-to'] = ch.inReplyTo;
|
|
211
|
+
if (typeof ch.references === 'string' && out.references === undefined)
|
|
212
|
+
out.references = ch.references;
|
|
213
|
+
}
|
|
214
|
+
return out;
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* Parses an SNS notification carrying an SES inbound-email payload into a
|
|
218
|
+
* normalized {@link InboundEmail}.
|
|
219
|
+
*
|
|
220
|
+
* When the SES `Message.content` field is present, it is base64-decoded
|
|
221
|
+
* and parsed as RFC 822 via `mailparser`. When `content` is absent
|
|
222
|
+
* (header-only notifications), we synthesize an `InboundEmail` from the
|
|
223
|
+
* SES `mail` metadata so the caller can still log/dedupe the message.
|
|
224
|
+
*
|
|
225
|
+
* SubscriptionConfirmation messages are returned as a synthetic
|
|
226
|
+
* `InboundEmail` whose `subject` is `'__sns:SubscriptionConfirmation'` and
|
|
227
|
+
* whose `headers['x-sns-subscribe-url']` carries the confirmation URL —
|
|
228
|
+
* applications inspect this so they can subscribe out-of-band.
|
|
229
|
+
*
|
|
230
|
+
* @param _headers - HTTP headers (unused).
|
|
231
|
+
* @param body - Raw HTTP body (SNS JSON).
|
|
232
|
+
* @returns The normalized inbound email.
|
|
233
|
+
*/
|
|
234
|
+
export const parseWebhookPayload = async (_headers, body) => {
|
|
235
|
+
let parsed;
|
|
236
|
+
try {
|
|
237
|
+
parsed = parseJsonBody(body);
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
throw new Error('SES inbound webhook body is not valid JSON.', { cause: error });
|
|
241
|
+
}
|
|
242
|
+
if (!isSnsPayload(parsed)) {
|
|
243
|
+
throw new Error('SES inbound webhook body is not an SNS notification payload.');
|
|
244
|
+
}
|
|
245
|
+
if (parsed.Type === 'SubscriptionConfirmation' || parsed.Type === 'UnsubscribeConfirmation') {
|
|
246
|
+
const headers = {};
|
|
247
|
+
if (typeof parsed.SubscribeURL === 'string')
|
|
248
|
+
headers['x-sns-subscribe-url'] = parsed.SubscribeURL;
|
|
249
|
+
return {
|
|
250
|
+
id: parsed.MessageId,
|
|
251
|
+
from: '',
|
|
252
|
+
to: [],
|
|
253
|
+
subject: `__sns:${parsed.Type}`,
|
|
254
|
+
headers,
|
|
255
|
+
receivedAt: new Date(parsed.Timestamp),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
let sesMessage;
|
|
259
|
+
try {
|
|
260
|
+
sesMessage = JSON.parse(parsed.Message);
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
throw new Error('SES inbound notification `Message` is not valid JSON.', { cause: error });
|
|
264
|
+
}
|
|
265
|
+
if (!sesMessage || typeof sesMessage !== 'object' || !sesMessage.mail) {
|
|
266
|
+
throw new Error('SES inbound notification is missing the `mail` field.');
|
|
267
|
+
}
|
|
268
|
+
const sesReceivedAt = new Date(sesMessage.mail.timestamp);
|
|
269
|
+
const sesMessageId = sesMessage.mail.messageId;
|
|
270
|
+
if (typeof sesMessage.content === 'string' && sesMessage.content.length > 0) {
|
|
271
|
+
const raw = base64ToBuffer(sesMessage.content);
|
|
272
|
+
const email = await parseRawMimeContent(raw, {
|
|
273
|
+
id: sesMessageId,
|
|
274
|
+
receivedAt: sesReceivedAt,
|
|
275
|
+
});
|
|
276
|
+
if (!email.messageId)
|
|
277
|
+
email.messageId = sesMessageId;
|
|
278
|
+
return email;
|
|
279
|
+
}
|
|
280
|
+
// Header-only notification — synthesize from SES metadata.
|
|
281
|
+
const ch = sesMessage.mail.commonHeaders ?? {};
|
|
282
|
+
const headers = headersFromSesMail(sesMessage.mail);
|
|
283
|
+
const messageIdFromCommon = unwrapMessageId(ch.messageId);
|
|
284
|
+
const inReplyTo = unwrapMessageId(ch.inReplyTo);
|
|
285
|
+
const references = splitReferences(ch.references).map((ref) => unwrapMessageId(ref) ?? ref);
|
|
286
|
+
const email = {
|
|
287
|
+
id: sesMessageId,
|
|
288
|
+
from: Array.isArray(ch.from) && ch.from.length > 0 ? ch.from[0] : (sesMessage.mail.source ?? ''),
|
|
289
|
+
to: Array.isArray(ch.to) && ch.to.length > 0
|
|
290
|
+
? ch.to.slice()
|
|
291
|
+
: Array.isArray(sesMessage.mail.destination)
|
|
292
|
+
? sesMessage.mail.destination.slice()
|
|
293
|
+
: [],
|
|
294
|
+
subject: typeof ch.subject === 'string' ? ch.subject : '',
|
|
295
|
+
headers,
|
|
296
|
+
receivedAt: sesReceivedAt,
|
|
297
|
+
};
|
|
298
|
+
if (Array.isArray(ch.cc) && ch.cc.length > 0)
|
|
299
|
+
email.cc = ch.cc.slice();
|
|
300
|
+
if (messageIdFromCommon)
|
|
301
|
+
email.messageId = messageIdFromCommon;
|
|
302
|
+
else
|
|
303
|
+
email.messageId = sesMessageId;
|
|
304
|
+
if (inReplyTo)
|
|
305
|
+
email.inReplyTo = inReplyTo;
|
|
306
|
+
if (references.length > 0)
|
|
307
|
+
email.references = references;
|
|
308
|
+
return email;
|
|
309
|
+
};
|
|
310
|
+
/**
|
|
311
|
+
* Dispatches an outbound reply through the bonded `@molecule/api-emails`
|
|
312
|
+
* transport. The reply's `In-Reply-To` and `References` headers are
|
|
313
|
+
* populated from the original message when present.
|
|
314
|
+
*
|
|
315
|
+
* @param email - The original inbound email being replied to.
|
|
316
|
+
* @param reply - The reply payload.
|
|
317
|
+
* @returns The reply dispatch result.
|
|
318
|
+
*/
|
|
319
|
+
export const replyTo = async (email, reply) => {
|
|
320
|
+
const fromAddress = reply.from ?? (email.to.length > 0 ? email.to[0] : undefined);
|
|
321
|
+
if (!fromAddress) {
|
|
322
|
+
throw new Error('Cannot dispatch inbound reply: no `from` address was supplied and the original email has no recipient to fall back to.');
|
|
323
|
+
}
|
|
324
|
+
const subject = reply.subject ?? `Re: ${email.subject}`;
|
|
325
|
+
const threadingHeaders = {};
|
|
326
|
+
if (email.messageId) {
|
|
327
|
+
threadingHeaders['In-Reply-To'] = `<${email.messageId}>`;
|
|
328
|
+
const refs = email.references ? [...email.references] : [];
|
|
329
|
+
refs.push(email.messageId);
|
|
330
|
+
threadingHeaders.References = refs.map((id) => `<${id}>`).join(' ');
|
|
331
|
+
}
|
|
332
|
+
const headers = {
|
|
333
|
+
...threadingHeaders,
|
|
334
|
+
...(reply.headers ?? {}),
|
|
335
|
+
};
|
|
336
|
+
const message = {
|
|
337
|
+
from: fromAddress,
|
|
338
|
+
to: email.from,
|
|
339
|
+
subject,
|
|
340
|
+
...(reply.textBody !== undefined ? { text: reply.textBody } : {}),
|
|
341
|
+
...(reply.htmlBody !== undefined ? { html: reply.htmlBody } : {}),
|
|
342
|
+
...(reply.attachments && reply.attachments.length > 0
|
|
343
|
+
? {
|
|
344
|
+
attachments: reply.attachments.map((a) => ({
|
|
345
|
+
filename: a.name,
|
|
346
|
+
content: base64ToBuffer(a.contentBase64),
|
|
347
|
+
contentType: a.contentType,
|
|
348
|
+
...(a.contentId !== undefined ? { cid: a.contentId } : {}),
|
|
349
|
+
})),
|
|
350
|
+
}
|
|
351
|
+
: {}),
|
|
352
|
+
...(Object.keys(headers).length > 0 ? { headers } : {}),
|
|
353
|
+
};
|
|
354
|
+
const result = await sendMail(message);
|
|
355
|
+
return {
|
|
356
|
+
id: result.messageId ?? '',
|
|
357
|
+
};
|
|
358
|
+
};
|
|
359
|
+
/**
|
|
360
|
+
* Indicates that this provider supports outbound reply dispatch via
|
|
361
|
+
* {@link replyTo}. The reply path requires the outbound
|
|
362
|
+
* `@molecule/api-emails` bond to be wired with a transport — typically
|
|
363
|
+
* `@molecule/api-emails-ses`.
|
|
364
|
+
*
|
|
365
|
+
* @returns Always `true`.
|
|
366
|
+
*/
|
|
367
|
+
export const supportsReply = () => true;
|
|
368
|
+
/**
|
|
369
|
+
* The AWS SES inbound-email provider implementing the
|
|
370
|
+
* {@link InboundEmailProvider} interface.
|
|
371
|
+
*/
|
|
372
|
+
export const provider = {
|
|
373
|
+
parseWebhookPayload,
|
|
374
|
+
verifySignature,
|
|
375
|
+
replyTo,
|
|
376
|
+
supportsReply,
|
|
377
|
+
};
|
|
378
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS SES inbound-email secret definitions — self-registered at import time so the
|
|
3
|
+
* runtime secrets registry (`@molecule/api-secrets`) can drive boot-time
|
|
4
|
+
* configuration reports and actionable "not configured" errors.
|
|
5
|
+
*
|
|
6
|
+
* Content is derived MECHANICALLY from this package's mlcl registry secrets
|
|
7
|
+
* entry (label/instructions/setupUrl/example) via the fleet formula, so
|
|
8
|
+
* packages sharing a key register byte-identical definitions and
|
|
9
|
+
* registration order never matters.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import type { SecretDefinition } from '@molecule/api-secrets';
|
|
14
|
+
/** Secret definitions required by the AWS SES inbound-email bond. */
|
|
15
|
+
export declare const emailsInboundSesSecretDefinitions: SecretDefinition[];
|
|
16
|
+
//# sourceMappingURL=secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAG7D,qEAAqE;AACrE,eAAO,MAAM,iCAAiC,EAAE,gBAAgB,EAuC/D,CAAA"}
|
package/dist/secrets.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS SES inbound-email secret definitions — self-registered at import time so the
|
|
3
|
+
* runtime secrets registry (`@molecule/api-secrets`) can drive boot-time
|
|
4
|
+
* configuration reports and actionable "not configured" errors.
|
|
5
|
+
*
|
|
6
|
+
* Content is derived MECHANICALLY from this package's mlcl registry secrets
|
|
7
|
+
* entry (label/instructions/setupUrl/example) via the fleet formula, so
|
|
8
|
+
* packages sharing a key register byte-identical definitions and
|
|
9
|
+
* registration order never matters.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import { registerSecrets } from '@molecule/api-secrets';
|
|
14
|
+
/** Secret definitions required by the AWS SES inbound-email bond. */
|
|
15
|
+
export const emailsInboundSesSecretDefinitions = [
|
|
16
|
+
{
|
|
17
|
+
key: 'AWS_ACCESS_KEY_ID',
|
|
18
|
+
description: 'AWS access key ID — Create an IAM user with the needed policy (SES/S3/SQS) and create an access key under Security credentials.',
|
|
19
|
+
helpUrl: 'https://console.aws.amazon.com/iam/',
|
|
20
|
+
required: true,
|
|
21
|
+
example: 'AKIA...',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
key: 'AWS_SECRET_ACCESS_KEY',
|
|
25
|
+
description: 'AWS secret access key — Shown once when creating the IAM access key — store it immediately.',
|
|
26
|
+
helpUrl: 'https://console.aws.amazon.com/iam/',
|
|
27
|
+
required: true,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
key: 'AWS_SES_REGION',
|
|
31
|
+
description: 'AWS SES region — The AWS region where SES is set up (and out of sandbox for production sending).',
|
|
32
|
+
required: true,
|
|
33
|
+
example: 'us-east-1',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
key: 'AWS_SES_INBOUND_TOPIC_ARN',
|
|
37
|
+
description: 'SES inbound SNS topic ARN — ARN of the SNS topic your SES receipt rule publishes inbound mail to.',
|
|
38
|
+
helpUrl: 'https://console.aws.amazon.com/ses/',
|
|
39
|
+
required: false,
|
|
40
|
+
example: 'arn:aws:sns:us-east-1:123456789012:ses-inbound',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
key: 'AWS_SNS_SIGNING_CERT_HOSTNAME_SUFFIXES',
|
|
44
|
+
description: 'SNS signing-cert hostname allowlist — Comma-separated hostname suffixes allowed for SNS signature certificates; the default (.amazonaws.com) is fine.',
|
|
45
|
+
required: false,
|
|
46
|
+
example: '.amazonaws.com',
|
|
47
|
+
default: '.amazonaws.com',
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
registerSecrets(emailsInboundSesSecretDefinitions);
|
|
51
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAEvD,qEAAqE;AACrE,MAAM,CAAC,MAAM,iCAAiC,GAAuB;IACnE;QACE,GAAG,EAAE,mBAAmB;QACxB,WAAW,EACT,iIAAiI;QACnI,OAAO,EAAE,qCAAqC;QAC9C,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,SAAS;KACnB;IACD;QACE,GAAG,EAAE,uBAAuB;QAC5B,WAAW,EACT,6FAA6F;QAC/F,OAAO,EAAE,qCAAqC;QAC9C,QAAQ,EAAE,IAAI;KACf;IACD;QACE,GAAG,EAAE,gBAAgB;QACrB,WAAW,EACT,kGAAkG;QACpG,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,WAAW;KACrB;IACD;QACE,GAAG,EAAE,2BAA2B;QAChC,WAAW,EACT,mGAAmG;QACrG,OAAO,EAAE,qCAAqC;QAC9C,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,gDAAgD;KAC1D;IACD;QACE,GAAG,EAAE,wCAAwC;QAC7C,WAAW,EACT,uJAAuJ;QACzJ,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,gBAAgB;QACzB,OAAO,EAAE,gBAAgB;KAC1B;CACF,CAAA;AAED,eAAe,CAAC,iCAAiC,CAAC,CAAA"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the AWS SES inbound-emails provider.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
export type { InboundEmail, InboundEmailAttachment, InboundEmailProvider, InboundEmailReply, InboundEmailReplyResult, } from '@molecule/api-emails-inbound';
|
|
7
|
+
/**
|
|
8
|
+
* Allowed hostname suffixes for the SNS `SigningCertURL`. AWS SNS only
|
|
9
|
+
* publishes signing certificates from `*.amazonaws.com`; any URL outside
|
|
10
|
+
* this allowlist MUST be rejected to defend against SSRF and certificate
|
|
11
|
+
* substitution attacks.
|
|
12
|
+
*
|
|
13
|
+
* Exposed for unit-testing; not part of the public bond surface.
|
|
14
|
+
*/
|
|
15
|
+
export declare const SNS_SIGNING_CERT_HOSTNAME_SUFFIXES: readonly string[];
|
|
16
|
+
/**
|
|
17
|
+
* Shape of an Amazon SNS notification (or SubscriptionConfirmation /
|
|
18
|
+
* UnsubscribeConfirmation) payload, as POSTed to a subscribed HTTPS
|
|
19
|
+
* endpoint. Only the fields used by this bond are typed here.
|
|
20
|
+
*
|
|
21
|
+
* @see https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html
|
|
22
|
+
*/
|
|
23
|
+
export interface SnsNotificationPayload {
|
|
24
|
+
/**
|
|
25
|
+
* Discriminates the kind of message: `Notification`,
|
|
26
|
+
* `SubscriptionConfirmation`, or `UnsubscribeConfirmation`.
|
|
27
|
+
*/
|
|
28
|
+
Type: string;
|
|
29
|
+
/** A unique UUID for the message. */
|
|
30
|
+
MessageId: string;
|
|
31
|
+
/** The notification topic ARN. */
|
|
32
|
+
TopicArn?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Subject line as supplied by the publisher. Optional for notifications.
|
|
35
|
+
*/
|
|
36
|
+
Subject?: string;
|
|
37
|
+
/** Message payload (string). For SES notifications this is JSON. */
|
|
38
|
+
Message: string;
|
|
39
|
+
/** ISO 8601 timestamp when the message was published. */
|
|
40
|
+
Timestamp: string;
|
|
41
|
+
/** Signature version. AWS SNS supports `1` (SHA1) and `2` (SHA256). */
|
|
42
|
+
SignatureVersion: string;
|
|
43
|
+
/** Base64-encoded signature over the canonical string. */
|
|
44
|
+
Signature: string;
|
|
45
|
+
/** URL of the X.509 PEM cert used to sign the message. */
|
|
46
|
+
SigningCertURL: string;
|
|
47
|
+
/** Confirmation token (only on SubscriptionConfirmation messages). */
|
|
48
|
+
Token?: string;
|
|
49
|
+
/** Subscribe URL (only on SubscriptionConfirmation messages). */
|
|
50
|
+
SubscribeURL?: string;
|
|
51
|
+
/** Unsubscribe URL (only on Notification / UnsubscribeConfirmation). */
|
|
52
|
+
UnsubscribeURL?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Shape of the JSON payload SES publishes to SNS for inbound-email
|
|
56
|
+
* notifications. Only the fields used by this bond are typed; SES emits a
|
|
57
|
+
* superset including `verdicts`, `dkim`, etc.
|
|
58
|
+
*
|
|
59
|
+
* @see https://docs.aws.amazon.com/ses/latest/dg/receiving-email-notifications-contents.html
|
|
60
|
+
*/
|
|
61
|
+
export interface SesInboundNotificationMessage {
|
|
62
|
+
/** Notification kind. We expect `Received` for inbound mail. */
|
|
63
|
+
notificationType: string;
|
|
64
|
+
/** Metadata about the SES `mail` object. */
|
|
65
|
+
mail: {
|
|
66
|
+
/** ISO timestamp SES received the message. */
|
|
67
|
+
timestamp: string;
|
|
68
|
+
/** Sender as decoded by SES. */
|
|
69
|
+
source: string;
|
|
70
|
+
/** SES-assigned message ID. */
|
|
71
|
+
messageId: string;
|
|
72
|
+
/** Envelope-recipient list. */
|
|
73
|
+
destination: string[];
|
|
74
|
+
/** Common headers SES extracts from the message. */
|
|
75
|
+
commonHeaders?: {
|
|
76
|
+
from?: string[];
|
|
77
|
+
to?: string[];
|
|
78
|
+
cc?: string[];
|
|
79
|
+
bcc?: string[];
|
|
80
|
+
subject?: string;
|
|
81
|
+
messageId?: string;
|
|
82
|
+
inReplyTo?: string;
|
|
83
|
+
references?: string;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* All raw headers SES extracted, when `headersTruncated` is `false`.
|
|
87
|
+
*/
|
|
88
|
+
headers?: Array<{
|
|
89
|
+
name: string;
|
|
90
|
+
value: string;
|
|
91
|
+
}>;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Raw RFC 822 message content, base64-encoded, present when the SES
|
|
95
|
+
* receipt rule includes the message content. Absent for header-only
|
|
96
|
+
* notifications.
|
|
97
|
+
*/
|
|
98
|
+
content?: string;
|
|
99
|
+
}
|
|
100
|
+
declare global {
|
|
101
|
+
namespace NodeJS {
|
|
102
|
+
/**
|
|
103
|
+
* Process Env interface — AWS SES Inbound shares credentials with the
|
|
104
|
+
* outbound `@molecule/api-emails-ses` bond. The SNS signing certificate
|
|
105
|
+
* is fetched at verification time; no signing key is held locally.
|
|
106
|
+
*/
|
|
107
|
+
interface ProcessEnv {
|
|
108
|
+
/**
|
|
109
|
+
* Allowlist of hostname suffixes (comma-separated) accepted for the
|
|
110
|
+
* SNS `SigningCertURL`. Defaults to `.amazonaws.com` when unset.
|
|
111
|
+
*/
|
|
112
|
+
AWS_SNS_SIGNING_CERT_HOSTNAME_SUFFIXES?: string;
|
|
113
|
+
/**
|
|
114
|
+
* Optional explicit SNS topic ARN. When set, notifications whose
|
|
115
|
+
* `TopicArn` does not match are rejected.
|
|
116
|
+
*/
|
|
117
|
+
AWS_SES_INBOUND_TOPIC_ARN?: string;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EACV,YAAY,EACZ,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,8BAA8B,CAAA;AAErC;;;;;;;GAOG;AACH,eAAO,MAAM,kCAAkC,EAAE,SAAS,MAAM,EAAuB,CAAA;AAEvF;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAA;IAEZ,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAA;IAEjB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAEhB,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAA;IAEf,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAA;IAEjB,uEAAuE;IACvE,gBAAgB,EAAE,MAAM,CAAA;IAExB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAA;IAEjB,0DAA0D;IAC1D,cAAc,EAAE,MAAM,CAAA;IAEtB,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAA;IAErB,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,6BAA6B;IAC5C,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAA;IAExB,4CAA4C;IAC5C,IAAI,EAAE;QACJ,8CAA8C;QAC9C,SAAS,EAAE,MAAM,CAAA;QACjB,gCAAgC;QAChC,MAAM,EAAE,MAAM,CAAA;QACd,+BAA+B;QAC/B,SAAS,EAAE,MAAM,CAAA;QACjB,+BAA+B;QAC/B,WAAW,EAAE,MAAM,EAAE,CAAA;QACrB,oDAAoD;QACpD,aAAa,CAAC,EAAE;YACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;YACf,EAAE,CAAC,EAAE,MAAM,EAAE,CAAA;YACb,EAAE,CAAC,EAAE,MAAM,EAAE,CAAA;YACb,GAAG,CAAC,EAAE,MAAM,EAAE,CAAA;YACd,OAAO,CAAC,EAAE,MAAM,CAAA;YAChB,SAAS,CAAC,EAAE,MAAM,CAAA;YAClB,SAAS,CAAC,EAAE,MAAM,CAAA;YAClB,UAAU,CAAC,EAAE,MAAM,CAAA;SACpB,CAAA;QACD;;WAEG;QACH,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KACjD,CAAA;IAED;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,OAAO,CAAC,MAAM,CAAC;IAEb,UAAU,MAAM,CAAC;QACf;;;;WAIG;QACH,UAAiB,UAAU;YACzB;;;eAGG;YACH,sCAAsC,CAAC,EAAE,MAAM,CAAA;YAE/C;;;eAGG;YACH,yBAAyB,CAAC,EAAE,MAAM,CAAA;SACnC;KACF;CACF"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the AWS SES inbound-emails provider.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Allowed hostname suffixes for the SNS `SigningCertURL`. AWS SNS only
|
|
8
|
+
* publishes signing certificates from `*.amazonaws.com`; any URL outside
|
|
9
|
+
* this allowlist MUST be rejected to defend against SSRF and certificate
|
|
10
|
+
* substitution attacks.
|
|
11
|
+
*
|
|
12
|
+
* Exposed for unit-testing; not part of the public bond surface.
|
|
13
|
+
*/
|
|
14
|
+
export const SNS_SIGNING_CERT_HOSTNAME_SUFFIXES = ['.amazonaws.com'];
|
|
15
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAsB,CAAC,gBAAgB,CAAC,CAAA"}
|