Commit beef46a
* feat(jira): include Jira attachments and recent comments in task context (#577)
Jira-origin tasks previously saw only the issue summary + description (ADF→
markdown) plus embedded HTTPS image URLs. Jira-hosted `media` file attachments
and issue comments were invisible to the agent, so a ticket that says "see the
attached log" or carries acceptance clarifications in comments would run
under-informed. Bring Jira to parity with Linear's on-demand context reads by
fetching the context authenticated at task-admission time in the webhook
processor (the Atlassian Remote MCP can't run headlessly — see #580).
- agent-admission download path (cdk/src/handlers/shared/jira-attachments.ts):
* downloadScreenAndStoreJiraAttachments — resolve Jira `media` attachments via
the gateway attachment-content endpoint (api.atlassian.com/ex/jira/{cloudId},
the only host the 3LO token is valid against), refresh-and-retry-once on
401/403, enforce the size cap while streaming, validate magic bytes, screen
through the existing Bedrock Guardrail, upload cleaned bytes to S3, and
return `passed` AttachmentRecords. Fail-closed: a selected attachment that
can't be safely fetched/screened throws JiraAttachmentError and the task is
rejected with a Jira comment. Unsupported/oversized/over-cap attachments are
silently skipped. Filenames are sanitized and ids validated to a safe token
so neither can traverse the S3 key / agent on-disk path.
* fetchRecentHumanComments — recent human (accountType `atlassian`) comments,
ADF→markdown, oldest-first. Fail-open: any failure proceeds without them.
- create-task-core: TaskCreationContext gains optional `taskId` (so processor-
uploaded S3 keys match the eventual record) and `preScreenedAttachments`
(merged verbatim, never re-screened; a non-`passed` record fails closed).
- jira-webhook-processor: wire both in; comments fold under a "## Recent
comments" heading, bounded so they never push task_description past the 10k
limit (advisory context must not become a hard gate).
- jira-adf.ts: extract the ADF→markdown walker so the processor and the comment
helper share it without a circular import (behavior-preserving).
- CDK: pass the attachments bucket to JiraIntegration; grant the processor
ReadWrite + ATTACHMENTS_BUCKET_NAME env; bump the async processor timeout to
60s for serial download+screen. No new OAuth scopes (read:jira-work covers it).
- docs: JIRA_SETUP_GUIDE "Issue context: attachments and comments" (supported
types, limits, skip vs fail-closed reject, comment behavior) + Starlight mirror.
Tests: new jira-attachments.test.ts (download/filter/screen/upload, sanitize,
401 retry, size cap, comment human/app filter + fail-open); processor tests for
comment folding + truncation and fail-closed attachment rejection; create-task-
core tests for preScreenedAttachments merge + taskId. `mise run build` green.
* fix(jira): exempt /v1/jira/webhook from WAF SizeRestrictions_BODY (#577)
Jira issue webhook payloads that carry attachment metadata exceed the AWS
managed `SizeRestrictions_BODY` 8 KB limit, so WAF returned 403 at the edge and
the webhook receiver Lambda never ran — the delivery vanished before ABCA saw
it. This is exactly the case #577 depends on (an issue with file attachments),
so without this exemption the feature can't work in any WAF-protected deploy.
Mirror the existing `/v1/linear/webhook` and `/v1/github/webhook` handling —
both rules must be updated together:
- AWSManagedRulesCommonRuleSet-TaskPaths (excludes SizeRestrictions_BODY): add
`/v1/jira/webhook` to the orStatement scope-down so large Jira bodies pass.
- AWSManagedRulesCommonRuleSet (full CRS for other paths): add a NOT
`/v1/jira/webhook` clause so the path isn't re-covered by the strict rule.
The receiver still HMAC-verifies the raw body and the priority-4 rate-limit
rule still applies, so relaxing the body-size check on this path is safe.
Test: task-api.test.ts asserts the exclusion scope-down contains the Jira path
and the full-CRS scope-down excludes it. 42 pass.
* fix(jira): use Accept: */* when downloading attachment content (#577)
The Jira attachment-content endpoint
(`/rest/api/3/attachment/content/{id}`) serves the file's own media type and
responds 406 Not Acceptable to a narrow `Accept: application/octet-stream`.
That made every attachment download fail with HTTP 406, and the fail-closed
path then rejected the whole task ("could not be downloaded (HTTP 406)").
Send `Accept: */*` so the gateway serves the real content type.
Verified against the live endpoint: octet-stream → 406, */* → 200.
Test asserts the download request sets Accept: */*.
* fix(jira): address #619 review — orphan cleanup, real-byte cap, comment screening, idempotency (#577)
Addresses isadeks's review on PR #619:
1. Zero-byte attachment → JiraAttachmentError (fail-closed). A 0-byte body
passed magic-byte + screening then tripped createAttachmentRecord's size
guard with a plain Error outside the fail-closed conversion (no ❌ comment).
Now rejected explicitly with a Jira comment.
2. 50 MB total-attachment cap enforced on REAL downloaded bytes, not the
attacker-declared `size` metadata (which can under-report). Cumulative real
bytes tracked in the download loop; throws once over the ceiling.
3. S3 orphan cleanup. downloadScreenAndStoreJiraAttachments now tracks uploaded
keys and best-effort-deletes them if the batch throws mid-way; the processor
deletes the returned pre-screened objects when createTaskCore returns non-201
(and on a 200 idempotent replay, whose objects the replayed task doesn't
reference). New exported cleanupPreScreenedAttachments helper.
4. Comment content screened SEPARATELY and dropped fail-open. Third-party
comment text that trips the guardrail no longer fails the reporter's task —
screenCommentsOrDrop screens the folded comment block and drops it (task
proceeds) on GUARDRAIL_INTERVENED / screening outage / no guardrail.
5. Deterministic idempotency key (`jira-<issueKey>-<webhookTimestamp>`) so an
async webhook re-delivery dedupes instead of minting a duplicate task and
re-downloading every attachment. A 200 idempotent replay is handled as
success (no ❌ comment), cleaning up the round's re-uploaded objects.
6. Webhook-processor timeout 60s → 300s. 10 serial 10s attachment fetches can
sum past 60s; a mid-loop kill would orphan objects and force a retry.
Tests: +5 (zero-byte reject, real-byte total cap, mid-batch orphan cleanup,
comment-blocked fail-open drop, deterministic idempotency key + 200-replay).
mise run build green (cdk 2362).
---------
Co-authored-by: Sphia Sadek <isadeks@gmail.com>
1 parent 9549473 commit beef46a
13 files changed
Lines changed: 2010 additions & 130 deletions
File tree
- cdk
- src
- constructs
- handlers
- stacks
- test
- constructs
- handlers
- docs
- guides
- src/content/docs/using
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
28 | 29 | | |
29 | 30 | | |
30 | 31 | | |
| |||
35 | 36 | | |
36 | 37 | | |
37 | 38 | | |
38 | | - | |
39 | | - | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
40 | 50 | | |
41 | 51 | | |
42 | 52 | | |
| |||
87 | 97 | | |
88 | 98 | | |
89 | 99 | | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
90 | 108 | | |
91 | 109 | | |
92 | 110 | | |
| |||
206 | 224 | | |
207 | 225 | | |
208 | 226 | | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
209 | 230 | | |
210 | 231 | | |
211 | 232 | | |
| |||
280 | 301 | | |
281 | 302 | | |
282 | 303 | | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
283 | 311 | | |
284 | 312 | | |
285 | 313 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
326 | 326 | | |
327 | 327 | | |
328 | 328 | | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
329 | 341 | | |
330 | 342 | | |
331 | 343 | | |
| |||
376 | 388 | | |
377 | 389 | | |
378 | 390 | | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
379 | 403 | | |
380 | 404 | | |
381 | 405 | | |
| |||
0 commit comments