Skip to content

feat(guardion): Guardion middleware + replace x-portkey-* with x-guardion-* headers#1

Open
rafaelsandroni wants to merge 1 commit into2.0.0from
guardion-v2
Open

feat(guardion): Guardion middleware + replace x-portkey-* with x-guardion-* headers#1
rafaelsandroni wants to merge 1 commit into2.0.0from
guardion-v2

Conversation

@rafaelsandroni
Copy link

Summary

This PR introduces the full Guardion integration layer on top of the Portkey AI Gateway fork and removes all x-portkey-* header references from Guardion-owned code, replacing them with x-guardion-* throughout.

What's included

New Guardion middleware (src/middlewares/guardion/)

  • index.tsguardionAuth middleware: extracts x-guardion-api-key / Authorization header, fetches GET /v1/config from the Guard API, caches the result (60s soft TTL, 24h hard TTL), injects the mapped config as x-guardion-config, and spoofs an OrganisationDetails context so the downstream auth middleware passes cleanly
  • mapper.tsmapGuardConfig(): translates Guard API config JSON (routing, features, guardrails) into the gateway's internal config format
  • telemetry.ts — buffered async telemetry flush: batches up to 50 logs and flushes every 5s to POST /v1/telemetry/logs on the Guard API, fire-and-forget

New Guardion plugin (src/plugins/guardion/)

  • guardrails.tshandler: calls POST /v1/eval on the Guard API, resolves monitorflag, intercept + correctionredact, interceptblock; fails open on Guard API downtime
  • sessionCache.ts — per-(apiKey, applicationId) rolling flag/block counters with configurable lock thresholds and durations
  • toolPolicy.ts — allowlist/denylist enforcement on tools[] declarations (beforeRequestHook) and tool_calls[] in responses (afterRequestHook)
  • guardrails.test.ts — 9 unit tests covering all resolution paths

Header refactor (this changeset)

  • edgeHeaderTranslator.ts — removed the bidirectional x-portkey-x-guardion- sync loop; x-guardion-policy now maps directly to x-guardion-config (no more x-portkey-config writes)
  • mapper.ts — renamed mapGuardConfigToPortkeymapGuardConfig, portkeyConfigguardionConfig; updated comments
  • guardion/index.ts — updated import to use renamed mapGuardConfig
  • sessionCache.ts — updated comment removing "Portkey's native cache handlers" wording

Integration test

  • tests/integration/src/middlewares/edgeHeaderTranslator/edgeHeaderTranslator.test.ts

Request flow

Client request (x-guardion-api-key or Authorization: Bearer)
  ↓
edgeHeaderTranslator       — normalizes x-guardion-policy → x-guardion-config
  ↓
guardionAuth               — fetches /v1/config, injects config header, spoofs org context
  ↓
authN / authZ              — reads spoofed OrganisationDetails, passes
  ↓
portkey()                  — reads x-guardion-config, activates hooks
  ↓
beforeRequestHook          — [guardion plugin] session lock, Guard API eval, tool policy
  ↓
LLM provider
  ↓
afterRequestHook           — [guardion plugin] output eval
  ↓
telemetry flush            — async batch → POST /v1/telemetry/logs

Remaining open issues (not in this PR)

# Item Priority
1 Implement GET /v1/config in Guard API Critical
2 Implement POST /v1/telemetry/logs in Guard API Critical
3 Create manifest.json + register guardion in conf.json Critical
4 Add policy field to EvaluationRequest schema Medium
5 Fix PolicyRequest schema: detectordetectors (array) Medium
6 Standardize Breakdown.result type Medium

Test plan

  • Start gateway with GUARD_API_URL=http://localhost:8000
  • Send request with x-guardion-api-key: <key> — verify Guard API /v1/config is called and config is injected
  • Send request with x-guardion-policy: my-policy — verify it is normalized to x-guardion-config: pc-my-policy
  • Verify x-portkey-* headers are no longer set or read by Guardion middleware
  • Trigger a guardrail violation — verify block / redact / flag responses are correct
  • Verify session lock engages after threshold is exceeded
  • Verify telemetry batch flushes to Guard API after 5s or 50 logs
  • Run unit tests: jest src/plugins/guardion/
  • Run integration tests: jest tests/integration/src/middlewares/edgeHeaderTranslator/

🤖 Generated with Claude Code

…an up portkey references

- edgeHeaderTranslator: remove bidirectional x-portkey ↔ x-guardion sync;
  x-guardion-policy now maps to x-guardion-config directly
- mapper.ts: rename mapGuardConfigToPortkey → mapGuardConfig, internal
  variable portkeyConfig → guardionConfig, update stale comments
- guardion/index.ts: update import and variable names to match renamed mapper export
- sessionCache.ts: remove reference to "Portkey's native cache handlers" in comment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment on lines +122 to +124
logger.info({
message: `fetchOrganisationIdFromAPIKey options: ${JSON.stringify(albusFetchOptions)}`,
});

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This logs sensitive data returned by
an access to API_KEY
as clear text.
This logs sensitive data returned by
an access to apiKey
as clear text.
This logs sensitive data returned by
an access to apiKey
as clear text.

Copilot Autofix

AI 7 days ago

In general, to fix clear-text logging of sensitive information, ensure that values like API keys, passwords, tokens, and authorization headers are never written to logs. Either: (1) remove such logs entirely, (2) log only non-sensitive metadata (e.g., URL, method, status code), or (3) explicitly redact or mask sensitive fields before logging.

Here, the problematic log is:

logger.info({
  message: `fetchOrganisationIdFromAPIKey options: ${JSON.stringify(albusFetchOptions)}`,
});

albusFetchOptions includes headers containing the apiKey, so we should avoid logging this whole object. The least intrusive change that preserves functionality is to log only non-sensitive parts of the request, such as the HTTP method and URL, and perhaps the list of header keys without their values. Since we must not change behavior outside logging, we will keep albusFetchOptions as-is for the fetch call and only modify the log. A simple, safe fix is to remove the detailed options logging or replace it with a redacted version that omits header values.

Concretely in src/services/albus/index.ts:

  • Leave albusFetchOptions unchanged so the request behavior is identical.
  • Replace the logger.info at lines 122–124 with a version that logs a safe summary, e.g., method and URL, and optionally that headers are present but not their values.
  • No new imports or helper methods are required; we can construct a minimal, non-sensitive object inline.

Suggested changeset 1
src/services/albus/index.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/services/albus/index.ts b/src/services/albus/index.ts
--- a/src/services/albus/index.ts
+++ b/src/services/albus/index.ts
@@ -120,7 +120,11 @@
   });
 
   logger.info({
-    message: `fetchOrganisationIdFromAPIKey options: ${JSON.stringify(albusFetchOptions)}`,
+    message: `fetchOrganisationIdFromAPIKey options: ${JSON.stringify({
+      method: albusFetchOptions.method,
+      url: albusFetchUrl,
+      headers: Object.keys(albusFetchOptions.headers),
+    })}`,
   });
 
   try {
EOF
@@ -120,7 +120,11 @@
});

logger.info({
message: `fetchOrganisationIdFromAPIKey options: ${JSON.stringify(albusFetchOptions)}`,
message: `fetchOrganisationIdFromAPIKey options: ${JSON.stringify({
method: albusFetchOptions.method,
url: albusFetchUrl,
headers: Object.keys(albusFetchOptions.headers),
})}`,
});

try {
Copilot is powered by AI and may make mistakes. Always verify output.
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.

1 participant