pipework 0.62.0 → 0.64.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/CHANGELOG.md +15 -0
- package/REFERENCE.md +41 -0
- package/dist/async/jobs/queue.d.ts +6 -0
- package/dist/async/jobs/queue.d.ts.map +1 -1
- package/dist/async/jobs/queue.js +3 -0
- package/dist/async/jobs/queue.js.map +1 -1
- package/dist/async/jobs/scheduler.d.ts +16 -0
- package/dist/async/jobs/scheduler.d.ts.map +1 -1
- package/dist/async/jobs/scheduler.js +70 -23
- package/dist/async/jobs/scheduler.js.map +1 -1
- package/dist/async/jobs/schedules-table.d.ts +7 -0
- package/dist/async/jobs/schedules-table.d.ts.map +1 -0
- package/dist/async/jobs/schedules-table.js +30 -0
- package/dist/async/jobs/schedules-table.js.map +1 -0
- package/dist/core/config/namespace.d.ts +1 -0
- package/dist/core/config/namespace.d.ts.map +1 -1
- package/dist/core/config/schema.d.ts +1 -0
- package/dist/core/config/schema.d.ts.map +1 -1
- package/dist/core/config/schema.js +2 -0
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/surface/types.d.ts +24 -0
- package/dist/core/surface/types.d.ts.map +1 -1
- package/dist/core/surface/worker.d.ts.map +1 -1
- package/dist/core/surface/worker.js +52 -0
- package/dist/core/surface/worker.js.map +1 -1
- package/dist/data/migrate/generate.d.ts.map +1 -1
- package/dist/data/migrate/generate.js +5 -3
- package/dist/data/migrate/generate.js.map +1 -1
- package/dist/data/migrate/internal-definitions.d.ts.map +1 -1
- package/dist/data/migrate/internal-definitions.js +10 -0
- package/dist/data/migrate/internal-definitions.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/infra/email/audit.d.ts +3 -0
- package/dist/infra/email/audit.d.ts.map +1 -0
- package/dist/infra/email/audit.js +25 -0
- package/dist/infra/email/audit.js.map +1 -0
- package/dist/infra/email/index.d.ts +5 -0
- package/dist/infra/email/index.d.ts.map +1 -0
- package/dist/infra/email/index.js +5 -0
- package/dist/infra/email/index.js.map +1 -0
- package/dist/infra/email/mail.d.ts +69 -0
- package/dist/infra/email/mail.d.ts.map +1 -0
- package/dist/infra/email/mail.js +144 -0
- package/dist/infra/email/mail.js.map +1 -0
- package/dist/infra/email/namespace.d.ts +15 -0
- package/dist/infra/email/namespace.d.ts.map +1 -0
- package/dist/infra/email/namespace.js +15 -0
- package/dist/infra/email/namespace.js.map +1 -0
- package/dist/infra/email/providers.d.ts +42 -0
- package/dist/infra/email/providers.d.ts.map +1 -0
- package/dist/infra/email/providers.js +71 -0
- package/dist/infra/email/providers.js.map +1 -0
- package/dist/infra/email/schema.d.ts +74 -0
- package/dist/infra/email/schema.d.ts.map +1 -0
- package/dist/infra/email/schema.js +39 -0
- package/dist/infra/email/schema.js.map +1 -0
- package/dist/infra/email/sigv4.d.ts +24 -0
- package/dist/infra/email/sigv4.d.ts.map +1 -0
- package/dist/infra/email/sigv4.js +55 -0
- package/dist/infra/email/sigv4.js.map +1 -0
- package/dist/infra/email/sns.d.ts +32 -0
- package/dist/infra/email/sns.d.ts.map +1 -0
- package/dist/infra/email/sns.js +173 -0
- package/dist/infra/email/sns.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { createPublicKey, verify as cryptoVerify } from 'node:crypto';
|
|
2
|
+
import { sql } from '../../data/db/sql.js';
|
|
3
|
+
import { pipeSystem } from '../../data/db/db.js';
|
|
4
|
+
import { Builder } from '../../request/di/builder.js';
|
|
5
|
+
import { UnauthorizedError, ValidationError } from '../../core/errors/index.js';
|
|
6
|
+
import { getBaseLogger } from '../../core/logging/index.js';
|
|
7
|
+
import { __flowStep } from '../../trace/runtime/flow-step.js';
|
|
8
|
+
import { auditEmail } from './audit.js';
|
|
9
|
+
import { normalizeAddress } from './mail.js';
|
|
10
|
+
const NOTIFICATION_FIELDS = ['Message', 'MessageId', 'Subject', 'Timestamp', 'TopicArn', 'Type'];
|
|
11
|
+
const CONFIRMATION_FIELDS = ['Message', 'MessageId', 'SubscribeURL', 'Timestamp', 'Token', 'TopicArn', 'Type'];
|
|
12
|
+
export function snsStringToSign(msg) {
|
|
13
|
+
const names = msg['Type'] === 'Notification' ? NOTIFICATION_FIELDS : CONFIRMATION_FIELDS;
|
|
14
|
+
return names.filter(n => msg[n] !== undefined).map(n => `${n}\n${msg[n]}\n`).join('');
|
|
15
|
+
}
|
|
16
|
+
function isSnsHost(url, region) {
|
|
17
|
+
let u;
|
|
18
|
+
try {
|
|
19
|
+
u = new URL(url);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
if (u.protocol !== 'https:' || u.username !== '' || u.password !== '' || u.port !== '')
|
|
25
|
+
return false;
|
|
26
|
+
const host = region !== undefined ? `sns.${region}.amazonaws.com` : null;
|
|
27
|
+
return host !== null ? u.hostname === host : /^sns\.[a-z0-9-]+\.amazonaws\.com$/.test(u.hostname);
|
|
28
|
+
}
|
|
29
|
+
async function defaultFetchCert(url) {
|
|
30
|
+
const res = await fetch(url);
|
|
31
|
+
if (!res.ok)
|
|
32
|
+
throw new Error(`certificate fetch answered HTTP ${res.status}`);
|
|
33
|
+
return res.text();
|
|
34
|
+
}
|
|
35
|
+
async function defaultConfirm(url) {
|
|
36
|
+
const res = await fetch(url);
|
|
37
|
+
if (!res.ok)
|
|
38
|
+
throw new Error(`subscription confirmation answered HTTP ${res.status}`);
|
|
39
|
+
}
|
|
40
|
+
function refuse(reason) {
|
|
41
|
+
getBaseLogger().warn({ event: 'sns message refused', reason }, 'SNS message refused');
|
|
42
|
+
throw new UnauthorizedError('SNS message verification failed.\n');
|
|
43
|
+
}
|
|
44
|
+
function recipientsOf(event) {
|
|
45
|
+
const type = String(event['eventType'] ?? event['notificationType'] ?? '').toLowerCase();
|
|
46
|
+
const mailPart = event['mail'];
|
|
47
|
+
const sesMessageId = typeof mailPart?.messageId === 'string' ? mailPart.messageId : null;
|
|
48
|
+
const list = (v) => Array.isArray(v) ? v.flatMap(r => (typeof r?.emailAddress === 'string' ? [r.emailAddress] : [])) : [];
|
|
49
|
+
if (type === 'bounce') {
|
|
50
|
+
const bounce = event['bounce'];
|
|
51
|
+
const bounceType = typeof bounce?.bounceType === 'string' ? bounce.bounceType : null;
|
|
52
|
+
return { recipients: list(bounce?.bouncedRecipients).map(address => ({ address, bounceType, kind: 'bounce' })), sesMessageId };
|
|
53
|
+
}
|
|
54
|
+
if (type === 'complaint') {
|
|
55
|
+
const complaint = event['complaint'];
|
|
56
|
+
return { recipients: list(complaint?.complainedRecipients).map(address => ({ address, bounceType: null, kind: 'complaint' })), sesMessageId };
|
|
57
|
+
}
|
|
58
|
+
return { recipients: [], sesMessageId };
|
|
59
|
+
}
|
|
60
|
+
const processEvent = __flowStep({ ref: 'pipework/email:notification' }, async (event) => {
|
|
61
|
+
const { recipients, sesMessageId } = recipientsOf(event);
|
|
62
|
+
const db = pipeSystem();
|
|
63
|
+
for (const r of recipients) {
|
|
64
|
+
const address = normalizeAddress(r.address);
|
|
65
|
+
const found = sesMessageId === null ? [] : await db.execute(sql `
|
|
66
|
+
SELECT id, trace_id FROM pipework_email_messages WHERE provider_message_id = ${sesMessageId}`);
|
|
67
|
+
const messageId = found[0]?.['id'] ?? null;
|
|
68
|
+
const messageTraceId = found[0]?.['trace_id'] ?? null;
|
|
69
|
+
const permanent = r.kind === 'complaint' || r.bounceType === 'Permanent';
|
|
70
|
+
if (permanent) {
|
|
71
|
+
await db.execute(sql `
|
|
72
|
+
INSERT INTO pipework_email_suppressions (address, reason, detail)
|
|
73
|
+
VALUES (${address}, ${r.kind}, ${JSON.stringify({ bounceType: r.bounceType, sesMessageId })}::jsonb)
|
|
74
|
+
ON CONFLICT (address) DO UPDATE SET reason = EXCLUDED.reason, detail = EXCLUDED.detail`);
|
|
75
|
+
getBaseLogger().warn({ event: 'email suppressed', address, reason: r.kind }, 'email suppressed');
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
getBaseLogger().info({ event: 'email bounce not suppressed', address, bounceType: r.bounceType }, 'transient bounce: address kept');
|
|
79
|
+
}
|
|
80
|
+
await auditEmail(r.kind, messageId, { address, bounceType: r.bounceType, suppressed: permanent, messageTraceId });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
/**
|
|
84
|
+
* Receives SES bounce and complaint events delivered through SNS (HTTP/S subscription).
|
|
85
|
+
* A permanent bounce or any complaint puts the address (lowercased) on
|
|
86
|
+
* `pipework_email_suppressions`, which the send job checks; a transient or undetermined
|
|
87
|
+
* bounce is only logged. Each one is a trace step with an `Email:bounce` / `Email:complaint`
|
|
88
|
+
* audit record whose `after` carries `messageTraceId`, the trace of the original send.
|
|
89
|
+
* A suppression is logged at warn as `email suppressed`; the alert condition (#458) is not built yet.
|
|
90
|
+
*
|
|
91
|
+
* Only messages from the configured `topicArns` are accepted (401 otherwise, before any subscription is
|
|
92
|
+
* confirmed or event processed): SNS signs every topic, including an attacker's own.
|
|
93
|
+
* Every message's signature (SignatureVersion 1 or 2) is checked against the certificate
|
|
94
|
+
* at `SigningCertURL`, which must be `https://sns.<region>.amazonaws.com/...`; a failure is a 401.
|
|
95
|
+
* A SubscriptionConfirmation is confirmed by fetching its `SubscribeURL`.
|
|
96
|
+
* SES events delivered through EventBridge are not supported.
|
|
97
|
+
*/
|
|
98
|
+
export function snsWebhook(config) {
|
|
99
|
+
if (config.topicArns.length === 0)
|
|
100
|
+
throw new Error('[pipework] mail.snsWebhook needs at least one topic ARN in topicArns.');
|
|
101
|
+
const fetchCert = config.fetchCert ?? defaultFetchCert;
|
|
102
|
+
const confirm = config.confirm ?? defaultConfirm;
|
|
103
|
+
const certs = new Map();
|
|
104
|
+
async function verifySignature(msg) {
|
|
105
|
+
const url = msg['SigningCertURL'];
|
|
106
|
+
const signature = msg['Signature'];
|
|
107
|
+
const version = msg['SignatureVersion'];
|
|
108
|
+
if (url === undefined || signature === undefined)
|
|
109
|
+
refuse('missing signature fields');
|
|
110
|
+
if (version !== '1' && version !== '2')
|
|
111
|
+
refuse('unknown SignatureVersion');
|
|
112
|
+
if (!isSnsHost(url, config.region) || !new URL(url).pathname.endsWith('.pem'))
|
|
113
|
+
refuse('SigningCertURL is not an SNS certificate URL');
|
|
114
|
+
let pem = certs.get(url);
|
|
115
|
+
if (pem === undefined) {
|
|
116
|
+
try {
|
|
117
|
+
pem = await fetchCert(url);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
refuse('certificate could not be fetched');
|
|
121
|
+
}
|
|
122
|
+
certs.set(url, pem);
|
|
123
|
+
}
|
|
124
|
+
let ok = false;
|
|
125
|
+
try {
|
|
126
|
+
ok = cryptoVerify(version === '1' ? 'RSA-SHA1' : 'RSA-SHA256', Buffer.from(snsStringToSign(msg)), createPublicKey(pem), Buffer.from(signature, 'base64'));
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
ok = false;
|
|
130
|
+
}
|
|
131
|
+
if (!ok)
|
|
132
|
+
refuse('signature does not match');
|
|
133
|
+
if (msg['TopicArn'] === undefined || !config.topicArns.includes(msg['TopicArn']))
|
|
134
|
+
refuse('TopicArn is not a configured topic');
|
|
135
|
+
}
|
|
136
|
+
return new Builder()
|
|
137
|
+
.public()
|
|
138
|
+
.rawBody()
|
|
139
|
+
.request()
|
|
140
|
+
.route('POST', config.path)
|
|
141
|
+
.fit(async ({ rawBody }) => {
|
|
142
|
+
let parsed;
|
|
143
|
+
try {
|
|
144
|
+
parsed = JSON.parse(rawBody.toString('utf-8'));
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
throw new ValidationError({ body: ['is not JSON'] }, 'SNS message is not JSON.');
|
|
148
|
+
}
|
|
149
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
150
|
+
throw new ValidationError({ body: ['is not an object'] }, 'SNS message is not an object.');
|
|
151
|
+
const msg = parsed;
|
|
152
|
+
await verifySignature(msg);
|
|
153
|
+
if (msg['Type'] === 'SubscriptionConfirmation') {
|
|
154
|
+
const subscribeUrl = msg['SubscribeURL'];
|
|
155
|
+
if (subscribeUrl === undefined || !isSnsHost(subscribeUrl, config.region))
|
|
156
|
+
refuse('SubscribeURL is not an SNS URL');
|
|
157
|
+
await confirm(subscribeUrl);
|
|
158
|
+
return { status: 200, body: { ok: true, confirmed: true } };
|
|
159
|
+
}
|
|
160
|
+
if (msg['Type'] === 'Notification') {
|
|
161
|
+
let event;
|
|
162
|
+
try {
|
|
163
|
+
event = JSON.parse(msg['Message'] ?? '');
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return { status: 200, body: { ok: true, ignored: true } };
|
|
167
|
+
}
|
|
168
|
+
await processEvent(event);
|
|
169
|
+
}
|
|
170
|
+
return { status: 200, body: { ok: true } };
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=sns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sns.js","sourceRoot":"","sources":["../../../src/infra/email/sns.ts"],"names":[],"mappings":"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"}
|