shieldstack-ts 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +9 -0
- package/.gitattributes +2 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +61 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +35 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +27 -0
- package/.github/workflows/ci.yml +69 -0
- package/CHANGELOG.md +59 -0
- package/CONTRIBUTING.md +83 -0
- package/Dockerfile +45 -0
- package/LICENSE +21 -0
- package/README.md +277 -0
- package/SECURITY.md +42 -0
- package/demo.ts +41 -0
- package/docker-compose.yml +49 -0
- package/examples/demo/AGENTS.md +5 -0
- package/examples/demo/CLAUDE.md +1 -0
- package/examples/demo/README.md +36 -0
- package/examples/demo/eslint.config.mjs +18 -0
- package/examples/demo/next.config.ts +8 -0
- package/examples/demo/package-lock.json +6041 -0
- package/examples/demo/package.json +25 -0
- package/examples/demo/public/file.svg +1 -0
- package/examples/demo/public/globe.svg +1 -0
- package/examples/demo/public/next.svg +1 -0
- package/examples/demo/public/vercel.svg +1 -0
- package/examples/demo/public/window.svg +1 -0
- package/examples/demo/src/app/api/chat/route.ts +38 -0
- package/examples/demo/src/app/favicon.ico +0 -0
- package/examples/demo/src/app/globals.css +75 -0
- package/examples/demo/src/app/layout.tsx +30 -0
- package/examples/demo/src/app/page.module.css +142 -0
- package/examples/demo/src/app/page.tsx +162 -0
- package/examples/demo/tsconfig.json +34 -0
- package/package.json +44 -0
- package/src/adapters/express.ts +28 -0
- package/src/adapters/hono.ts +22 -0
- package/src/adapters/index.ts +4 -0
- package/src/adapters/next.ts +26 -0
- package/src/budgeting/InMemoryStore.ts +26 -0
- package/src/budgeting/RedisStore.ts +41 -0
- package/src/budgeting/index.ts +5 -0
- package/src/budgeting/tokenLimiter.ts +60 -0
- package/src/budgeting/types.ts +10 -0
- package/src/core/ShieldStack.ts +119 -0
- package/src/index.ts +7 -0
- package/src/observability/index.ts +2 -0
- package/src/observability/logger.ts +62 -0
- package/src/sanitizers/index.ts +4 -0
- package/src/sanitizers/injection.ts +49 -0
- package/src/sanitizers/pii.ts +97 -0
- package/src/sanitizers/secrets.ts +49 -0
- package/src/streams/StreamSanitizer.ts +46 -0
- package/src/streams/index.ts +2 -0
- package/src/validation/index.ts +2 -0
- package/src/validation/zodValidator.ts +46 -0
- package/tests/injection.test.ts +23 -0
- package/tests/pii.test.ts +21 -0
- package/tests/redis.integration.ts +65 -0
- package/tests/redisStore.test.ts +107 -0
- package/tests/tokenLimiter.test.ts +27 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +10 -0
package/.dockerignore
ADDED
package/.gitattributes
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
name: Bug Report
|
|
2
|
+
description: File a bug report
|
|
3
|
+
title: "[Bug]: "
|
|
4
|
+
labels: ["bug", "triage"]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: |
|
|
9
|
+
Thanks for taking the time to fill out this bug report!
|
|
10
|
+
|
|
11
|
+
- type: input
|
|
12
|
+
id: version
|
|
13
|
+
attributes:
|
|
14
|
+
label: ShieldStack TS Version
|
|
15
|
+
placeholder: "0.1.0"
|
|
16
|
+
validations:
|
|
17
|
+
required: true
|
|
18
|
+
|
|
19
|
+
- type: dropdown
|
|
20
|
+
id: runtime
|
|
21
|
+
attributes:
|
|
22
|
+
label: Runtime
|
|
23
|
+
options:
|
|
24
|
+
- Node.js
|
|
25
|
+
- Bun
|
|
26
|
+
- Cloudflare Workers
|
|
27
|
+
- Other
|
|
28
|
+
validations:
|
|
29
|
+
required: true
|
|
30
|
+
|
|
31
|
+
- type: textarea
|
|
32
|
+
id: description
|
|
33
|
+
attributes:
|
|
34
|
+
label: Bug Description
|
|
35
|
+
description: A clear and concise description of the bug
|
|
36
|
+
validations:
|
|
37
|
+
required: true
|
|
38
|
+
|
|
39
|
+
- type: textarea
|
|
40
|
+
id: reproduction
|
|
41
|
+
attributes:
|
|
42
|
+
label: Steps to Reproduce
|
|
43
|
+
placeholder: |
|
|
44
|
+
1. Initialize ShieldStack with config...
|
|
45
|
+
2. Call evaluateRequest with...
|
|
46
|
+
3. Observe...
|
|
47
|
+
validations:
|
|
48
|
+
required: true
|
|
49
|
+
|
|
50
|
+
- type: textarea
|
|
51
|
+
id: expected
|
|
52
|
+
attributes:
|
|
53
|
+
label: Expected Behavior
|
|
54
|
+
validations:
|
|
55
|
+
required: true
|
|
56
|
+
|
|
57
|
+
- type: textarea
|
|
58
|
+
id: context
|
|
59
|
+
attributes:
|
|
60
|
+
label: Additional Context
|
|
61
|
+
description: Any logs, screenshots, or other context
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Feature Request
|
|
2
|
+
description: Suggest an idea for ShieldStack TS
|
|
3
|
+
title: "[Feature]: "
|
|
4
|
+
labels: ["enhancement"]
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
id: problem
|
|
8
|
+
attributes:
|
|
9
|
+
label: Problem Statement
|
|
10
|
+
description: What problem would this feature solve?
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
|
|
14
|
+
- type: textarea
|
|
15
|
+
id: solution
|
|
16
|
+
attributes:
|
|
17
|
+
label: Proposed Solution
|
|
18
|
+
description: Describe how you'd like this to work
|
|
19
|
+
validations:
|
|
20
|
+
required: true
|
|
21
|
+
|
|
22
|
+
- type: textarea
|
|
23
|
+
id: alternatives
|
|
24
|
+
attributes:
|
|
25
|
+
label: Alternatives Considered
|
|
26
|
+
description: Any alternative approaches you have considered
|
|
27
|
+
|
|
28
|
+
- type: dropdown
|
|
29
|
+
id: priority
|
|
30
|
+
attributes:
|
|
31
|
+
label: Priority
|
|
32
|
+
options:
|
|
33
|
+
- Low
|
|
34
|
+
- Medium
|
|
35
|
+
- High — blocking production use
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
<!-- Describe your changes clearly and concisely -->
|
|
4
|
+
|
|
5
|
+
## Type of Change
|
|
6
|
+
|
|
7
|
+
- [ ] Bug fix (non-breaking change which fixes an issue)
|
|
8
|
+
- [ ] New feature (non-breaking change which adds functionality)
|
|
9
|
+
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
|
10
|
+
- [ ] Documentation update
|
|
11
|
+
|
|
12
|
+
## Checklist
|
|
13
|
+
|
|
14
|
+
- [ ] My changes follow the code style of this project
|
|
15
|
+
- [ ] I have run `npm run typecheck` with no errors
|
|
16
|
+
- [ ] I have added tests that prove my fix/feature works
|
|
17
|
+
- [ ] All existing tests pass (`npm run test`)
|
|
18
|
+
- [ ] I have updated the documentation where necessary
|
|
19
|
+
- [ ] I have updated `CHANGELOG.md` with my changes
|
|
20
|
+
|
|
21
|
+
## Related Issues
|
|
22
|
+
|
|
23
|
+
<!-- Link related issues: Fixes #123 -->
|
|
24
|
+
|
|
25
|
+
## Testing
|
|
26
|
+
|
|
27
|
+
<!-- Describe how you tested your changes -->
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
env:
|
|
10
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
name: Test (Node ${{ matrix.node-version }})
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
|
|
17
|
+
strategy:
|
|
18
|
+
matrix:
|
|
19
|
+
node-version: [20.x, 22.x, 24.x]
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Setup Node.js ${{ matrix.node-version }}
|
|
25
|
+
uses: actions/setup-node@v4
|
|
26
|
+
with:
|
|
27
|
+
node-version: ${{ matrix.node-version }}
|
|
28
|
+
cache: 'npm'
|
|
29
|
+
|
|
30
|
+
- name: Install dependencies
|
|
31
|
+
run: npm ci
|
|
32
|
+
|
|
33
|
+
- name: Type check
|
|
34
|
+
run: npm run typecheck
|
|
35
|
+
|
|
36
|
+
- name: Run tests
|
|
37
|
+
run: npm run test
|
|
38
|
+
|
|
39
|
+
- name: Build
|
|
40
|
+
run: npm run build
|
|
41
|
+
|
|
42
|
+
publish:
|
|
43
|
+
name: Publish to npm
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
needs: test
|
|
46
|
+
# Only publish on push to main AND when NPM_TOKEN secret is configured
|
|
47
|
+
if: github.ref == 'refs/heads/main' && github.event_name == 'push' && secrets.NPM_TOKEN != ''
|
|
48
|
+
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@v4
|
|
51
|
+
|
|
52
|
+
- name: Setup Node.js
|
|
53
|
+
uses: actions/setup-node@v4
|
|
54
|
+
with:
|
|
55
|
+
node-version: '22.x'
|
|
56
|
+
registry-url: 'https://registry.npmjs.org'
|
|
57
|
+
cache: 'npm'
|
|
58
|
+
|
|
59
|
+
- name: Install dependencies
|
|
60
|
+
run: npm ci
|
|
61
|
+
|
|
62
|
+
- name: Build
|
|
63
|
+
run: npm run build
|
|
64
|
+
|
|
65
|
+
- name: Publish
|
|
66
|
+
run: npm publish --access public
|
|
67
|
+
env:
|
|
68
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
69
|
+
continue-on-error: true
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to ShieldStack TS will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## [0.1.0] — 2026-04-03
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
#### Core Engine
|
|
14
|
+
- `ShieldStack` class as the central middleware orchestrator
|
|
15
|
+
- `async evaluateRequest()` pipeline with ordered security checks
|
|
16
|
+
- `validateOutput()` for Zod schema enforcement on LLM responses
|
|
17
|
+
- `createStreamSanitizer()` returning a native `TransformStream<Uint8Array, Uint8Array>`
|
|
18
|
+
|
|
19
|
+
#### Sanitizers
|
|
20
|
+
- `PIIRedactor` — regex-based detection for emails, phone numbers, credit cards, and SSNs
|
|
21
|
+
- Four configurable policies: `redact`, `hash`, `mask`, `block`
|
|
22
|
+
- `InjectionDetector` — weighted heuristic scoring system to detect prompt injection attempts
|
|
23
|
+
- `SecretsDetector` — high-entropy pattern detection for AWS keys, Slack/GitHub tokens
|
|
24
|
+
|
|
25
|
+
#### Budgeting
|
|
26
|
+
- `TokenLimiter` — async token bucket with sliding window enforcement
|
|
27
|
+
- `RateLimitStore` interface for pluggable storage backends
|
|
28
|
+
- `InMemoryStore` — default synchronous-equivalent in-process store
|
|
29
|
+
- `RedisStore` — duck-typed Redis adapter compatible with `ioredis`, `@upstash/redis`, `node-redis`
|
|
30
|
+
|
|
31
|
+
#### Streaming
|
|
32
|
+
- `StreamSanitizer` — real-time chunk-level PII and secrets redaction via `TransformStream`
|
|
33
|
+
|
|
34
|
+
#### Observability
|
|
35
|
+
- `Logger` — structured JSON audit logging with `info`, `warn`, `error`, `debug` levels
|
|
36
|
+
- Event types: `data_scrubbed`, `injection_detected`, `secrets_detected`, `rate_limit_exceeded`
|
|
37
|
+
|
|
38
|
+
#### Adapters
|
|
39
|
+
- `expressShield()` — Express.js `req/res/next` middleware
|
|
40
|
+
- `withShield()` — Next.js App Router route handler wrapper
|
|
41
|
+
- `honoShield()` — Hono middleware for Cloudflare Workers
|
|
42
|
+
|
|
43
|
+
#### Developer Experience
|
|
44
|
+
- Full CJS and ESM dual output via `tsup`
|
|
45
|
+
- TypeScript declaration files (`.d.ts` and `.d.mts`)
|
|
46
|
+
- Vitest test suite: 15 tests across 4 modules (100% pass rate)
|
|
47
|
+
- Multi-stage production `Dockerfile` with non-root user security hardening
|
|
48
|
+
- `docker-compose.yml` with Redis healthcheck and persistent volume
|
|
49
|
+
- Next.js `examples/demo` — live "Trust Terminal" chat UI
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Upcoming — [0.2.0]
|
|
54
|
+
|
|
55
|
+
- ML-based prompt injection classifier (DistilBERT)
|
|
56
|
+
- Custom user-defined PII pattern registration
|
|
57
|
+
- OpenTelemetry metrics export
|
|
58
|
+
- Fastify adapter
|
|
59
|
+
- Cloudflare Durable Objects store
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Contributing to ShieldStack TS
|
|
2
|
+
|
|
3
|
+
## Development Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
git clone https://github.com/ShujaSN/shieldstack-ts.git
|
|
7
|
+
cd shieldstack-ts
|
|
8
|
+
npm install
|
|
9
|
+
npm run build
|
|
10
|
+
npm run test
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Development Workflow
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm run dev # Watch mode — rebuilds library on file changes
|
|
19
|
+
npm run test # Run full Vitest suite
|
|
20
|
+
npm run typecheck # TypeScript type checking (no emit)
|
|
21
|
+
npm run lint # ESLint across src/
|
|
22
|
+
npm run format # Prettier formatting
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Commit Convention
|
|
28
|
+
|
|
29
|
+
We use [Conventional Commits](https://www.conventionalcommits.org/):
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
feat: add Fastify adapter
|
|
33
|
+
fix: correct SSN regex false positive
|
|
34
|
+
docs: update Redis setup guide
|
|
35
|
+
test: add edge case for empty stream chunks
|
|
36
|
+
chore: bump tsup to v9
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Branching Strategy
|
|
42
|
+
|
|
43
|
+
| Branch | Purpose |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `main` | Stable, production releases |
|
|
46
|
+
| `develop` | Integration branch for features |
|
|
47
|
+
| `feat/*` | Individual feature branches |
|
|
48
|
+
| `fix/*` | Bug fix branches |
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Pull Request Guidelines
|
|
53
|
+
|
|
54
|
+
1. All PRs must target `develop`, not `main`
|
|
55
|
+
2. All tests must pass: `npm run test`
|
|
56
|
+
3. TypeScript must compile cleanly: `npm run typecheck`
|
|
57
|
+
4. Update `CHANGELOG.md` under `Upcoming` with your changes
|
|
58
|
+
5. Fill out the PR template completely
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Adding a New Adapter
|
|
63
|
+
|
|
64
|
+
1. Create `src/adapters/yourframework.ts`
|
|
65
|
+
2. Export a function: `export function yourShield(shield: ShieldStack) { ... }`
|
|
66
|
+
3. Re-export from `src/adapters/index.ts`
|
|
67
|
+
4. Add a usage example to `README.md`
|
|
68
|
+
5. Add tests in `tests/`
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Code Style
|
|
73
|
+
|
|
74
|
+
- Functional where practical, class-based for stateful services
|
|
75
|
+
- Async/await everywhere (no raw Promises)
|
|
76
|
+
- No `any` types except where unavoidable in framework adapters
|
|
77
|
+
- Prefer `const` over `let`
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Reporting Vulnerabilities
|
|
82
|
+
|
|
83
|
+
See [SECURITY.md](./SECURITY.md) — do not open public issues for security concerns.
|
package/Dockerfile
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
FROM node:20-alpine AS lib-builder
|
|
2
|
+
|
|
3
|
+
WORKDIR /lib
|
|
4
|
+
|
|
5
|
+
COPY package*.json ./
|
|
6
|
+
RUN npm ci --ignore-scripts
|
|
7
|
+
|
|
8
|
+
COPY . .
|
|
9
|
+
RUN npm run build
|
|
10
|
+
|
|
11
|
+
FROM node:20-alpine AS demo-builder
|
|
12
|
+
|
|
13
|
+
WORKDIR /demo
|
|
14
|
+
|
|
15
|
+
COPY examples/demo/package*.json ./
|
|
16
|
+
RUN npm ci --ignore-scripts
|
|
17
|
+
|
|
18
|
+
# Pull in the compiled library directly instead of relying on the file: link
|
|
19
|
+
COPY --from=lib-builder /lib/dist /demo/node_modules/shieldstack-ts/dist
|
|
20
|
+
COPY --from=lib-builder /lib/package.json /demo/node_modules/shieldstack-ts/package.json
|
|
21
|
+
|
|
22
|
+
COPY examples/demo .
|
|
23
|
+
RUN npm run build
|
|
24
|
+
|
|
25
|
+
FROM node:20-alpine AS runner
|
|
26
|
+
|
|
27
|
+
WORKDIR /app
|
|
28
|
+
|
|
29
|
+
ENV NODE_ENV=production
|
|
30
|
+
ENV NEXT_TELEMETRY_DISABLED=1
|
|
31
|
+
|
|
32
|
+
RUN addgroup --system --gid 1001 nodejs && \
|
|
33
|
+
adduser --system --uid 1001 nextjs
|
|
34
|
+
|
|
35
|
+
COPY --from=demo-builder /demo/public ./public
|
|
36
|
+
COPY --from=demo-builder --chown=nextjs:nodejs /demo/.next/standalone ./
|
|
37
|
+
COPY --from=demo-builder --chown=nextjs:nodejs /demo/.next/static ./.next/static
|
|
38
|
+
|
|
39
|
+
USER nextjs
|
|
40
|
+
|
|
41
|
+
EXPOSE 3000
|
|
42
|
+
ENV PORT=3000
|
|
43
|
+
ENV HOSTNAME="0.0.0.0"
|
|
44
|
+
|
|
45
|
+
CMD ["node", "server.js"]
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ali Shuja
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<h1>🛡️ ShieldStack TS</h1>
|
|
4
|
+
|
|
5
|
+
<p><strong>Enterprise-grade LLM security middleware for TypeScript applications.</strong><br/>
|
|
6
|
+
Stop PII leaks, jailbreaks, and runaway API bills before they reach your LLM.</p>
|
|
7
|
+
|
|
8
|
+
[](https://www.npmjs.com/package/shieldstack-ts)
|
|
9
|
+
[](./LICENSE)
|
|
10
|
+
[](#testing)
|
|
11
|
+
[](https://www.typescriptlang.org/)
|
|
12
|
+
[](https://workers.cloudflare.com/)
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Overview
|
|
19
|
+
|
|
20
|
+
ShieldStack TS is a **framework-agnostic, edge-compatible** middleware layer that sits between your application and any LLM provider — OpenAI, Anthropic, Ollama, and others.
|
|
21
|
+
|
|
22
|
+
It intercepts every request and response with sub-2ms overhead, enforcing your security policies without changing your application logic.
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Your App ──► ShieldStack ──► LLM Provider
|
|
26
|
+
↑ ↑ ↑ ↑
|
|
27
|
+
│ │ │ └─ Token Budget Enforcement
|
|
28
|
+
│ │ └──── Secrets Detection
|
|
29
|
+
│ └─────── Prompt Injection Defense
|
|
30
|
+
└────────── PII Redaction
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
| Feature | Description |
|
|
38
|
+
|---|---|
|
|
39
|
+
| 🔐 **PII Redaction** | Automatically strips emails, phone numbers, credit cards, and SSNs |
|
|
40
|
+
| 🛡️ **Injection Detection** | Heuristic risk-scoring blocks jailbreak and system-override attempts |
|
|
41
|
+
| 🔑 **Secrets Scanning** | Detects AWS keys, GitHub/Slack tokens, and high-entropy strings |
|
|
42
|
+
| 💰 **Denial-of-Wallet Prevention** | Per-user async token budgets backed by in-memory or Redis storage |
|
|
43
|
+
| 🌊 **Stream Sanitization** | Real-time chunk-level redaction via native `TransformStream` API |
|
|
44
|
+
| 📊 **Audit Logging** | Structured JSON telemetry for every security event |
|
|
45
|
+
| ✅ **Schema Validation** | Enforce structured LLM output contracts with Zod |
|
|
46
|
+
| ⚡ **Edge-First** | Runs on Node.js, Bun, and Cloudflare Workers — zero native dependencies |
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install shieldstack-ts
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { ShieldStack } from 'shieldstack-ts';
|
|
62
|
+
|
|
63
|
+
const shield = new ShieldStack({
|
|
64
|
+
pii: {
|
|
65
|
+
policy: 'redact', // 'redact' | 'hash' | 'mask' | 'block'
|
|
66
|
+
emails: true,
|
|
67
|
+
creditCards: true,
|
|
68
|
+
phoneNumbers: true,
|
|
69
|
+
},
|
|
70
|
+
injectionDetection: {
|
|
71
|
+
threshold: 0.8, // 0.0 (lenient) – 1.0 (strict)
|
|
72
|
+
},
|
|
73
|
+
tokenLimiter: {
|
|
74
|
+
maxTokens: 10000, // max tokens per user per window
|
|
75
|
+
windowMs: 3600000, // 1 hour
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Evaluate a prompt before sending to your LLM
|
|
80
|
+
// Throws if blocked, returns sanitized text if safe
|
|
81
|
+
const safePrompt = await shield.evaluateRequest(userInput, userId, tokenEstimate);
|
|
82
|
+
|
|
83
|
+
// Sanitize the LLM's streaming response in real-time
|
|
84
|
+
const sanitizedStream = llmResponse.body.pipeThrough(shield.createStreamSanitizer());
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Framework Adapters
|
|
90
|
+
|
|
91
|
+
### Express.js
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { expressShield } from 'shieldstack-ts';
|
|
95
|
+
|
|
96
|
+
app.post('/chat', expressShield(shield), (req, res) => {
|
|
97
|
+
// req.body is already PII-sanitized
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Next.js App Router
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { withShield } from 'shieldstack-ts';
|
|
105
|
+
|
|
106
|
+
async function chatHandler(req: Request): Promise<Response> {
|
|
107
|
+
// your handler
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const POST = withShield(shield, chatHandler);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Hono (Cloudflare Workers)
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { honoShield } from 'shieldstack-ts';
|
|
117
|
+
|
|
118
|
+
app.use('/chat', honoShield(shield));
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Distributed Rate Limiting with Redis
|
|
124
|
+
|
|
125
|
+
For multi-server deployments, pass a Redis client to share token budgets across all instances:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { ShieldStack, RedisStore } from 'shieldstack-ts';
|
|
129
|
+
import { Redis } from '@upstash/redis'; // or ioredis, node-redis
|
|
130
|
+
|
|
131
|
+
const shield = new ShieldStack({
|
|
132
|
+
tokenLimiter: {
|
|
133
|
+
maxTokens: 50000,
|
|
134
|
+
windowMs: 3600000,
|
|
135
|
+
store: new RedisStore(new Redis({ url: process.env.REDIS_URL })),
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
> **Duck-typed client support** — any Redis client implementing `.get()`, `.set()`, and `.del()` works without forced peer dependencies.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Redaction Policies
|
|
145
|
+
|
|
146
|
+
Configure how PII is handled per field type:
|
|
147
|
+
|
|
148
|
+
| Policy | Result | Use Case |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `redact` | `[REDACTED_EMAIL]` | Maximum privacy |
|
|
151
|
+
| `hash` | `[HASHED_EMAIL]` | Pseudonymous, consistent |
|
|
152
|
+
| `mask` | `***` | Visual concealment |
|
|
153
|
+
| `block` | Throws error | Zero-tolerance compliance |
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Structured Output Validation
|
|
158
|
+
|
|
159
|
+
Enforce a Zod schema on LLM responses to prevent hallucinated shapes:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { z } from 'zod';
|
|
163
|
+
|
|
164
|
+
const responseSchema = z.object({
|
|
165
|
+
answer: z.string(),
|
|
166
|
+
confidence: z.number().min(0).max(1),
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const shield = new ShieldStack({ schema: responseSchema });
|
|
170
|
+
|
|
171
|
+
// Validates and throws if LLM output doesn't match
|
|
172
|
+
const validatedOutput = shield.validateOutput(llmJsonResponse);
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Performance
|
|
178
|
+
|
|
179
|
+
| Operation | Overhead |
|
|
180
|
+
|---|---|
|
|
181
|
+
| Token limit check (in-memory) | < 0.1ms |
|
|
182
|
+
| Token limit check (Redis) | 1–3ms |
|
|
183
|
+
| Injection detection | < 0.5ms |
|
|
184
|
+
| PII redaction | < 1ms |
|
|
185
|
+
| Stream sanitization per chunk | < 0.2ms |
|
|
186
|
+
| **Total end-to-end** | **< 2ms** |
|
|
187
|
+
|
|
188
|
+
LLM calls take 500ms–5s. ShieldStack adds less than **0.4%** overhead.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Testing
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
npm run test
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
✓ tests/pii.test.ts (3 tests)
|
|
200
|
+
✓ tests/injection.test.ts (3 tests)
|
|
201
|
+
✓ tests/tokenLimiter.test.ts (3 tests)
|
|
202
|
+
✓ tests/redisStore.test.ts (6 tests)
|
|
203
|
+
|
|
204
|
+
Test Files 4 passed
|
|
205
|
+
Tests 15 passed
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Running the Demo
|
|
211
|
+
|
|
212
|
+
A full Next.js demo app is included to visualize the middleware in real-time:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
cd examples/demo
|
|
216
|
+
npm install
|
|
217
|
+
npm run dev
|
|
218
|
+
# Open http://localhost:3000
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Try sending:
|
|
222
|
+
- A prompt containing a fake email → watch `[REDACTED_EMAIL]` appear in the stream
|
|
223
|
+
- `"Ignore previous instructions"` → watch it get blocked with a 403 error
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Docker
|
|
228
|
+
|
|
229
|
+
Build the production image:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
docker build -t shieldstack-demo .
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Run the full stack (Demo + Redis):
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
docker compose up --build
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Uses a **3-stage multi-stage build** for a minimal (`~80MB`), non-root, production-hardened container image.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Compatibility
|
|
246
|
+
|
|
247
|
+
| Runtime | Supported |
|
|
248
|
+
|---|---|
|
|
249
|
+
| Node.js 18+ | ✅ |
|
|
250
|
+
| Bun 1.x | ✅ |
|
|
251
|
+
| Cloudflare Workers | ✅ |
|
|
252
|
+
| Deno (via npm compat) | ✅ |
|
|
253
|
+
| AWS Lambda | ✅ |
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Contributing
|
|
258
|
+
|
|
259
|
+
Contributions, issues, and feature requests are welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
|
|
260
|
+
|
|
261
|
+
1. Fork the repository
|
|
262
|
+
2. Create your feature branch: `git checkout -b feat/my-feature`
|
|
263
|
+
3. Commit your changes: `git commit -m 'feat: add my feature'`
|
|
264
|
+
4. Push to the branch: `git push origin feat/my-feature`
|
|
265
|
+
5. Open a Pull Request
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Security
|
|
270
|
+
|
|
271
|
+
To report a vulnerability, see [SECURITY.md](./SECURITY.md). Please do **not** open a public GitHub issue for security concerns.
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## License
|
|
276
|
+
|
|
277
|
+
[MIT](./LICENSE) © Ali Shuja
|