@cat-factory/server 0.113.1 → 0.113.2
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/dist/modules/github/GitHubWebhookController.d.ts.map +1 -1
- package/dist/modules/github/GitHubWebhookController.js +11 -2
- package/dist/modules/github/GitHubWebhookController.js.map +1 -1
- package/dist/modules/vcs/VcsWebhookController.d.ts.map +1 -1
- package/dist/modules/vcs/VcsWebhookController.js +18 -0
- package/dist/modules/vcs/VcsWebhookController.js.map +1 -1
- package/dist/webhooks/signatureLog.d.ts +26 -0
- package/dist/webhooks/signatureLog.d.ts.map +1 -0
- package/dist/webhooks/signatureLog.js +60 -0
- package/dist/webhooks/signatureLog.js.map +1 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GitHubWebhookController.d.ts","sourceRoot":"","sources":["../../../src/modules/github/GitHubWebhookController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"GitHubWebhookController.d.ts","sourceRoot":"","sources":["../../../src/modules/github/GitHubWebhookController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAG3B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAE/C;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAAC,MAAM,CAAC,CA+EtD"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { StateSigner } from '../../github/state.js';
|
|
3
|
+
import { logWebhookSignatureRejection } from '../../webhooks/signatureLog.js';
|
|
3
4
|
/**
|
|
4
5
|
* Public GitHub-facing endpoints (NOT under /workspaces, since GitHub calls
|
|
5
6
|
* them): the webhook receiver and the App setup callback. Mounted at `/github`.
|
|
@@ -12,13 +13,21 @@ import { StateSigner } from '../../github/state.js';
|
|
|
12
13
|
export function githubWebhookController() {
|
|
13
14
|
const app = new Hono();
|
|
14
15
|
app.post('/webhooks', async (c) => {
|
|
15
|
-
const
|
|
16
|
+
const container = c.get('container');
|
|
17
|
+
const github = container.github;
|
|
16
18
|
if (!github)
|
|
17
19
|
return c.json({ error: { code: 'unavailable', message: 'GitHub not configured' } }, 503);
|
|
18
20
|
// Verify against the raw bytes before parsing.
|
|
19
21
|
const raw = await c.req.arrayBuffer();
|
|
20
|
-
const
|
|
22
|
+
const signature = c.req.header('x-hub-signature-256') ?? null;
|
|
23
|
+
const ok = await github.webhookVerifier.verify(raw, signature);
|
|
21
24
|
if (!ok) {
|
|
25
|
+
// Response stays terse (external caller); log the operator-facing setup remedy.
|
|
26
|
+
logWebhookSignatureRejection({
|
|
27
|
+
provider: 'github',
|
|
28
|
+
secretConfigured: container.config.github.webhookSecret !== '',
|
|
29
|
+
signaturePresent: !!signature,
|
|
30
|
+
});
|
|
22
31
|
return c.json({ error: { code: 'unauthorized', message: 'Invalid signature' } }, 401);
|
|
23
32
|
}
|
|
24
33
|
const eventName = c.req.header('x-github-event') ?? '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GitHubWebhookController.js","sourceRoot":"","sources":["../../../src/modules/github/GitHubWebhookController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"GitHubWebhookController.js","sourceRoot":"","sources":["../../../src/modules/github/GitHubWebhookController.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VcsWebhookController.d.ts","sourceRoot":"","sources":["../../../src/modules/vcs/VcsWebhookController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAI3B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"VcsWebhookController.d.ts","sourceRoot":"","sources":["../../../src/modules/vcs/VcsWebhookController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAI3B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAG/C;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAAC,MAAM,CAAC,CAqEnD"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { getVcsProvider, isVcsProvider } from '@cat-factory/kernel';
|
|
3
|
+
import { logWebhookSignatureRejection } from '../../webhooks/signatureLog.js';
|
|
3
4
|
/**
|
|
4
5
|
* Provider-neutral webhook receiver for non-GitHub VCS systems (GitLab first). GitHub keeps
|
|
5
6
|
* its own `/github/webhooks` route (HMAC + the GitHub-specific projection); this is the
|
|
@@ -36,6 +37,15 @@ export function vcsWebhookController() {
|
|
|
36
37
|
return c.json({ error: { code: 'unavailable', message: 'Webhook verification not configured' } }, 503);
|
|
37
38
|
}
|
|
38
39
|
if (!(await bundle.webhookVerifier.verify(raw, signatureHeader))) {
|
|
40
|
+
// Response stays terse (external caller); log the operator-facing setup remedy. NOTE: a
|
|
41
|
+
// provider bundle only carries a `webhookVerifier` once its secret is configured (an unset
|
|
42
|
+
// secret 503s at the guard above), so `secretConfigured` is effectively always true here —
|
|
43
|
+
// the "no secret configured" sub-case is reached via the GitHub route, not this one.
|
|
44
|
+
logWebhookSignatureRejection({
|
|
45
|
+
provider: providerParam,
|
|
46
|
+
secretConfigured: connectionSecret(c.get('container').config, providerParam) !== '',
|
|
47
|
+
signaturePresent: !!signatureHeader,
|
|
48
|
+
});
|
|
39
49
|
return c.json({ error: { code: 'unauthorized', message: 'Invalid signature' } }, 401);
|
|
40
50
|
}
|
|
41
51
|
let payload;
|
|
@@ -71,4 +81,12 @@ function resolveConnection(config, provider) {
|
|
|
71
81
|
// GitHub uses its dedicated `/github/webhooks` route; the neutral route does not serve it.
|
|
72
82
|
return null;
|
|
73
83
|
}
|
|
84
|
+
/** The deployment's configured webhook secret for a provider ('' when unset) — the signal for
|
|
85
|
+
* the C2 "no secret configured" rejection sub-case. */
|
|
86
|
+
function connectionSecret(config, provider) {
|
|
87
|
+
if (provider === 'gitlab')
|
|
88
|
+
return config.gitlab?.webhookSecret ?? '';
|
|
89
|
+
// GitHub is not served by the neutral route (see resolveConnection).
|
|
90
|
+
return '';
|
|
91
|
+
}
|
|
74
92
|
//# sourceMappingURL=VcsWebhookController.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VcsWebhookController.js","sourceRoot":"","sources":["../../../src/modules/vcs/VcsWebhookController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"VcsWebhookController.js","sourceRoot":"","sources":["../../../src/modules/vcs/VcsWebhookController.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Which webhook route rejected the delivery — GitHub (HMAC) or a neutral VCS provider (token). */
|
|
2
|
+
export type WebhookSignatureProvider = 'github' | 'gitlab';
|
|
3
|
+
export interface WebhookSignatureRejection {
|
|
4
|
+
provider: WebhookSignatureProvider;
|
|
5
|
+
/** Whether the deployment has a webhook secret configured for this route/connection. */
|
|
6
|
+
secretConfigured: boolean;
|
|
7
|
+
/** Whether the inbound delivery carried the provider's signature/token header at all. */
|
|
8
|
+
signaturePresent: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The operator-facing warning for a rejected webhook delivery, tailored to the failure sub-case:
|
|
12
|
+
* - no deployment secret configured — every signed delivery fails closed;
|
|
13
|
+
* - a delivery with no signature header — the provider side has no secret set (or the caller
|
|
14
|
+
* isn't the provider);
|
|
15
|
+
* - a signature that did not match — the two secrets differ.
|
|
16
|
+
*
|
|
17
|
+
* Self-sufficient without the doc link; carries no secret material. Exported for unit tests.
|
|
18
|
+
*/
|
|
19
|
+
export declare function describeWebhookSignatureRejection(rejection: WebhookSignatureRejection): string;
|
|
20
|
+
/**
|
|
21
|
+
* Log the {@link describeWebhookSignatureRejection} warning for a rejected delivery. The HTTP
|
|
22
|
+
* response stays a terse 401 to the external caller; this makes the setup error visible to the
|
|
23
|
+
* operator in the logs. The `provider` doubles as a structured field for log filtering.
|
|
24
|
+
*/
|
|
25
|
+
export declare function logWebhookSignatureRejection(rejection: WebhookSignatureRejection): void;
|
|
26
|
+
//# sourceMappingURL=signatureLog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signatureLog.d.ts","sourceRoot":"","sources":["../../src/webhooks/signatureLog.ts"],"names":[],"mappings":"AAoBA,mGAAmG;AACnG,MAAM,MAAM,wBAAwB,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE1D,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,wBAAwB,CAAA;IAClC,wFAAwF;IACxF,gBAAgB,EAAE,OAAO,CAAA;IACzB,yFAAyF;IACzF,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAgCD;;;;;;;;GAQG;AACH,wBAAgB,iCAAiC,CAAC,SAAS,EAAE,yBAAyB,GAAG,MAAM,CAuB9F;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,yBAAyB,GAAG,IAAI,CAKvF"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { DOCS } from '../config/docs.js';
|
|
2
|
+
import { logger } from '../observability/logger.js';
|
|
3
|
+
const PROVIDER_COPY = {
|
|
4
|
+
github: {
|
|
5
|
+
label: 'GitHub',
|
|
6
|
+
envVar: 'GITHUB_WEBHOOK_SECRET',
|
|
7
|
+
header: 'X-Hub-Signature-256',
|
|
8
|
+
providerSecretField: "the GitHub App's 'Webhook secret' (App settings → Webhook)",
|
|
9
|
+
docsUrl: DOCS.githubIntegration('authentication'),
|
|
10
|
+
},
|
|
11
|
+
gitlab: {
|
|
12
|
+
label: 'GitLab',
|
|
13
|
+
envVar: 'GITLAB_WEBHOOK_SECRET',
|
|
14
|
+
header: 'X-Gitlab-Token',
|
|
15
|
+
providerSecretField: "the webhook's 'Secret token' (GitLab project/group Settings → Webhooks)",
|
|
16
|
+
docsUrl: DOCS.vcsProviders('setup'),
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* The operator-facing warning for a rejected webhook delivery, tailored to the failure sub-case:
|
|
21
|
+
* - no deployment secret configured — every signed delivery fails closed;
|
|
22
|
+
* - a delivery with no signature header — the provider side has no secret set (or the caller
|
|
23
|
+
* isn't the provider);
|
|
24
|
+
* - a signature that did not match — the two secrets differ.
|
|
25
|
+
*
|
|
26
|
+
* Self-sufficient without the doc link; carries no secret material. Exported for unit tests.
|
|
27
|
+
*/
|
|
28
|
+
export function describeWebhookSignatureRejection(rejection) {
|
|
29
|
+
const { label, envVar, header, providerSecretField, docsUrl } = PROVIDER_COPY[rejection.provider];
|
|
30
|
+
const lead = `A ${label} webhook delivery was rejected (401 Invalid signature):`;
|
|
31
|
+
let cause;
|
|
32
|
+
if (!rejection.secretConfigured) {
|
|
33
|
+
cause =
|
|
34
|
+
`this deployment has no webhook secret configured (${envVar} is unset), so every ` +
|
|
35
|
+
`delivery fails verification. Set ${envVar} to the same value as ${providerSecretField}.`;
|
|
36
|
+
}
|
|
37
|
+
else if (!rejection.signaturePresent) {
|
|
38
|
+
cause =
|
|
39
|
+
`no ${header} header was present. Either ${providerSecretField} is not set (add one ` +
|
|
40
|
+
`matching ${envVar}) or the caller is not ${label}.`;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// `signaturePresent` is header PRESENCE, not validity — so this branch also covers a
|
|
44
|
+
// malformed GitHub `X-Hub-Signature-256` (wrong `sha256=` prefix / non-hex digest). A
|
|
45
|
+
// mismatched secret is by far the dominant cause, so the remedy leads with it.
|
|
46
|
+
cause =
|
|
47
|
+
`the signature did not match. ${providerSecretField} and this deployment's ${envVar} ` +
|
|
48
|
+
`differ — set them to the same value.`;
|
|
49
|
+
}
|
|
50
|
+
return `${lead} ${cause} See ${docsUrl}.`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Log the {@link describeWebhookSignatureRejection} warning for a rejected delivery. The HTTP
|
|
54
|
+
* response stays a terse 401 to the external caller; this makes the setup error visible to the
|
|
55
|
+
* operator in the logs. The `provider` doubles as a structured field for log filtering.
|
|
56
|
+
*/
|
|
57
|
+
export function logWebhookSignatureRejection(rejection) {
|
|
58
|
+
logger.warn({ provider: rejection.provider, event: 'webhook_signature_rejected' }, describeWebhookSignatureRejection(rejection));
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=signatureLog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signatureLog.js","sourceRoot":"","sources":["../../src/webhooks/signatureLog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAA;AA2CnD,MAAM,aAAa,GAAmD;IACpE,MAAM,EAAE;QACN,KAAK,EAAE,QAAQ;QACf,MAAM,EAAE,uBAAuB;QAC/B,MAAM,EAAE,qBAAqB;QAC7B,mBAAmB,EAAE,4DAA4D;QACjF,OAAO,EAAE,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC;KAClD;IACD,MAAM,EAAE;QACN,KAAK,EAAE,QAAQ;QACf,MAAM,EAAE,uBAAuB;QAC/B,MAAM,EAAE,gBAAgB;QACxB,mBAAmB,EAAE,yEAAyE;QAC9F,OAAO,EAAE,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC;KACpC;CACF,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iCAAiC,CAAC,SAAoC;IACpF,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;IACjG,MAAM,IAAI,GAAG,KAAK,KAAK,yDAAyD,CAAA;IAEhF,IAAI,KAAa,CAAA;IACjB,IAAI,CAAC,SAAS,CAAC,gBAAgB,EAAE,CAAC;QAChC,KAAK;YACH,qDAAqD,MAAM,uBAAuB;gBAClF,oCAAoC,MAAM,yBAAyB,mBAAmB,GAAG,CAAA;IAC7F,CAAC;SAAM,IAAI,CAAC,SAAS,CAAC,gBAAgB,EAAE,CAAC;QACvC,KAAK;YACH,MAAM,MAAM,+BAA+B,mBAAmB,uBAAuB;gBACrF,YAAY,MAAM,0BAA0B,KAAK,GAAG,CAAA;IACxD,CAAC;SAAM,CAAC;QACN,qFAAqF;QACrF,sFAAsF;QACtF,+EAA+E;QAC/E,KAAK;YACH,gCAAgC,mBAAmB,0BAA0B,MAAM,GAAG;gBACtF,sCAAsC,CAAA;IAC1C,CAAC;IAED,OAAO,GAAG,IAAI,IAAI,KAAK,QAAQ,OAAO,GAAG,CAAA;AAC3C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAAC,SAAoC;IAC/E,MAAM,CAAC,IAAI,CACT,EAAE,QAAQ,EAAE,SAAS,CAAC,QAAQ,EAAE,KAAK,EAAE,4BAA4B,EAAE,EACrE,iCAAiC,CAAC,SAAS,CAAC,CAC7C,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/server",
|
|
3
|
-
"version": "0.113.
|
|
3
|
+
"version": "0.113.2",
|
|
4
4
|
"description": "Runtime-neutral HTTP layer for the Agent Architecture Board: the Hono controllers, middleware (auth/authz/CORS/error), request helpers and the gateway seams shared by every deployment facade (Cloudflare Worker, Node service).",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -32,10 +32,10 @@
|
|
|
32
32
|
"valibot": "^1.4.2",
|
|
33
33
|
"@cat-factory/agents": "0.54.8",
|
|
34
34
|
"@cat-factory/contracts": "0.128.0",
|
|
35
|
-
"@cat-factory/integrations": "0.81.16",
|
|
36
35
|
"@cat-factory/kernel": "0.123.0",
|
|
37
36
|
"@cat-factory/orchestration": "0.107.1",
|
|
38
37
|
"@cat-factory/prompt-fragments": "0.13.15",
|
|
38
|
+
"@cat-factory/integrations": "0.81.16",
|
|
39
39
|
"@cat-factory/spend": "0.12.24"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|