@chdlabs/my-shield 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -10,13 +10,14 @@
10
10
 
11
11
  ## Highlights
12
12
 
13
- - ⚡ **Sub-0.1ms Clean Pass**: Legitimate requests pass through the L1 Bloom filter in `< 0.1ms`.
14
- - ðŸŽŊ **Zero False-Positive Blocks**: Two-tiered L1 caching (Bloom filter + LRU confirmed threat cache) guarantees statistical Bloom false positives are never blocked.
15
- - ðŸŊ **Silent Honeypot Decoys**: Static trap routes (`/.env`, `/wp-login.php`, `/.git/config`, etc.) return a silent `200 OK` decoy response after a brief delay and instantly blacklist scanners.
16
- - 🔍 **404 Anomaly & Intelligence**: Captures every 404 URL probe, bans scanners probing $\ge 5$ unique paths in 60s, and auto-promotes frequent attack targets to active honeypots.
17
- - ðŸ’ū **Single-File Deduplicated Persistence**: Preserves learned probe patterns and promoted honeypots across process restarts in an atomic JSON file (`404_intelligence.json`).
18
- - 📊 **Self-Contained Security Dashboard**: Built-in dark-themed telemetry console with real-time Server-Sent Events (SSE), charts, and active block management.
19
- - ðŸŠķ **Zero Production Dependencies**: Pure JavaScript built exclusively with Node.js built-ins (`crypto`, `fs`, `path`, `events`).
13
+ - **Sub-0.1ms Clean Pass**: Legitimate requests pass through the L1 Bloom filter in `< 0.1ms`.
14
+ - **Zero False-Positive Blocks**: Two-tiered L1 caching (Bloom filter + LRU confirmed threat cache) guarantees statistical Bloom false positives are never blocked.
15
+ - **Silent Honeypot Decoys**: Static trap routes (`/.env`, `/wp-login.php`, `/.git/config`, etc.) return a silent `200 OK` decoy response after a brief delay and instantly blacklist scanners.
16
+ - **404 Anomaly & Intelligence**: Captures every 404 URL probe, bans scanners probing $\ge 5$ unique paths in 60s, and auto-promotes frequent attack targets to active honeypots.
17
+ - **Single-File Deduplicated Persistence**: Preserves learned probe patterns and promoted honeypots across process restarts in an atomic JSON file (`404_intelligence.json`).
18
+ - **Self-Contained Security Dashboard**: Built-in dark-themed telemetry console with real-time Server-Sent Events (SSE), charts, and active block management.
19
+ - **Safe By Default**: Admin dashboard is disabled by default (`admin.enabled: false`) with dedicated RBAC integration to prevent unauthorized telemetry access.
20
+ - **Zero Production Dependencies**: Pure JavaScript built exclusively with Node.js built-ins (`crypto`, `fs`, `path`, `events`).
20
21
 
21
22
  ---
22
23
 
@@ -70,6 +71,10 @@ import { createShield } from '@chdlabs/my-shield';
70
71
  const app = express();
71
72
 
