mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-08 01:43:55 +00:00
fix(git-sources): harden webhook delivery, transport errors, and clone limits (#1249)
* fix(git-sources): harden webhook delivery, transport errors, and clone limits Map webhook-pull outcomes to real HTTP status codes (200 success, 202 debounced, 404 no source, 422 failure) instead of always returning 200, so a Git provider and any monitoring on it can tell when a delivery actually failed. Close a concurrent webhook fan-out gap: the debounce window is now re-checked inside the per-stack lock, so simultaneous deliveries for one push run a single clone instead of one per request. The whole pull/apply critical section runs under a single lock acquisition. Unwrap fetch transport causes (ENOTFOUND, ECONNREFUSED, ECONNRESET, TLS) so a clone failure surfaces an actionable, host-qualified message instead of a bare "fetch failed". Cap how many bytes a single clone may download to protect the host disk; operators can tune it with GITSOURCE_MAX_CLONE_BYTES (default 100 MB). Log webhook pull failures server-side, since the webhook path is unattended. * test(git-sources): assert surfaced host via toContain to satisfy CodeQL * fix(git-sources): bound per-file read, treat debounced webhooks as non-failure, correct clone-cap docs * docs(git-sources): correct clone-cap comment to describe a download bound, not disk
This commit is contained in:
@@ -27,6 +27,31 @@ export function gitSourceStatus(code: GitSourceErrorCode): number {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a webhook-pull outcome to an HTTP status so a Git provider (and any
|
||||
* monitoring on top of it) can tell delivery succeeded, was a no-op, or
|
||||
* failed by status code alone, not just by parsing the JSON body. The
|
||||
* missing-source case is handled by the route as a 404 before this runs.
|
||||
*
|
||||
* success -> 200 applied / pending update ready
|
||||
* skipped -> 202 accepted but debounced (no work done this call)
|
||||
* error -> 422 request understood, the pull/apply/deploy failed
|
||||
*/
|
||||
export function webhookPullStatus(status: 'success' | 'skipped' | 'error'): number {
|
||||
switch (status) {
|
||||
case 'success': return 200;
|
||||
case 'skipped': return 202;
|
||||
case 'error': return 422;
|
||||
default: {
|
||||
// Exhaustiveness guard: if a new status is added to the union without a
|
||||
// case here, this becomes a compile error instead of returning undefined.
|
||||
const _exhaustive: never = status;
|
||||
void _exhaustive;
|
||||
return 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function sendGitSourceError(res: Response, err: unknown): void {
|
||||
if (err instanceof GitSourceError) {
|
||||
res.status(gitSourceStatus(err.code)).json({ error: err.message, code: err.code });
|
||||
|
||||
Reference in New Issue
Block a user