Commit 08237b8
authored
fix(labeler): notification contact-resolution, unsubscribe, and verification-trust fixes (review round 1) (#2116)
* fix(labeler): honor RFC 8058 one-click unsubscribe query param
A one-click unsubscribe POST carries the recipient hash (c) in the
List-Unsubscribe header URL's query string and only List-Unsubscribe=One-Click
in the body. The POST parser read c from the form body only, so a one-click
request returned success without suppressing the recipient. Read c (and the
confirm token) from the body first, falling back to the query.
* fix(labeler): resolve package contacts for release-subject notices
A release subject's rkey is slug:version, but contactTargetFromUri passed the
whole rkey as the package slug, so getPackage missed the package and its
security[]/authors[] contacts were skipped in favor of the publisher-profile
fallback. Parse the parent package slug (before the version delimiter) so a
release notice reaches the package's contacts; a package rkey and a malformed
rkey both degrade safely.
* fix(labeler): alert operators when a takedown notice has no contact
An emergency takedown issuance that resolves no publisher contact recorded only
a generic undeliverable notification row, so the signal that manual outreach is
needed was easy to miss. Emit a dedicated takedown-no-contact operational event
(severity high) plus its operator-alert outbox row on that path. A takedown
retract and a takedown that does resolve a contact emit nothing, and a deduped
replay does not re-emit.
* fix(labeler): gate double-opt-in bypass on trusted, current verification
A publisher's verification claim upgraded a contact past double opt-in on the
strength of any in-force claim. Verification claims are self-assertable — the
aggregator indexes any issuer, including self-issued ones — so a publisher could
name a victim address in its profile, self-issue a claim, and have an
unconfirmed notice sent there. Upgrade only on a claim whose issuer is in the
configured trust set, that is unexpired, and whose bound displayName still
matches the publisher's current identity. No issuer is trusted by default, so
every address falls back to double opt-in.
The publisher view carries a current displayName but no handle (the aggregator's
identity-event ingestion is unbuilt), so the binding checks displayName only;
handle-binding is deferred until the view carries a handle.
* fix(labeler): make takedown no-contact alert recoverable
The takedown-no-contact operator alert was emitted only on the first pass's
send outcome, after the undeliverable notifications row had committed. A
transient failure of that fire-and-forget event write lost the alert
permanently: a deferred replay dedups on the existing notifications row and
never reached the emit again. Key the alert on its own operational_events row
instead, and run the check on every takedown issuance (including the dedup
replay path), so a replay after a failed emit recovers the signal while a normal
replay does not duplicate it.
* fix(labeler): scope unsubscribe query fallback, keep confirm body-only
The one-click query fallback was applied to the confirm and not-me POST parsers
too, so a scanner POSTing the original confirmation link URL (with c and t in
the query) could confirm an address without the user submitting the form.
Restrict the query fallback to the RFC 8058 one-click unsubscribe path; confirm
and not-me require their capability from the POST body.
* fix(labeler): validate release rkey shape; make no-contact alert dedup atomic
Contact resolution stripped a subject rkey at the first colon without checking
the collection or the canonical release rkey shape, so a malformed colon-bearing
subject could resolve a DIFFERENT package's contacts. Only strip a canonical
release record (release collection + well-formed slug:version); anything else
keeps its rkey and degrades to the publisher tier.
The takedown-no-contact alert used an existence-check-then-insert with no
uniqueness guard, so two concurrent replays could double-emit. Add a
UNIQUE(action_id, event_type) index (migration 0012) and make the insert
idempotent (ON CONFLICT DO NOTHING), with the outbox gated on the event being
written; concurrency and replay now converge to exactly one event.
* fix(labeler): scope dedup index to takedown-no-contact; require semver
Migration 0012 created a GLOBAL unique index on (action_id, event_type), which
would fail at migration time on any existing DB holding historical duplicate
rows of other event types. Make it PARTIAL (WHERE event_type =
'takedown-no-contact') and point the insert's ON CONFLICT target at that
predicate, so it stays safe on historical data while still deduping the alert.
The release-rkey parse validated only the slug and a non-empty suffix, so
'gallery:not-semver' still resolved the gallery package. Validate the complete
canonical shape (slug + percent-decoded semver version, mirroring the
aggregator's parseReleaseRkey) before stripping; anything else degrades to the
publisher tier.1 parent 3692bbf commit 08237b8
7 files changed
Lines changed: 845 additions & 54 deletions
Lines changed: 14 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
18 | | - | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
19 | 21 | | |
20 | 22 | | |
21 | 23 | | |
| |||
114 | 116 | | |
115 | 117 | | |
116 | 118 | | |
117 | | - | |
| 119 | + | |
118 | 120 | | |
119 | 121 | | |
120 | 122 | | |
121 | | - | |
| 123 | + | |
122 | 124 | | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
128 | | - | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
129 | 138 | | |
130 | 139 | | |
131 | 140 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
20 | | - | |
21 | | - | |
22 | | - | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
23 | 28 | | |
24 | 29 | | |
25 | 30 | | |
| |||
41 | 46 | | |
42 | 47 | | |
43 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
44 | 54 | | |
45 | 55 | | |
46 | 56 | | |
| |||
60 | 70 | | |
61 | 71 | | |
62 | 72 | | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
63 | 78 | | |
64 | 79 | | |
65 | 80 | | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
66 | 85 | | |
67 | 86 | | |
68 | 87 | | |
| |||
82 | 101 | | |
83 | 102 | | |
84 | 103 | | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
85 | 108 | | |
86 | 109 | | |
87 | 110 | | |
| |||
327 | 350 | | |
328 | 351 | | |
329 | 352 | | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
330 | 444 | | |
331 | 445 | | |
332 | 446 | | |
| |||
491 | 605 | | |
492 | 606 | | |
493 | 607 | | |
494 | | - | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
495 | 614 | | |
496 | 615 | | |
497 | 616 | | |
| |||
531 | 650 | | |
532 | 651 | | |
533 | 652 | | |
534 | | - | |
535 | | - | |
536 | | - | |
537 | | - | |
538 | | - | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
539 | 671 | | |
540 | 672 | | |
541 | | - | |
| 673 | + | |
| 674 | + | |
542 | 675 | | |
543 | 676 | | |
544 | 677 | | |
| 678 | + | |
545 | 679 | | |
546 | 680 | | |
547 | 681 | | |
548 | | - | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
549 | 692 | | |
550 | 693 | | |
551 | 694 | | |
| |||
555 | 698 | | |
556 | 699 | | |
557 | 700 | | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
558 | 709 | | |
559 | 710 | | |
560 | 711 | | |
| |||
577 | 728 | | |
578 | 729 | | |
579 | 730 | | |
580 | | - | |
581 | | - | |
582 | | - | |
583 | | - | |
584 | | - | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
585 | 738 | | |
586 | 739 | | |
587 | 740 | | |
588 | | - | |
| 741 | + | |
589 | 742 | | |
590 | | - | |
| 743 | + | |
591 | 744 | | |
592 | 745 | | |
593 | 746 | | |
594 | 747 | | |
595 | 748 | | |
596 | 749 | | |
597 | 750 | | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
598 | 782 | | |
599 | 783 | | |
600 | 784 | | |
| |||
0 commit comments