72
73
  const shield = createShield({
74
+ whitelist: ['127.0.0.1', '::1'],
75
+ admin: {
76
+ enabled: true // Set to true to activate dashboard & telemetry (Default: false)
77
+ },
73
78
  bloom: {
74
79
  expectedItems: 100_000,
75
80
  falsePositiveRate: 0.01
@@ -113,8 +118,10 @@ app.get('/api/data', (req, res) => res.json({ status: 'ok' }));
113
118
  // 4. Mount 404 intelligence collector (AFTER application routes)
114
119
  app.use(shield.notFoundCollector());
115
120
 
116
- // 5. Mount admin dashboard (protect with your auth middleware)
117
- app.use('/admin/shield', authenticateAdmin, shield.adminRouter());
121
+ // 5. Mount admin dashboard (protect API endpoints with your app's auth & RBAC middleware)
122
+ app.use('/admin/shield', shield.adminRouter({
123
+ authMiddleware: [authenticateUser, requireAdminRole]
124
+ }));
118
125
 
119
126
  // Graceful shutdown: flushes 404 intelligence to disk
120
127
  process.on('SIGTERM', async () => {
@@ -125,11 +132,83 @@ process.on('SIGTERM', async () => {
125
132
 
126
133
  ---
127
134
 
135
+ ## 🔐 Admin Dashboard & Role-Based Access Control (RBAC)
136
+
137
+ The built-in admin dashboard provides live telemetry, attack velocity, probe intelligence, active threat management, and real-time Server-Sent Events (SSE).
138
+
139
+ ### 1. Enabling the Dashboard
140
+ By default, the admin dashboard is **disabled** (`admin.enabled: false`). If mounted while disabled, `/admin/shield` safely returns `404 Not Found`. To enable it, explicitly configure:
141
+ ```javascript
142
+ const shield = createShield({
143
+ admin: {
144
+ enabled: true // Required to activate dashboard routes
145
+ }
146
+ });
147
+ ```
148
+
149
+ ### 2. Restricting Access to Administrators Only
150
+ Do **not** wrap `shield.adminRouter()` at the top-level route (e.g., `app.use('/admin/shield', requireAdmin, ...)`). Doing so causes browser page visits to fail with an immediate `401 Unauthorized` JSON response because web browsers do not send `Authorization` headers on initial page navigations.
151
+
152
+ Instead, pass your authentication and authorization middleware into `shield.adminRouter({ authMiddleware })`:
153
+ ```javascript
154
+ app.use('/admin/shield', shield.adminRouter({
155
+ authMiddleware: [authenticateUser, requireAdminRole]
156
+ }));
157
+ ```
158
+
159
+ #### Why this pattern is secure:
160
+ 1. **Public Static Delivery**: The browser can freely download the dashboard SPA shell (`index.html`, `dashboard.css`, `dashboard.js`).
161
+ 2. **Protected API & Telemetry**: All data-bearing endpoints (`/admin/shield/api/stats`, `/admin/shield/api/blocked`, `/admin/shield/api/stream`, etc.) are strictly guarded by your `authMiddleware`.
162
+ 3. **Graceful UI Feedback**: If a non-admin or unauthenticated visitor accesses the page, the dashboard renders an informative warning banner rather than a raw, broken JSON error.
163
+
164
+ ### 3. Complete Express RBAC Example
165
+
166
+ ```javascript
167
+ // Step A: Authentication middleware (verifies JWT / Bearer token)
168
+ function authenticateUser(req, res, next) {
169
+ const authHeader = req.headers.authorization || (req.query?.token ? `Bearer ${req.query.token}` : null);
170
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
171
+ return res.status(401).json({ error: 'Authentication required. Missing Authorization header.' });
172
+ }
173
+
174
+ const token = authHeader.split(' ')[1];
175
+ try {
176
+ const claims = verifyJwt(token); // Verify your JWT / PQC token
177
+ req.user = claims;
178
+ next();
179
+ } catch (err) {
180
+ return res.status(401).json({ error: 'Invalid or expired session token.' });
181
+ }
182
+ }
183
+
184
+ // Step B: Role-Based Access Control (RBAC) middleware
185
+ function requireAdminRole(req, res, next) {
186
+ // Check user role from token claims or database lookup
187
+ if (!req.user || (req.user.role !== 'admin' && req.user.role !== 'super_admin')) {
188
+ return res.status(403).json({ error: 'Access denied: Administrator privileges required.' });
189
+ }
190
+ next();
191
+ }
192
+
193
+ // Step C: Mount protected admin router
194
+ app.use('/admin/shield', shield.adminRouter({
195
+ authMiddleware: [authenticateUser, requireAdminRole]
196
+ }));
197
+ ```
198
+
199
+ ### 4. How the Dashboard Authenticates
200
+ The dashboard client automatically looks for the session token in the following order:
201
+ 1. **URL Query Parameter**: Navigating to `http://your-server.com/admin/shield?token=<jwt>` extracts the token into `sessionStorage` and automatically scrubs the parameter from the browser's address bar to prevent token leakage.
202
+ 2. **Storage Fallback**: Checks `sessionStorage` and `localStorage` under standard keys: `shield_session_token`, `pqc_session_token`, `token`, `access_token`, `auth_token`, or `jwt`.
203
+
204
+ ---
205
+
128
206
  ## Configuration Reference
129
207
 
130
208
  | Option | Type | Default | Description |
131
209
  |---|---|---|---|
132
210
  | `whitelist` | `string[]` | `[]` | List of IP addresses that are never blocked |
211
+ | `admin.enabled` | `boolean` | `false` | Explicit switch to enable the admin dashboard & telemetry |
133
212
  | `bloom.expectedItems` | `number` | `100000` | Estimated distinct IPs to hold in filter |
134
213
  | `bloom.falsePositiveRate` | `number` | `0.01` | Target false positive probability (1%) |
135
214
  | `bloom.sizeBytes` | `number` | `null` | Explicit bit array size in bytes |
@@ -175,23 +254,6 @@ app.use(shield.keyLeakDetector());
175
254
 
176
255
  ---
177
256
 
178
- ## Publishing to npmjs
179
-
180
- To publish `@chdlabs/my-shield` to npm:
181
-
182
- 1. Log in to your npm account (if not already logged in):
183
- ```bash
184
- npm login
185
- ```
186
- 2. Navigate to the `sdk` directory and publish:
187
- ```bash
188
- cd sdk
189
- npm publish
190
- ```
191
- *(Note: Since `@chdlabs` is a scoped package, `"publishConfig": { "access": "public" }` is pre-configured in `package.json`).*
192
-
193
- ---
194
-
195
257
  ## License
196
258
 
197
259
  MIT ÂĐ CHDLabs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chdlabs/my-shield",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Zero-latency security shield middleware with Bloom filter, honeypots, and 404 intelligence",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -18,8 +18,9 @@
18
18
  "peerDependencies": {
19
19
  "express": ">=4.18.0 || >=5.0.0"
20
20
  },
21
- "dependencies": {},
22
- "devDependencies": {},
21
+ "devDependencies": {
22
+ "express": "^5.2.1"
23
+ },
23
24
  "engines": {
24
25
  "node": ">=20.0.0"
25
26
  },
@@ -42,4 +43,4 @@
42
43
  "scripts": {
43
44
  "test": "node test/integration.test.js"
44
45
  }
45
- }
46
+ }
@@ -15,6 +15,19 @@ const __filename = fileURLToPath(import.meta.url);
15
15
  const __dirname = path.dirname(__filename);
16
16
 
17
17
  export function createAdminRouter(shield, options = {}) {
18
+ const isEnabled = options.enabled !== undefined ? Boolean(options.enabled) : Boolean(shield?.adminEnabled);
19
+ if (!isEnabled) {
20
+ shield?.logger?.warn?.('[SHIELD] Admin dashboard router is mounted, but admin is disabled in configuration. Enable via createShield({ admin: { enabled: true } }) or shield.adminRouter({ enabled: true })');
21
+ const disabledRouter = express.Router();
22
+ disabledRouter.use((req, res) => {
23
+ res.status(404).json({
24
+ error: 'Shield admin dashboard is disabled in configuration.',
25
+ hint: 'Enable via createShield({ admin: { enabled: true } }) or shield.adminRouter({ enabled: true })'
26
+ });
27
+ });
28
+ return disabledRouter;
29
+ }
30
+
18
31
  const router = express.Router();
19
32
  const dashboardDir = path.resolve(__dirname, 'dashboard');
20
33
  const authMiddleware = Array.isArray(options.authMiddleware) ? options.authMiddleware : (options.authMiddleware ? [options.authMiddleware] : []);
@@ -532,3 +532,23 @@ body.shield-body {
532
532
  from { transform: translateX(20px); opacity: 0; }
533
533
  to { transform: translateX(0); opacity: 1; }
534
534
  }
535
+
536
+ /* Auth Challenge Banners */
537
+ .auth-banner {
538
+ padding: 0.85rem 1.25rem;
539
+ margin-bottom: 1.5rem;
540
+ border-radius: var(--radius-md);
541
+ font-size: 0.875rem;
542
+ line-height: 1.4;
543
+ font-weight: 500;
544
+ }
545
+ .auth-banner-unauth {
546
+ background-color: rgba(234, 179, 8, 0.15);
547
+ border: 1px solid rgba(234, 179, 8, 0.4);
548
+ color: #fde047;
549
+ }
550
+ .auth-banner-forbidden {
551
+ background-color: rgba(239, 68, 68, 0.15);
552
+ border: 1px solid rgba(239, 68, 68, 0.4);
553
+ color: #fca5a5;
554
+ }
@@ -49,8 +49,57 @@
49
49
  }, 4000);
50
50
  }
51
51
 
52
+ function setAuthBanner(type, message) {
53
+ let banner = document.getElementById('shield-auth-banner');
54
+ if (!banner) {
55
+ banner = document.createElement('div');
56
+ banner.id = 'shield-auth-banner';
57
+ const main = document.querySelector('.main-content');
58
+ if (main) {
59
+ main.insertBefore(banner, main.firstChild);
60
+ }
61
+ }
62
+ if (banner) {
63
+ banner.className = `auth-banner ${type === '403' ? 'auth-banner-forbidden' : 'auth-banner-unauth'}`;
64
+ banner.textContent = message;
65
+ banner.style.display = 'block';
66
+ }
67
+ }
68
+
69
+ function clearAuthBanner() {
70
+ const banner = document.getElementById('shield-auth-banner');
71
+ if (banner) {
72
+ banner.style.display = 'none';
73
+ }
74
+ }
75
+
52
76
  function getStoredToken() {
53
- return localStorage.getItem('pqc_session_token');
77
+ if (typeof window === 'undefined') return null;
78
+
79
+ // 1. Check URL query params for ?token=... or ?auth=...
80
+ try {
81
+ const params = new URLSearchParams(window.location.search);
82
+ const queryToken = params.get('token') || params.get('auth');
83
+ if (queryToken) {
84
+ sessionStorage.setItem('shield_session_token', queryToken);
85
+ params.delete('token');
86
+ params.delete('auth');
87
+ const cleanQuery = params.toString() ? `?${params.toString()}` : '';
88
+ window.history.replaceState({}, '', window.location.pathname + cleanQuery + window.location.hash);
89
+ return queryToken;
90
+ }
91
+ } catch {
92
+ // Ignore URL parsing errors
93
+ }
94
+
95
+ // 2. Check session and local storage with multi-key fallback
96
+ return sessionStorage.getItem('shield_session_token')
97
+ || localStorage.getItem('shield_session_token')
98
+ || localStorage.getItem('pqc_session_token')
99
+ || localStorage.getItem('token')
100
+ || localStorage.getItem('access_token')
101
+ || localStorage.getItem('auth_token')
102
+ || localStorage.getItem('jwt');
54
103
  }
55
104
 
56
105
  async function apiFetch(url, options = {}) {
@@ -61,9 +110,13 @@
61
110
  }
62
111
  const res = await fetch(url, { ...options, headers });
63
112
  if (res.status === 401) {
113
+ setAuthBanner('401', '⚠ïļ Authentication required: Please log in to the application with an administrator account to access telemetry and management APIs.');
64
114
  showToast('Authentication required. Please login with an admin account.', 'error');
65
115
  } else if (res.status === 403) {
116
+ setAuthBanner('403', 'ðŸšŦ Access denied: Administrator privileges required for this account.');
66
117
  showToast('Access denied: Administrator privileges required.', 'error');
118
+ } else if (res.ok) {
119
+ clearAuthBanner();
67
120
  }
68
121
  return res;
69
122
  }
package/src/index.d.ts CHANGED
@@ -43,12 +43,17 @@ export interface IntelligenceOptions {
43
43
  autoPromote?: AutoPromoteOptions;
44
44
  }
45
45
 
46
+ export interface AdminOptions {
47
+ enabled?: boolean;
48
+ }
49
+
46
50
  export interface ShieldOptions {
47
51
  whitelist?: string[];
48
52
  bloom?: BloomOptions;
49
53
  lru?: LRUOptions;
50
54
  honeypot?: HoneypotOptions;
51
55
  intelligence?: IntelligenceOptions;
56
+ admin?: AdminOptions;
52
57
  blockStatusCode?: number;
53
58
  logger?: {
54
59
  info?: (...args: any[]) => void;
@@ -64,6 +69,7 @@ export interface CostThrottleOptions {
64
69
  }
65
70
 
66
71
  export interface AdminRouterOptions {
72
+ enabled?: boolean;
67
73
  authMiddleware?: RequestHandler | RequestHandler[];
68
74
  }
69
75
 
package/src/index.js CHANGED
@@ -121,6 +121,10 @@ export class Shield extends EventEmitter {
121
121
  shield: this,
122
122
  logger: this.logger
123
123
  });
124
+
125
+ // 8. Admin Dashboard Configuration (Default: false)
126
+ const adminOpts = options.admin || {};
127
+ this.adminEnabled = Boolean(adminOpts.enabled);
124
128
  }
125
129
 
126
130
  /**