]> git.99rst.org Git - sgasser-llm-shield.git/log
sgasser-llm-shield.git
11 days agoFix container security findings (#153)
Stefan Gasser [Sun, 26 Jul 2026 07:35:56 +0000 (09:35 +0200)]
Fix container security findings (#153)

11 days agoAdd detector-neutral regression coverage (#151)
Stefan Gasser [Sun, 26 Jul 2026 07:05:22 +0000 (09:05 +0200)]
Add detector-neutral regression coverage (#151)

13 days agoBump version to 0.8.2 (#150) v0.8.2
Stefan Gasser [Thu, 23 Jul 2026 20:37:52 +0000 (22:37 +0200)]
Bump version to 0.8.2 (#150)

13 days agoUse a neutral container runtime (#149)
Stefan Gasser [Thu, 23 Jul 2026 20:26:05 +0000 (22:26 +0200)]
Use a neutral container runtime (#149)

13 days agoBump version to 0.8.1 (#148) v0.8.1
Stefan Gasser [Thu, 23 Jul 2026 17:02:47 +0000 (19:02 +0200)]
Bump version to 0.8.1 (#148)

13 days agoHarden the release container image (#147)
Stefan Gasser [Thu, 23 Jul 2026 16:58:25 +0000 (18:58 +0200)]
Harden the release container image (#147)

* Harden the release container image

* Fix PII detector test isolation

13 days agoFix web security issues in dashboard, detector, and CORS (#146)
Stefan Gasser [Thu, 23 Jul 2026 16:47:21 +0000 (18:47 +0200)]
Fix web security issues in dashboard, detector, and CORS (#146)

- Escape the client-supplied model string before rendering it into the
  dashboard logs table, preventing stored XSS in the dashboard origin.
- Bound the variable-name run in the ENV_PASSWORD and ENV_SECRET regexes
  to {0,128} to remove quadratic backtracking (ReDoS) on long inputs.
- Exclude the same-origin dashboard routes from the wildcard CORS policy
  so its unauthenticated JSON APIs are no longer readable cross-origin,
  while keeping permissive CORS for the proxy and mask APIs.

3 weeks agoBump version to 0.8.0 v0.8.0
Stefan Gasser [Sun, 12 Jul 2026 09:01:01 +0000 (11:01 +0200)]
Bump version to 0.8.0

3 weeks agoProtect OpenAI Responses requests (#142)
Stefan Gasser [Sun, 12 Jul 2026 08:48:16 +0000 (10:48 +0200)]
Protect OpenAI Responses requests (#142)

4 weeks agoAccept Claude Code system role messages v0.7.6
Stefan Gasser [Tue, 7 Jul 2026 12:25:42 +0000 (14:25 +0200)]
Accept Claude Code system role messages

4 weeks agoBump version to 0.7.5 (#138) v0.7.5
Stefan Gasser [Fri, 3 Jul 2026 09:41:14 +0000 (11:41 +0200)]
Bump version to 0.7.5 (#138)

4 weeks agoAdd configurable detector timeout (#137)
Stefan Gasser [Fri, 3 Jul 2026 09:38:01 +0000 (11:38 +0200)]
Add configurable detector timeout (#137)

4 weeks agoBump version to 0.7.4 (#136) v0.7.4
Stefan Gasser [Thu, 2 Jul 2026 20:49:10 +0000 (22:49 +0200)]
Bump version to 0.7.4 (#136)

4 weeks agoAvoid concurrent detector span scans (#135)
Stefan Gasser [Thu, 2 Jul 2026 20:45:49 +0000 (22:45 +0200)]
Avoid concurrent detector span scans (#135)

5 weeks agoUpdate browser extension install copy (#133)
Stefan Gasser [Mon, 29 Jun 2026 09:42:22 +0000 (11:42 +0200)]
Update browser extension install copy (#133)

5 weeks agoBump version to 0.7.3 v0.7.3
Stefan Gasser [Sun, 28 Jun 2026 23:42:11 +0000 (01:42 +0200)]
Bump version to 0.7.3

5 weeks agoRefine positioning copy
Stefan Gasser [Sun, 28 Jun 2026 23:23:52 +0000 (01:23 +0200)]
Refine positioning copy

5 weeks agoSoften compliance boundary wording
Stefan Gasser [Sun, 28 Jun 2026 23:17:01 +0000 (01:17 +0200)]
Soften compliance boundary wording

5 weeks agoRefine PasteGuard positioning docs (#129)
Stefan Gasser [Sun, 28 Jun 2026 22:59:08 +0000 (00:59 +0200)]
Refine PasteGuard positioning docs (#129)

5 weeks agoAdd Postgres-compatible request logging (#127)
Stefan Gasser [Sun, 28 Jun 2026 21:16:12 +0000 (23:16 +0200)]
Add Postgres-compatible request logging (#127)

Abstract dashboard request logging behind Kysely so it runs on either
SQLite (default) or Postgres, selected via logging.driver. The schema and
migrations are shared across both backends, existing SQLite databases are
baselined and upgraded in place, and Logger reads/writes are async with
numeric coercion for Postgres aggregates. Includes unit, legacy-upgrade,
and live-Postgres integration tests plus updated config/docs.

5 weeks agoMove privacy helpers into domain modules (#125)
Stefan Gasser [Sun, 28 Jun 2026 18:59:54 +0000 (20:59 +0200)]
Move privacy helpers into domain modules (#125)

5 weeks agoConsolidate masking restoration into shared helpers (#124)
Stefan Gasser [Sun, 28 Jun 2026 18:41:49 +0000 (20:41 +0200)]
Consolidate masking restoration into shared helpers (#124)

* Add shared restoration helpers

* Share stream restoration across providers

* Extract request privacy pipeline

* Remove deferred TODO note

* Remove dead unmask wrappers after restoration refactor

pii/mask and secrets/mask no longer re-export unmask/stream/flush/response
wrappers; callers go through the shared StreamRestorer and restoreResponse.
Drop the unused unmask re-export from masking/service and the restoreText helper.

Simplify processPrivacyPipeline to derive PII masking from config.mode and
drop the unused originalRequest/piiMasked result fields and the maskPII option.

5 weeks agoBump version to 0.7.2 (#123) v0.7.2
Stefan Gasser [Sun, 28 Jun 2026 09:16:21 +0000 (11:16 +0200)]
Bump version to 0.7.2 (#123)

5 weeks agoApply markers to restored secrets (#122)
Stefan Gasser [Sun, 28 Jun 2026 09:12:15 +0000 (11:12 +0200)]
Apply markers to restored secrets (#122)

5 weeks agoBump version to 0.7.1 v0.7.1
Stefan Gasser [Sat, 27 Jun 2026 07:33:37 +0000 (09:33 +0200)]
Bump version to 0.7.1

5 weeks agoImprove GLiNER detector caching (#119)
Elad Kapusta [Sat, 27 Jun 2026 07:23:34 +0000 (10:23 +0300)]
Improve GLiNER detector caching (#119)

6 weeks agoBump version to 0.7.0 v0.7.0
Stefan Gasser [Wed, 24 Jun 2026 06:43:39 +0000 (08:43 +0200)]
Bump version to 0.7.0

6 weeks agoUpdate tested integration docs (#117)
Stefan Gasser [Wed, 24 Jun 2026 06:34:12 +0000 (08:34 +0200)]
Update tested integration docs (#117)

6 weeks agoLimit masking scans to input roles (#115)
Stefan Gasser [Tue, 23 Jun 2026 18:52:25 +0000 (20:52 +0200)]
Limit masking scans to input roles (#115)

Default PII and secrets detection to scan only user-controlled roles (user, tool, function, mcp) instead of every message role. Infer per-span roles in the Codex and Anthropic extractors so harness-injected context (environment_context, system-reminder, Codex AGENTS.md) and system/assistant content stay unscanned.

Restrict the dashboard preview to scanned roles after masking.

6 weeks agoTune GLiNER confidence floors (#114)
Stefan Gasser [Tue, 23 Jun 2026 11:41:55 +0000 (13:41 +0200)]
Tune GLiNER confidence floors (#114)

* Tune GLiNER confidence floors

* Fix Docker torch dependency install

6 weeks agoDefault phone detection to international formats (#111)
Stefan Gasser [Tue, 23 Jun 2026 10:07:38 +0000 (12:07 +0200)]
Default phone detection to international formats (#111)

* Default phone detection to international formats

* Update mask API phone example

6 weeks agoFix OpenAI stream line buffering (#112)
Stefan Gasser [Tue, 23 Jun 2026 09:42:43 +0000 (11:42 +0200)]
Fix OpenAI stream line buffering (#112)

6 weeks agoClean up unused exports and route helpers (#113)
Stefan Gasser [Tue, 23 Jun 2026 09:39:04 +0000 (11:39 +0200)]
Clean up unused exports and route helpers (#113)

* Remove unused postcss dependency

* Drop unused secret and masking exports

* Extract streaming header helper

* Trim redundant JSDoc comments

* Document Codex login setup

* Revert "Document Codex login setup"

This reverts commit a60ef991da233710e3785a964a6a11b6540f8de3.

6 weeks agoStop seeding config.yaml into new Conductor workspaces (#110)
Stefan Gasser [Tue, 23 Jun 2026 08:55:05 +0000 (10:55 +0200)]
Stop seeding config.yaml into new Conductor workspaces (#110)

Remove file_include_globs so each workspace regenerates config.yaml from config.example.yaml in setup instead of inheriting a stale copy. The inherited copy carried a fixed detector_url that didn't match the per-workspace detector port, leaving the proxy pointed at the wrong detector. The remaining globs only covered an empty .env, so the setting is dropped entirely.

6 weeks agoImprove Conductor dev workflow (#109)
Stefan Gasser [Tue, 23 Jun 2026 07:53:28 +0000 (09:53 +0200)]
Improve Conductor dev workflow (#109)

6 weeks agoRemove language detection from PII flow (#108)
Stefan Gasser [Tue, 23 Jun 2026 07:42:46 +0000 (09:42 +0200)]
Remove language detection from PII flow (#108)

* Remove language detection from PII flow

* Format detector tests

* Fix detector phone region typing

6 weeks agoTrack browser-extension requests separately in the dashboard (#107)
Stefan Gasser [Tue, 23 Jun 2026 07:31:37 +0000 (09:31 +0200)]
Track browser-extension requests separately in the dashboard (#107)

Add a `source` column to request logs so `/api/mask` calls sent with the
`X-PasteGuard-Source: browser-extension` header are counted and badged
separately from direct API calls and provider-proxied requests.

- Backfill `source` from `provider` for existing rows via migration
- Split stats into api_requests vs browser_extension_requests
- Replace the route-only Provider column with a unified Source column
- Add an Extension stat card

6 weeks agoEnable all secret detectors by default (#106)
Stefan Gasser [Tue, 23 Jun 2026 07:25:40 +0000 (09:25 +0200)]
Enable all secret detectors by default (#106)

6 weeks agoRedirect root path to dashboard or health (#105)
Stefan Gasser [Tue, 23 Jun 2026 06:40:42 +0000 (08:40 +0200)]
Redirect root path to dashboard or health (#105)

6 weeks agoRename masking whitelist config to allowlist (#104)
Stefan Gasser [Tue, 23 Jun 2026 06:34:57 +0000 (08:34 +0200)]
Rename masking whitelist config to allowlist (#104)

Rename the masking whitelist config key and its associated schema,
types, and helper (filterAllowlistedEntities) to use allowlist
terminology. Updates docs and the example config to match.

6 weeks agoBump version to 0.6.0 (#103) v0.6.0
Stefan Gasser [Tue, 23 Jun 2026 06:08:57 +0000 (08:08 +0200)]
Bump version to 0.6.0 (#103)

6 weeks agoAdd configurable masking denylist and regex whitelist (#101)
Stefan Gasser [Tue, 23 Jun 2026 06:04:47 +0000 (08:04 +0200)]
Add configurable masking denylist and regex whitelist (#101)

* Add configurable masking denylist

* Align whitelist pattern config

* Harden denylist/whitelist masking

- Merge denylist matches additively so they never shrink detector coverage
- Skip denylist matches inside existing placeholders to avoid corrupting secret/PII masks
- Anchor regex whitelist to the full entity so a partial match can't unmask larger PII
- Skip detection when PII detection is off and no denylist is configured
- Reject regex patterns that match the empty string at config load
- Reuse the conflict-resolver overlap helper; update docs and tests

* Fix streaming unmask when a placeholder delimiter splits across chunks

findPartialPlaceholderStart only buffered when the full "[[" delimiter appeared
within one chunk. If a stream chunk ended with a lone "[" (the first half of
"[["), it was emitted as safe, so the placeholder was never reassembled and
leaked to the client un-restored. Buffer a trailing partial of the start
delimiter too. Restores PII and secrets placeholders that the upstream model
tokenizes across the "[[" boundary.

* Remove ReDoS caveat from PII detection docs

* Simplify partial-placeholder detection and cover the closing-delimiter split

Replace the single-iteration loop in findPartialPlaceholderStart with a direct
trailing-bracket check, and add tests for a placeholder whose closing "]]" is
split across stream chunks.

* Exclude denylist matches by known placeholders; de-magic the match score

Replace the placeholder-shape regex heuristic with exact exclusion against the
real placeholders carried over from secrets masking: secretPlaceholders() is
threaded through detectPII/analyzeRequest and passed in /api/mask, so a denylist
pattern can no longer match (and corrupt) the internals of an existing
secret/PII placeholder. Also replace the vestigial denylist match score 2 with a
named DENYLIST_MATCH_SCORE constant.

* Require knownPlaceholders on detectPII so routes can't skip placeholder protection

* Clarify whitelist/denylist docs and drop Claude Code wording from the default

6 weeks agoMerge pull request #102 from eladkapusta/main
Stefan Gasser [Mon, 22 Jun 2026 20:06:52 +0000 (22:06 +0200)]
Merge pull request #102 from eladkapusta/main

Fix for metadata chunk processing in OpenAi-API providers using streaming mode

6 weeks agoAdded fix for receiving metadata (usage etc.) in API streaming mode - some providers...
Elad Kapusta [Mon, 22 Jun 2026 17:44:39 +0000 (20:44 +0300)]
Added fix for receiving metadata (usage etc.) in API streaming mode - some providers use a single chunk with empty string content which fell through

6 weeks agoReplace Presidio with an open-source PII detector (#100) v0.5.0
Stefan Gasser [Mon, 22 Jun 2026 10:29:05 +0000 (12:29 +0200)]
Replace Presidio with an open-source PII detector (#100)

Replace the Microsoft Presidio analyzer with a self-hosted open-source PII detector (Python/FastAPI, detector/) shipped in the all-in-one image: multilingual GLiNER NER plus a deterministic regex/checksum layer for structured identifiers.

- Add VAT_CODE (EU VAT, checksum-validated); member-state prefixes only, case-insensitive and overlap-safe so a label or word can't hide a valid number.
- /api/mask: detect secrets before PII so a connection string isn't partly masked as an email (matches the provider routes).
- Rename presidio_url to detector_url; language-agnostic detection; CPU-only torch; detector CI (ruff/pyright/pytest).
- Bump version to 0.5.0.

7 weeks agoAdd PII detection accuracy benchmark (#99)
Stefan Gasser [Wed, 17 Jun 2026 20:24:19 +0000 (22:24 +0200)]
Add PII detection accuracy benchmark (#99)

Adds a corpus-driven PII accuracy benchmark against the Presidio /analyze endpoint, plus pl/ro precision negatives, non-western PERSON coverage, and address-like LOCATION cases.

7 weeks agoAdd Conductor workspace configuration (#95)
Stefan Gasser [Sun, 14 Jun 2026 20:02:05 +0000 (22:02 +0200)]
Add Conductor workspace configuration (#95)

8 weeks agoBump version to 0.4.2 (#93) v0.4.2
Stefan Gasser [Wed, 10 Jun 2026 12:47:00 +0000 (14:47 +0200)]
Bump version to 0.4.2 (#93)

8 weeks agoMerge pull request #92 from sgasser/fix/log-masked-content-with-secrets
Stefan Gasser [Wed, 10 Jun 2026 12:35:29 +0000 (14:35 +0200)]
Merge pull request #92 from sgasser/fix/log-masked-content-with-secrets

Fix masked content not logged when secrets are detected

8 weeks agoUse correct double-bracket placeholder format in comments and docs
Stefan Gasser [Wed, 10 Jun 2026 12:32:05 +0000 (14:32 +0200)]
Use correct double-bracket placeholder format in comments and docs

8 weeks agoExpose logging and secrets_detection in /info, remove unused log_content
Stefan Gasser [Wed, 10 Jun 2026 12:19:59 +0000 (14:19 +0200)]
Expose logging and secrets_detection in /info, remove unused log_content

Follow-ups from #91:

- /info now includes secrets_detection and logging sections so users can
  verify their config is loaded (the issue reporter could not).
- The dashboard fallback message no longer claims log_masked_content is
  false when content is simply absent for another reason.
- Remove the log_content option: it was defined in config and documented,
  but no code ever read it. Logging raw request/response content would
  contradict the privacy guarantees, so it is removed rather than
  implemented. Zod strips unknown keys, so existing configs that still
  set log_content keep loading.

8 weeks agoFix masked content not logged when secrets are detected
Stefan Gasser [Wed, 10 Jun 2026 12:03:49 +0000 (14:03 +0200)]
Fix masked content not logged when secrets are detected

Masked content was never stored whenever secrets were detected, ignoring
the log_masked_content setting. The dashboard then showed a misleading
"Masked content not logged (log_masked_content: false)" message.

With secrets_detection.action "mask" (the default), maskedContent already
has both PII and secrets replaced by placeholders by the time it reaches
the logger, so it is safe to store. Gate on log_masked_content plus
whether detected secrets were actually masked, via a pure
shouldLogMaskedContent helper. The secrets-masked condition keeps route
mode with action "route_local" safe: there secrets are intentionally left
raw for the trusted local provider and must never be persisted.

This also enforces log_masked_content centrally for the openai/anthropic/
codex routes, which previously passed masked content unconditionally.

The helper lives in its own module so the unit test can import it without
tripping over the wholesale logger mock used by other route tests.

Fixes #91

2 months agoMerge pull request #90 from sgasser/release/v0.4.1 v0.4.1
Stefan Gasser [Thu, 4 Jun 2026 12:58:00 +0000 (14:58 +0200)]
Merge pull request #90 from sgasser/release/v0.4.1

Bump version to 0.4.1

2 months agoBump version to 0.4.1
Stefan Gasser [Thu, 4 Jun 2026 12:55:47 +0000 (14:55 +0200)]
Bump version to 0.4.1

2 months agoUpdate vulnerable Bun dependencies (#89)
Stefan Gasser [Thu, 4 Jun 2026 12:52:57 +0000 (14:52 +0200)]
Update vulnerable Bun dependencies (#89)

* Update vulnerable Bun dependencies

* Remove unnecessary dependency override

2 months agoBump version to 0.4.0 (#88) v0.4.0
Stefan Gasser [Thu, 4 Jun 2026 08:15:09 +0000 (10:15 +0200)]
Bump version to 0.4.0 (#88)

2 months agoAdd Codex CLI support (#87)
Stefan Gasser [Thu, 4 Jun 2026 08:12:52 +0000 (10:12 +0200)]
Add Codex CLI support (#87)

2 months agoBump version to 0.3.6 v0.3.6
Stefan Gasser [Tue, 2 Jun 2026 15:29:17 +0000 (17:29 +0200)]
Bump version to 0.3.6

2 months agoHandle structured OpenAI stream content (#84)
thstyl2000 [Tue, 2 Jun 2026 15:22:32 +0000 (17:22 +0200)]
Handle structured OpenAI stream content (#84)

2 months agoFix route test PII detector mock isolation (#85)
Stefan Gasser [Tue, 2 Jun 2026 15:11:34 +0000 (17:11 +0200)]
Fix route test PII detector mock isolation (#85)

4 months agofix: use UID 1000 for container user to match typical Linux host users (#77) v0.3.5
Stefan Gasser [Fri, 13 Mar 2026 07:54:28 +0000 (08:54 +0100)]
fix: use UID 1000 for container user to match typical Linux host users (#77)

Fixes permission denied errors when mounting volumes. Most Linux desktop
users have UID 1000, so bind-mounted directories now work without manual
chown.

Fixes #76

5 months agofeat: add configurable request timeout (#79) v0.3.4
Stefan Gasser [Wed, 4 Mar 2026 08:28:29 +0000 (09:28 +0100)]
feat: add configurable request timeout (#79)

Add server.request_timeout config option (default: 600 seconds).
Previously hardcoded to 120 seconds which caused timeouts for
complex Opus queries.

Set to 0 to disable timeout entirely.

Closes #78

5 months agoBump version to 0.3.3 (#75) v0.3.3
Stefan Gasser [Fri, 27 Feb 2026 18:58:19 +0000 (19:58 +0100)]
Bump version to 0.3.3 (#75)

5 months agofix: preserve unknown fields in Anthropic schemas (restores prompt caching) (#74)
raseidl [Fri, 27 Feb 2026 18:55:36 +0000 (19:55 +0100)]
fix: preserve unknown fields in Anthropic schemas (restores prompt caching) (#74)

* fix: preserve cache_control in Anthropic schemas to restore prompt caching

* fix: add biome-ignore for passthrough tests, extend fix to OpenAI

- Add biome-ignore comments to suppress noExplicitAny in tests
  (required for testing unknown field preservation)
- Add .passthrough() to OpenAI schemas for consistency
  (OpenAIMessageSchema, OpenAIContentPartSchema)
- Format Anthropic schemas to match project style

* test: add OpenAI passthrough tests for consistency with Anthropic

- Schema tests for name, tool_calls, audio content, unknown fields
- Extractor tests for field preservation through applyMasked

---------

Co-authored-by: Stefan Gasser <redacted>
5 months agoBump version to 0.3.2 (#72) v0.3.2
Stefan Gasser [Fri, 20 Feb 2026 20:57:46 +0000 (21:57 +0100)]
Bump version to 0.3.2 (#72)

5 months agoFix SIGILL crash on CPUs without AVX2 support (#70) (#71)
Stefan Gasser [Fri, 20 Feb 2026 20:52:52 +0000 (21:52 +0100)]
Fix SIGILL crash on CPUs without AVX2 support (#70) (#71)

Copy Bun binary from official oven/bun:1-slim image instead of using
the install script. The official images use baseline builds for x64,
which only require SSE4.2 and work on older/low-power CPUs like the
Intel Atom C3558R that lack AVX2 instructions.

Also fixes compatibility with updated presidio-analyzer base image:
- Use USER root for build, then switch to non-root (UID 1001) for runtime
- Fix Presidio config paths (/app/presidio_analyzer/conf/)
- Move PasteGuard to /pasteguard to avoid overwriting Presidio's /app
- Update supervisord to run without root privileges
- Update volume mount paths in docker-compose.yml and docs

5 months agoFix missing Presidio recognizers for URL, US_SSN, CRYPTO, etc. (#69) v0.3.1
Stefan Gasser [Mon, 9 Feb 2026 08:05:13 +0000 (09:05 +0100)]
Fix missing Presidio recognizers for URL, US_SSN, CRYPTO, etc. (#69)

The config generator only included 6 recognizers, missing standard ones
like UrlRecognizer, UsSsnRecognizer, CryptoRecognizer. This caused
detection failures when users enabled these entity types.

Changes:
- Add GLOBAL_RECOGNIZERS for pattern-based detection (7 recognizers)
- Add LANGUAGE_RECOGNIZERS for language-specific detection
- Only load language-specific recognizers when that language is configured
- EN: US + UK recognizers (8)
- ES: Spanish NIF/NIE (2)
- IT: Italian documents (5)
- PL: Polish PESEL (1)
- KO: Korean RRN (1)

Fixes #67

6 months agoRestructure docs around use cases (#66)
Stefan Gasser [Sat, 31 Jan 2026 21:50:00 +0000 (22:50 +0100)]
Restructure docs around use cases (#66)

* Restructure docs around use cases

Replace the single Integrations page with four dedicated use case pages
(Browser Extension, Coding Tools, Apps & SDKs, Self-Hosted) so each
audience has a clear path through the docs.

- Rewrite introduction around four user contexts with cards
- Simplify quickstart to be tool-neutral
- Add Use Cases navigation group in mint.json
- Update README links to point to new docs pages
- Replace demo.gif with comparison.png
- Remove duplicate h1 headings (Mintlify renders frontmatter title)
- Remove Copilot from platform lists per positioning doc

* Sync docs and README with website messaging

- Update tagline to "AI gets the context. Not your secrets."
- Add trust statement and detection stats (30+ types, 24 languages)
- Rename use cases to match website: Chat, Apps, Coding Tools, API Integration
- Reorder use cases consistently across docs and README
- Replace comparison image with refined rose/teal design
- Add light/dark mode image support for GitHub and Mintlify

* Improve dark mode text contrast in comparison image

6 months agoBump version to 0.3.0 (#64) v0.3.0
Stefan Gasser [Mon, 26 Jan 2026 10:27:41 +0000 (11:27 +0100)]
Bump version to 0.3.0 (#64)

6 months agoFix basic auth by using HTTPException.getResponse() (#63)
Stefan Gasser [Mon, 26 Jan 2026 10:02:18 +0000 (11:02 +0100)]
Fix basic auth by using HTTPException.getResponse() (#63)

6 months agoIncrease Presidio startup timeout for multi-language images (#62)
Stefan Gasser [Mon, 26 Jan 2026 08:26:19 +0000 (09:26 +0100)]
Increase Presidio startup timeout for multi-language images (#62)

The EU image loads 9 spaCy language models which takes ~2 minutes.
The previous 30-second timeout caused PasteGuard to restart multiple
times before Presidio was ready.

- Increase default timeout from 30s to 180s
- Add PRESIDIO_STARTUP_TIMEOUT env var for customization
- Document startup time in config and docs

Fixes #58

6 months agoRename API_KEY_OPENAI to API_KEY_SK with expanded pattern (#61)
Stefan Gasser [Mon, 26 Jan 2026 08:18:03 +0000 (09:18 +0100)]
Rename API_KEY_OPENAI to API_KEY_SK with expanded pattern (#61)

- Rename API_KEY_OPENAI to API_KEY_SK for broader coverage
- Expand pattern from /sk-.../ to /sk[-_].../ to match both hyphen and underscore
- Reduce minimum length from 45 to 20 chars for Stripe compatibility
- Now detects: OpenAI, Anthropic, Stripe, RevenueCat, and similar sk-prefixed keys
- Add tests for Stripe (sk_test_, sk_live_), Anthropic (sk-ant-), RevenueCat (sk_)
- Update all documentation and config examples

6 months agoMerge pull request #60 from sgasser/feature/remove-secret-masked-prefix
Stefan Gasser [Mon, 26 Jan 2026 07:43:16 +0000 (08:43 +0100)]
Merge pull request #60 from sgasser/feature/remove-secret-masked-prefix

Remove SECRET_MASKED_ prefix from secrets placeholders

6 months agoRemove SECRET_MASKED_ prefix from secrets placeholders
Stefan Gasser [Mon, 26 Jan 2026 07:15:59 +0000 (08:15 +0100)]
Remove SECRET_MASKED_ prefix from secrets placeholders

Simplify placeholder format by using [[TYPE_N]] for both PII and secrets.
This makes the format consistent and shorter.

Before: [[SECRET_MASKED_API_KEY_OPENAI_1]]
After:  [[API_KEY_OPENAI_1]]

6 months agoMerge pull request #57 from sgasser/feature/generic-mask-api
Stefan Gasser [Mon, 26 Jan 2026 07:20:09 +0000 (08:20 +0100)]
Merge pull request #57 from sgasser/feature/generic-mask-api

Add generic /api/mask endpoint for standalone text masking

6 months agoFix entity extraction and improve API consistency
Stefan Gasser [Mon, 26 Jan 2026 07:13:59 +0000 (08:13 +0100)]
Fix entity extraction and improve API consistency

- Use direct placeholder lookup instead of fragile substring matching
- Make error details always an array for consistent response format
- Add languageFallback field to response
- Add tests for error paths and edge cases

6 months agoReuse filterWhitelistedEntities and add combined masking test
Stefan Gasser [Wed, 21 Jan 2026 17:42:58 +0000 (18:42 +0100)]
Reuse filterWhitelistedEntities and add combined masking test

- Import filterWhitelistedEntities from pii/detect instead of duplicating logic
- Add test for combined PII + secrets masking (default behavior)

6 months agoRefactor logging interfaces for simpler data structures
Stefan Gasser [Wed, 21 Jan 2026 17:31:02 +0000 (18:31 +0100)]
Refactor logging interfaces for simpler data structures

- Simplify PIILogData: entityTypes string[] instead of allEntities objects
- Simplify SecretsLogData: types string[] instead of matches objects
- Move mapping logic into toPIILogData/toSecretsLogData converters
- Update api.ts to use createLogData() instead of manual construction

6 months agoAdd source tracking to dashboard
Stefan Gasser [Wed, 21 Jan 2026 06:56:56 +0000 (07:56 +0100)]
Add source tracking to dashboard

- Add API requests counter to stats section
- Add Source column to logs table with badges
  - API badge (amber) for /api/mask requests
  - PROXY badge (gray) for OpenAI/Anthropic proxy requests
- Backward compatible: existing logs show as PROXY

6 months agoAdd generic /api/mask endpoint for standalone text masking
Stefan Gasser [Wed, 21 Jan 2026 06:14:05 +0000 (07:14 +0100)]
Add generic /api/mask endpoint for standalone text masking

Adds a new POST /api/mask endpoint that can be used by browser extensions,
CLIs, or any client that needs to mask PII and secrets independently of
the OpenAI/Anthropic proxy routes.

Features:
- Detects and masks both PII and secrets (configurable via detect param)
- Returns context mapping for client-side unmasking
- Supports multi-turn conversations via startFrom counters
- Auto-detects language or accepts explicit language parameter
- Logs requests to dashboard for visibility
- Rejects whitespace-only text input
- Consistent error handling for both PII and secrets detection

6 months agoReplace generic provider/LLM terminology with OpenAI or Anthropic (#56) v0.2.1
Stefan Gasser [Tue, 20 Jan 2026 22:40:11 +0000 (23:40 +0100)]
Replace generic provider/LLM terminology with OpenAI or Anthropic (#56)

- Use specific names instead of "provider" or "LLM" in user-facing text
- Keep "local LLM" for Ollama/vLLM references
- Update README, docs, and configuration files

6 months agoAdd scan_roles and whitelist documentation (#55) v0.2.0
Stefan Gasser [Tue, 20 Jan 2026 22:12:25 +0000 (23:12 +0100)]
Add scan_roles and whitelist documentation (#55)

6 months agoAdd Anthropic API support (#51)
Stefan Gasser [Tue, 20 Jan 2026 22:06:58 +0000 (23:06 +0100)]
Add Anthropic API support (#51)

* Add Anthropic provider

- Add /anthropic/v1/messages endpoint with full API compatibility
- Support OAuth tokens from Claude Code for subscription users
- Provider-agnostic text extraction for PII/secrets masking
- Support streaming and non-streaming responses
- Remove unused cloud provider health checks (only local services need them)

* Add Anthropic provider documentation

- Update README and introduction to mention Anthropic support
- Add Claude Code and Anthropic SDK to integrations
- Document Anthropic provider config with OAuth support
- Create separate API reference pages for OpenAI and Anthropic
- Update navigation structure

* Improve provider error messages in logs

- Add errorMessage getter to parse OpenAI/Anthropic error formats
- Log parsed error message instead of generic "Provider error"

* Update docs wording for multi-provider support

- Clarify OpenAI and Anthropic APIs with compatible providers
- Note Anthropic endpoint is mask mode only (route mode coming)

* Add route mode support for Anthropic endpoint

- Add callLocalAnthropic function for Ollama's Anthropic API
- Update Anthropic route to support route mode with local provider
- Update docs to reflect both mask and route mode support

* Add Anthropic brand color to dashboard provider badges

* Fix duplicate /v1 prefix in Anthropic proxy wildcard handler

The path variable already contains the full path after stripping the
/anthropic prefix (e.g., /v1/messages or /api/foo). Adding /v1 again
caused double prefixes for v1 paths and incorrect paths for non-v1
endpoints like /api/event_logging/batch.

* Add role field to Anthropic extractor for scan_roles filtering

* Remove OAuth token reading, use transparent header forwarding

- Delete oauth.ts - no longer read tokens from local storage
- Simplify client.ts to forward all auth headers transparently
- Simplify anthropic.ts wildcard handler
- Add Claude Code system prompt to default whitelist
- Whitelist merges user entries with default (not replaces)

* Simplify wildcard proxies to fully transparent passthrough

* Fix wildcard proxy host header forwarding

* Update documentation: simplify intro, remove OAuth docs, add Anthropic to architecture

* Improve documentation: simplify intro, update API links, remove redundant api_key exports

6 months agoFix OpenAI wildcard proxy host header and query string forwarding (#54)
Stefan Gasser [Tue, 20 Jan 2026 21:23:49 +0000 (22:23 +0100)]
Fix OpenAI wildcard proxy host header and query string forwarding (#54)

6 months agoAdd whitelist config for masking exclusions (#53)
Stefan Gasser [Tue, 20 Jan 2026 20:52:02 +0000 (21:52 +0100)]
Add whitelist config for masking exclusions (#53)

Adds masking.whitelist config option to exclude specific text patterns
from PII masking. Useful for preventing false positives on known text
like company names or product identifiers.

- Add whitelist property to MaskingSchema (default: empty array)
- Add filterWhitelistedEntities function to filter detected PII
- Patterns match if detected text is contained in whitelist entry
  or whitelist entry is contained in detected text

6 months agoAdd scan_roles config for role-based PII/secrets filtering (#52)
Stefan Gasser [Tue, 20 Jan 2026 19:07:03 +0000 (20:07 +0100)]
Add scan_roles config for role-based PII/secrets filtering (#52)

Allows configuring which message roles to scan for PII and secrets.
By default all roles are scanned (existing behavior). When scan_roles
is set, only messages with matching roles are sent to Presidio.

Use case: Skip scanning large system prompts to reduce API calls and
avoid false positives on app-controlled content.

- Add role field to TextSpan type
- OpenAI extractor populates role from message
- PII detector filters by scan_roles before Presidio calls
- Secrets detector filters by scan_roles before detection

6 months agoRefactor for multi-provider architecture (#49)
Stefan Gasser [Mon, 19 Jan 2026 17:30:22 +0000 (18:30 +0100)]
Refactor for multi-provider architecture (#49)

Reorganizes the codebase to support multiple LLM providers with a clean,
extensible architecture. This is a preparatory refactor that improves
code organization without adding new provider support.

Architecture changes:
- Move masking utilities to src/masking/ (conflict-resolver, placeholders, context)
- Add provider-specific directories: src/providers/openai/
- Create shared provider utilities: src/providers/errors.ts, src/routes/utils.ts
- Extract OpenAI-specific code to src/masking/extractors/openai.ts
- Add service layer: src/services/pii.ts, src/services/secrets.ts
- Move stream transformer to provider directory

New patterns:
- Provider-agnostic text extraction with TextExtractor interface
- Shared error handling with ProviderError class
- Centralized timeout constants in src/constants/timeouts.ts
- Unified logging helpers in src/routes/utils.ts

Removed:
- src/services/decision.ts (logic moved to service layer)
- src/providers/openai-client.ts (replaced by src/providers/openai/client.ts)

All 219 tests pass.

6 months agoReorganize for multi-provider support (#48)
Stefan Gasser [Sat, 17 Jan 2026 20:07:39 +0000 (21:07 +0100)]
Reorganize for multi-provider support (#48)

* Reorganize for multi-provider support

Move LLM client to providers/ and rename proxy routes to openai:
- src/services/llm-client.ts → src/providers/openai-client.ts
- src/routes/proxy.ts → src/routes/openai.ts
- Rename proxyRoutes export to openaiRoutes

Prepares codebase for adding Anthropic provider support.

* Fix import sorting for biome check

6 months agoAdd per-part PII/secrets detection for multimodal messages (#47)
Stefan Gasser [Sat, 17 Jan 2026 19:32:54 +0000 (20:32 +0100)]
Add per-part PII/secrets detection for multimodal messages (#47)

Each text block in multimodal messages is now analyzed separately
rather than concatenating all text together. This enables precise
masking while preserving message structure.

Changes:
- Per-part detection for both PII and secrets
- Symmetric pii/ and secrets/ module structure
- Shared utilities in utils/message-transform.ts
- Rename "redact" → "mask" for consistency
- Centralize MaskResult, Span interfaces

Breaking changes:
- Header: X-PasteGuard-Secrets-Redacted → X-PasteGuard-Secrets-Masked
- Config: secrets_detection.action "redact" → "mask"

6 months agoAdd Production Setup section to installation docs (#45)
Stefan Gasser [Sat, 17 Jan 2026 14:24:42 +0000 (15:24 +0100)]
Add Production Setup section to installation docs (#45)

6 months agoSimplify Quick Start docker command (#44)
Stefan Gasser [Sat, 17 Jan 2026 14:17:20 +0000 (15:17 +0100)]
Simplify Quick Start docker command (#44)

6 months agoAll-in-one Docker image with prebuilt language support (#42) v0.1.0
Stefan Gasser [Sat, 17 Jan 2026 13:10:36 +0000 (14:10 +0100)]
All-in-one Docker image with prebuilt language support (#42)

* All-in-one Docker image with dev mode support

Simplify deployment with a single container that includes both the proxy
and Presidio PII detection. No git clone required - just docker run.

Changes:
- Combined Dockerfile: Multi-stage build with Presidio + Bun in one container
- supervisord: Process manager for running both services
- Prebuilt images: ghcr.io/sgasser/pasteguard:en (~2.7GB) and :eu (~12GB)
- Release workflow: GitHub Actions to build en/eu images on version tags
- Dev mode: docker compose up presidio -d for local development with hot-reload
- Updated docs: curl-based installation, no cloning needed for production
- Language models: Changed from _md to _lg for better PII detection accuracy

Image tags:
- en (default/latest): English only
- eu: European languages (en, de, es, fr, it, nl, pl, pt, ro)

* Move Docker files into docker/ directory

Reorganize Docker-related files for cleaner project structure:
- Dockerfile → docker/Dockerfile
- supervisord.conf → docker/supervisord.conf
- presidio/ → docker/presidio/

Keep docker-compose.yml in root for convenience (docker compose up works).
Update all path references in workflow, configs, and docs.

* Fix CI: update Dockerfile path

* Include config.example.yaml in Docker image for zero-config startup

* Simplify docs: zero-config quickstart

* Add missing ENV detection entities to config example

Add ENV_PASSWORD, ENV_SECRET, and CONNECTION_STRING to the
secrets_detection entities section to match what's documented
and implemented.

* Auto-configure languages per Docker image

- Add PASTEGUARD_LANGUAGES env var to Dockerfile (set from LANGUAGES build arg)
- Update config.example.yaml to use env var with fallback to 'en'
- Support comma-separated string for languages config (e.g., "en,de,fr")
- EN image now auto-enables English, EU image auto-enables all 9 EU languages

Users can still override via config.yaml with array syntax if needed.

* Update docs: languages are auto-configured per image

* Clarify runtime vs build-time env vars in docs

* Update docs: languages are auto-configured per Docker image

* Remove confusing env var override example from docs

* List specific EU languages in docs instead of 'All 9'

6 months agodocs: add browser extension beta section (#43)
Stefan Gasser [Sat, 17 Jan 2026 11:28:40 +0000 (12:28 +0100)]
docs: add browser extension beta section (#43)

Add beta signup CTA to README and introduction docs to build
early adopter community for the upcoming browser extension.

6 months agoMerge pull request #41 from sgasser/feature/config-refactor-and-wildcard-proxy
Stefan Gasser [Sat, 17 Jan 2026 00:10:25 +0000 (01:10 +0100)]
Merge pull request #41 from sgasser/feature/config-refactor-and-wildcard-proxy

Refactor config: providers.upstream → providers.openai, add wildcard proxy

6 months agoRemove secrets detection tests from proxy.test.ts (tested in detect.test.ts)
Stefan Gasser [Sat, 17 Jan 2026 00:05:08 +0000 (01:05 +0100)]
Remove secrets detection tests from proxy.test.ts (tested in detect.test.ts)

6 months agoFix wildcard proxy body forwarding, simplify config example
Stefan Gasser [Fri, 16 Jan 2026 23:53:24 +0000 (00:53 +0100)]
Fix wildcard proxy body forwarding, simplify config example

6 months agoRefactor config: providers.upstream → providers.openai, add wildcard proxy
Stefan Gasser [Fri, 16 Jan 2026 23:36:14 +0000 (00:36 +0100)]
Refactor config: providers.upstream → providers.openai, add wildcard proxy

Config changes:
- Rename providers.upstream to providers.openai for clarity
- Remove routing config section (simplified to: PII → local, no PII → openai)
- Move local provider to top-level config (not under providers)
- Change default secrets action from block to redact

Proxy changes:
- Replace specific /models route with wildcard /* proxy
- Supports all OpenAI endpoints: /models, /embeddings, /audio/*, etc.

Documentation:
- Update all docs to reflect new config structure
- Remove docs/api-reference/models.mdx (now covered by wildcard proxy)

6 months agofix: log and display API errors in dashboard (#40)
Stefan Gasser [Fri, 16 Jan 2026 22:30:36 +0000 (23:30 +0100)]
fix: log and display API errors in dashboard (#40)

Fixes #35

- Add LLMError class to preserve upstream status code and body
- Add status_code and error_message columns to request logs
- Add Status column to dashboard with OK/error badges
- Pass through upstream errors (429, 401, etc.) with original status
- Return OpenAI-compatible JSON format for all error responses
- Set X-PasteGuard headers consistently via Hono context

6 months agoMerge pull request #39 from sgasser/fix/issue-33-offset-error
Stefan Gasser [Fri, 16 Jan 2026 16:40:34 +0000 (17:40 +0100)]
Merge pull request #39 from sgasser/fix/issue-33-offset-error

Fix overlapping entity conflict resolution (#33)

6 months agoFix lint: format code, remove unused isContainedIn
Stefan Gasser [Fri, 16 Jan 2026 16:39:28 +0000 (17:39 +0100)]
Fix lint: format code, remove unused isContainedIn

git clone https://git.99rst.org/PROJECT