Skip to content

feat(protect): per-site Pulse rules client (fetch rules by site UUID)#62

Merged
devlob merged 4 commits into
mainfrom
pulse-per-site-rules-connect
Jul 14, 2026
Merged

feat(protect): per-site Pulse rules client (fetch rules by site UUID)#62
devlob merged 4 commits into
mainfrom
pulse-per-site-rules-connect

Conversation

@devlob

@devlob devlob commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

What

Adds a per-site rules source to the runtime guard so a Pulse app fetches the exact WAF rules for its vulnerable npm packages using only its site UUID — no WAF token, no user config.

Changes

  • PulseRuleClient (src/protect/engine/pulse-client.js) — GET <base>/rules/<uuid>, fail-open, last-known-good disk cache. Sibling of PatchstackRuleClient.
  • createProtection — new siteUuid rule source in resolveRules; precedence siteUuid → token → bundled rules. Cached + fail-open like the token path.
  • protect installer — bakes the site UUID from .patchstackrc.json into the scaffolded guard (replaces a sentinel) so the runtime calls pulse/rules/{uuid} with zero user config; falls back to PATCHSTACK_SITE_UUID env or the bundled rule.
  • guard template / typessiteUuid? + pulseRulesUrl? on CreateProtectionOptions.

Tests

Rebased onto main (post #57 Tier-3 + #58 tests/protect/ restructure). New tests under tests/protect/:

  • pulse-client.test.ts — fetch / parse / cache / fail-open
  • runtime-pulse.test.tscreateProtection fetches by UUID + falls back to bundled on fetch failure
  • dave-example-rule.test.ts — engine enforces a real hub-format rule (isset/contains) inline + via the fetch-by-UUID path
  • protect-install.test.ts — installer bakes the UUID

Full suite green: 386 tests, 26 files.

Related

  • Server endpoint GET /monitor/pulse/rules/{uuid} ships in patchstack/saas#999.
  • Merging + tagging cuts the next release (0.3.9) so @latest fetches per-site rules by UUID; bundled rules.json stays the offline fallback.

Adds PulseRuleClient (GET <base>/rules/<uuid>, fail-open + last-known-good cache)
as a third rule source in createProtection; precedence siteUuid -> token ->
bundled. The protect installer bakes the site UUID from .patchstackrc.json into
the scaffolded guard so the runtime fetches per-site rules with no user config.
Bundled rules.json stays as the offline fallback.
@coderbuds

coderbuds Bot commented Jul 14, 2026

Copy link
Copy Markdown

Well-structured per-site rules client with comprehensive test coverage.

🎯 Quality: 100% Elite · 📦 Size: Large — consider splitting if possible

📈 This month: Your 59th PR — above team average · Averaging Excellent

See how your team is trending →

@patchstackdave patchstackdave left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might need changes per my PR comment in saas?

devlob added 2 commits July 14, 2026 15:48
… expiry; guard bakeSiteUuid on UUID format

- bakeSiteUuid now validates UUID format before baking, falling through to the
  inert placeholder for missing/malformed values (avoids junk in the TS literal)
- runtime-pulse: last-known-good disk cache, siteUuid>token precedence, whitelist
  suppression over the Pulse path
- pulse-client: cache expiry past TTL + clearCache() refetch
- protect-install: realistic UUID fixture + malformed-value inert case
…connect

# Conflicts:
#	src/protect/install.ts
#	src/protect/templates/guard.ts
@devlob

devlob commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

/review

@devlob devlob requested a review from patchstackdave July 14, 2026 14:42
@devlob devlob merged commit 16b645a into main Jul 14, 2026
4 checks passed
@devlob devlob deleted the pulse-per-site-rules-connect branch July 14, 2026 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants