@chdlabs/my-shield 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.
@@ -0,0 +1,266 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Shield Security Console | @chdlabs/my-shield</title>
7
+ <meta name="description" content="Real-time security telemetry, honeypot monitoring, and 404 intelligence governance">
8
+ <link rel="stylesheet" href="/admin/shield/dashboard.css">
9
+ </head>
10
+ <body class="shield-body">
11
+ <header class="shield-header">
12
+ <div class="header-left">
13
+ <div class="shield-logo">
14
+ <span class="logo-icon">🛡️</span>
15
+ <div class="logo-text">
16
+ <span class="logo-title">CHDLABS SHIELD</span>
17
+ <span class="logo-sub">Zero-Latency Threat Defense</span>
18
+ </div>
19
+ </div>
20
+ <div id="live-indicator" class="status-pill status-live">
21
+ <span class="pulse-dot"></span> LIVE TELEMETRY
22
+ </div>
23
+ </div>
24
+ <div class="header-right">
25
+ <button id="btn-refresh" class="btn btn-secondary" title="Refresh data">
26
+ <span class="btn-icon">↻</span> Refresh
27
+ </button>
28
+ <a href="/" class="btn btn-secondary" id="link-app-home">← Back to App</a>
29
+ </div>
30
+ </header>
31
+
32
+ <div class="shield-layout">
33
+ <!-- Navigation Tabs -->
34
+ <nav class="shield-nav" aria-label="Shield Views">
35
+ <button class="nav-tab active" data-view="overview" id="tab-overview">
36
+ <span class="tab-icon">⚡</span> Threat Overview
37
+ </button>
38
+ <button class="nav-tab" data-view="intelligence" id="tab-intelligence">
39
+ <span class="tab-icon">🎯</span> 404 Intelligence
40
+ </button>
41
+ <button class="nav-tab" data-view="honeypots" id="tab-honeypots">
42
+ <span class="tab-icon">🍯</span> Honeypot Traps
43
+ </button>
44
+ <button class="nav-tab" data-view="health" id="tab-health">
45
+ <span class="tab-icon">🩺</span> System Health
46
+ </button>
47
+ </nav>
48
+
49
+ <main class="shield-main">
50
+ <!-- ── VIEW 1: THREAT OVERVIEW ── -->
51
+ <section id="view-overview" class="view-panel active">
52
+ <div class="metric-grid">
53
+ <div class="metric-card" id="card-blocked-ips">
54
+ <div class="metric-label">Active Blocked Threats</div>
55
+ <div class="metric-value text-danger" id="val-blocked-ips">0</div>
56
+ <div class="metric-hint">In LRU cache (TTL enforced)</div>
57
+ </div>
58
+ <div class="metric-card" id="card-total-events">
59
+ <div class="metric-label">Recorded Security Events</div>
60
+ <div class="metric-value text-white" id="val-total-events">0</div>
61
+ <div class="metric-hint">In ring buffer</div>
62
+ </div>
63
+ <div class="metric-card" id="card-bloom-fill">
64
+ <div class="metric-label">Bloom Filter Saturation</div>
65
+ <div class="metric-value text-emerald" id="val-bloom-fill">0.00%</div>
66
+ <div class="metric-hint" id="val-bloom-fpr">Est. False Positive: 0.00%</div>
67
+ </div>
68
+ <div class="metric-card" id="card-velocity">
69
+ <div class="metric-label">Attack Velocity</div>
70
+ <div class="metric-value text-amber" id="val-attack-velocity">0/min</div>
71
+ <div class="metric-hint">Threat events in last 60s</div>
72
+ </div>
73
+ </div>
74
+
75
+ <div class="split-grid">
76
+ <!-- Active Blocks List -->
77
+ <div class="panel">
78
+ <div class="panel-header">
79
+ <h2 class="panel-title">Active IP Blacklist</h2>
80
+ <div class="panel-actions">
81
+ <button id="btn-open-block-modal" class="btn btn-sm btn-danger">+ Block IP</button>
82
+ </div>
83
+ </div>
84
+ <div class="table-container">
85
+ <table class="data-table" id="table-blocked-ips">
86
+ <thead>
87
+ <tr>
88
+ <th>IP Address</th>
89
+ <th>Reason</th>
90
+ <th>TTL Remaining</th>
91
+ <th>Actions</th>
92
+ </tr>
93
+ </thead>
94
+ <tbody id="tbody-blocked-ips">
95
+ <tr><td colspan="4" class="text-muted">Loading blocked entries...</td></tr>
96
+ </tbody>
97
+ </table>
98
+ </div>
99
+ </div>
100
+
101
+ <!-- Live Event Feed -->
102
+ <div class="panel">
103
+ <div class="panel-header">
104
+ <h2 class="panel-title">Real-Time Event Stream</h2>
105
+ <button id="btn-clear-feed" class="btn btn-sm btn-secondary">Clear</button>
106
+ </div>
107
+ <div class="event-feed-container" id="event-feed" role="log" aria-live="polite">
108
+ <!-- Populated dynamically via textContent & safe elements -->
109
+ </div>
110
+ </div>
111
+ </div>
112
+ </section>
113
+
114
+ <!-- ── VIEW 2: 404 INTELLIGENCE ── -->
115
+ <section id="view-intelligence" class="view-panel">
116
+ <div class="metric-grid">
117
+ <div class="metric-card">
118
+ <div class="metric-label">Total 404 Hits Captured</div>
119
+ <div class="metric-value text-white" id="intel-total-hits">0</div>
120
+ <div class="metric-hint">Across all scan probes</div>
121
+ </div>
122
+ <div class="metric-card">
123
+ <div class="metric-label">Unique Paths Discovered</div>
124
+ <div class="metric-value text-cyan" id="intel-unique-paths">0</div>
125
+ <div class="metric-hint">Tracked with deduplication</div>
126
+ </div>
127
+ <div class="metric-card">
128
+ <div class="metric-label">Persistence Status</div>
129
+ <div class="metric-value text-emerald" id="intel-file-status">Active</div>
130
+ <div class="metric-hint" id="intel-last-saved">Never saved</div>
131
+ </div>
132
+ <div class="metric-card">
133
+ <div class="metric-label">Export Intelligence</div>
134
+ <div class="metric-action-box">
135
+ <a href="/admin/shield/api/intelligence/file" class="btn btn-sm btn-secondary" download>
136
+ ⬇ Download JSON
137
+ </a>
138
+ </div>
139
+ <div class="metric-hint">Single deduplicated file</div>
140
+ </div>
141
+ </div>
142
+
143
+ <div class="split-grid">
144
+ <!-- Top 404 Probed Paths -->
145
+ <div class="panel">
146
+ <div class="panel-header">
147
+ <h2 class="panel-title">Top Targeted 404 Paths</h2>
148
+ <span class="text-muted text-xs">Sorted by frequency</span>
149
+ </div>
150
+ <div class="chart-container" id="intel-paths-chart">
151
+ <!-- Populated with SVG bars -->
152
+ </div>
153
+ </div>
154
+
155
+ <!-- Scanner Fingerprints -->
156
+ <div class="panel">
157
+ <div class="panel-header">
158
+ <h2 class="panel-title">Scanner Fingerprints (User-Agents)</h2>
159
+ </div>
160
+ <div class="table-container">
161
+ <table class="data-table" id="table-fingerprints">
162
+ <thead>
163
+ <tr>
164
+ <th>User Agent</th>
165
+ <th>Probes</th>
166
+ </tr>
167
+ </thead>
168
+ <tbody id="tbody-fingerprints">
169
+ <tr><td colspan="2" class="text-muted">No fingerprints recorded yet</td></tr>
170
+ </tbody>
171
+ </table>
172
+ </div>
173
+ </div>
174
+ </div>
175
+ </section>
176
+
177
+ <!-- ── VIEW 3: HONEYPOT TRAPS ── -->
178
+ <section id="view-honeypots" class="view-panel">
179
+ <div class="panel mb-4">
180
+ <div class="panel-header">
181
+ <h2 class="panel-title">Add Custom Trap Route</h2>
182
+ </div>
183
+ <form id="form-add-trap" class="inline-form">
184
+ <input type="text" id="input-trap-path" class="input-text" placeholder="e.g. /actuator/heapdump or /wp-config.php" required>
185
+ <button type="submit" class="btn btn-primary">+ Add Trap</button>
186
+ </form>
187
+ </div>
188
+
189
+ <div class="panel">
190
+ <div class="panel-header">
191
+ <h2 class="panel-title">Active Honeypot Traps</h2>
192
+ <span class="text-muted text-xs" id="honeypot-count">0 traps active</span>
193
+ </div>
194
+ <div class="table-container">
195
+ <table class="data-table" id="table-traps">
196
+ <thead>
197
+ <thead>
198
+ <tr>
199
+ <th>Trap Route</th>
200
+ <th>Source</th>
201
+ <th>Catches</th>
202
+ <th>Actions</th>
203
+ </tr>
204
+ </thead>
205
+ <tbody id="tbody-traps">
206
+ <tr><td colspan="4" class="text-muted">Loading honeypot traps...</td></tr>
207
+ </tbody>
208
+ </table>
209
+ </div>
210
+ </div>
211
+ </section>
212
+
213
+ <!-- ── VIEW 4: SYSTEM HEALTH ── -->
214
+ <section id="view-health" class="view-panel">
215
+ <div class="split-grid">
216
+ <div class="panel">
217
+ <div class="panel-header">
218
+ <h2 class="panel-title">Memory Allocation</h2>
219
+ </div>
220
+ <div class="health-list" id="health-memory-list">
221
+ <!-- Populated with memory stats -->
222
+ </div>
223
+ </div>
224
+ <div class="panel">
225
+ <div class="panel-header">
226
+ <h2 class="panel-title">Core Subsystem Telemetry</h2>
227
+ </div>
228
+ <div class="health-list" id="health-telemetry-list">
229
+ <!-- Populated with bloom/lru/buffer metrics -->
230
+ </div>
231
+ </div>
232
+ </div>
233
+ </section>
234
+ </main>
235
+ </div>
236
+
237
+ <!-- Notification Toast Container -->
238
+ <div id="toast-container" class="toast-container" aria-live="assertive"></div>
239
+
240
+ <!-- Modal for Manual IP Blocking -->
241
+ <div id="modal-block-ip" class="modal-backdrop" hidden>
242
+ <div class="modal-dialog">
243
+ <div class="modal-header">
244
+ <h3 class="modal-title">Manual IP Blacklist</h3>
245
+ <button type="button" class="btn-close" id="btn-close-modal">✕</button>
246
+ </div>
247
+ <form id="form-block-ip">
248
+ <div class="form-group">
249
+ <label for="input-block-ip">IP Address</label>
250
+ <input type="text" id="input-block-ip" class="input-text" placeholder="192.0.2.1" required>
251
+ </div>
252
+ <div class="form-group">
253
+ <label for="input-block-reason">Block Reason</label>
254
+ <input type="text" id="input-block-reason" class="input-text" placeholder="Manual security ban" value="manual_admin_ban">
255
+ </div>
256
+ <div class="modal-actions">
257
+ <button type="button" class="btn btn-secondary" id="btn-cancel-modal">Cancel</button>
258
+ <button type="submit" class="btn btn-danger">Confirm Blacklist</button>
259
+ </div>
260
+ </form>
261
+ </div>
262
+ </div>
263
+
264
+ <script src="/admin/shield/dashboard.js"></script>
265
+ </body>
266
+ </html>
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Client-Side DOM Tripwire Script
3
+ * Generates an invisible decoy form and link in the DOM.
4
+ * Headless scrapers and vulnerability crawlers interacting with hidden form
5
+ * elements trigger an immediate honeypot tripwire event.
6
+ *
7
+ * @author ChdLabs
8
+ * @license MIT
9
+ */
10
+ (function() {
11
+ 'use strict';
12
+
13
+ function injectTripwire() {
14
+ if (typeof document === 'undefined') return;
15
+
16
+ const trapContainer = document.createElement('div');
17
+ trapContainer.setAttribute('aria-hidden', 'true');
18
+ trapContainer.style.position = 'absolute';
19
+ trapContainer.style.left = '-9999px';
20
+ trapContainer.style.top = '-9999px';
21
+ trapContainer.style.width = '1px';
22
+ trapContainer.style.height = '1px';
23
+ trapContainer.style.overflow = 'hidden';
24
+ trapContainer.style.opacity = '0';
25
+ trapContainer.style.pointerEvents = 'none';
26
+
27
+ // Decoy form
28
+ const form = document.createElement('form');
29
+ form.setAttribute('method', 'POST');
30
+ form.setAttribute('action', '/api/__shield_trap');
31
+
32
+ const inputUser = document.createElement('input');
33
+ inputUser.setAttribute('type', 'text');
34
+ inputUser.setAttribute('name', 'admin_override');
35
+ inputUser.setAttribute('tabindex', '-1');
36
+ inputUser.setAttribute('autocomplete', 'off');
37
+
38
+ const inputPass = document.createElement('input');
39
+ inputPass.setAttribute('type', 'password');
40
+ inputPass.setAttribute('name', 'debug_pass');
41
+ inputPass.setAttribute('tabindex', '-1');
42
+
43
+ form.appendChild(inputUser);
44
+ form.appendChild(inputPass);
45
+ trapContainer.appendChild(form);
46
+
47
+ // Decoy link
48
+ const link = document.createElement('a');
49
+ link.setAttribute('href', '/api/__shield_trap');
50
+ link.setAttribute('rel', 'nofollow');
51
+ link.setAttribute('tabindex', '-1');
52
+ link.textContent = 'Admin Console';
53
+ trapContainer.appendChild(link);
54
+
55
+ document.body.appendChild(trapContainer);
56
+ }
57
+
58
+ if (document.readyState === 'loading') {
59
+ document.addEventListener('DOMContentLoaded', injectTripwire);
60
+ } else {
61
+ injectTripwire();
62
+ }
63
+ })();
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Pure JavaScript In-Memory Bloom Filter
3
+ * Uses 32-bit MurmurHash3 with Kirsch-Mitzenmacher double hashing optimization:
4
+ * g_i(x) = (h1(x) + i * h2(x)) mod m
5
+ *
6
+ * @author ChdLabs
7
+ * @license MIT
8
+ */
9
+
10
+ /**
11
+ * 32-bit MurmurHash3 implementation in pure JavaScript
12
+ * @param {string} str Input string
13
+ * @param {number} seed 32-bit seed
14
+ * @returns {number} 32-bit unsigned integer hash
15
+ */
16
+ export function murmur3_32(str, seed = 0) {
17
+ let h = seed >>> 0;
18
+ const l = str.length;
19
+ let i = 0;
20
+
21
+ // Body in 4-byte chunks
22
+ while (i + 4 <= l) {
23
+ let k = (str.charCodeAt(i) & 0xff) |
24
+ ((str.charCodeAt(i + 1) & 0xff) << 8) |
25
+ ((str.charCodeAt(i + 2) & 0xff) << 16) |
26
+ ((str.charCodeAt(i + 3) & 0xff) << 24);
27
+
28
+ k = Math.imul(k, 0xcc9e2d51);
29
+ k = (k << 15) | (k >>> 17);
30
+ k = Math.imul(k, 0x1b873593);
31
+
32
+ h ^= k;
33
+ h = (h << 13) | (h >>> 19);
34
+ h = Math.imul(h, 5) + 0xe6546b64;
35
+ i += 4;
36
+ }
37
+
38
+ // Remainder
39
+ let k = 0;
40
+ const remaining = l - i;
41
+ if (remaining === 3) k ^= (str.charCodeAt(i + 2) & 0xff) << 16;
42
+ if (remaining >= 2) k ^= (str.charCodeAt(i + 1) & 0xff) << 8;
43
+ if (remaining >= 1) {
44
+ k ^= (str.charCodeAt(i) & 0xff);
45
+ k = Math.imul(k, 0xcc9e2d51);
46
+ k = (k << 15) | (k >>> 17);
47
+ k = Math.imul(k, 0x1b873593);
48
+ h ^= k;
49
+ }
50
+
51
+ // Finalization
52
+ h ^= l;
53
+ h ^= h >>> 16;
54
+ h = Math.imul(h, 0x85ebca6b);
55
+ h ^= h >>> 13;
56
+ h = Math.imul(h, 0xc2b2ae35);
57
+ h ^= h >>> 16;
58
+
59
+ return h >>> 0;
60
+ }
61
+
62
+ export class BloomFilter {
63
+ /**
64
+ * @param {Object} options
65
+ * @param {number} [options.expectedItems=100000] Estimated items to store
66
+ * @param {number} [options.falsePositiveRate=0.01] Desired false positive probability (e.g. 0.01 = 1%)
67
+ * @param {number} [options.sizeBytes] Explicit bit array size in bytes (overrides calculations)
68
+ */
69
+ constructor({ expectedItems = 100_000, falsePositiveRate = 0.01, sizeBytes = null } = {}) {
70
+ const n = Math.max(1, expectedItems);
71
+ const p = Math.max(0.0001, Math.min(0.5, falsePositiveRate));
72
+
73
+ if (sizeBytes && sizeBytes > 0) {
74
+ this.sizeBits = sizeBytes * 8;
75
+ this.numHashes = Math.max(1, Math.round((this.sizeBits / n) * Math.LN2));
76
+ } else {
77
+ // Optimal size in bits: m = - (n * ln(p)) / (ln(2)^2)
78
+ const m = Math.ceil(- (n * Math.log(p)) / (Math.LN2 * Math.LN2));
79
+ this.sizeBits = Math.max(64, m);
80
+ // Optimal hash functions: k = (m / n) * ln(2)
81
+ this.numHashes = Math.max(1, Math.round((this.sizeBits / n) * Math.LN2));
82
+ }
83
+
84
+ const bufferLength = Math.ceil(this.sizeBits / 8);
85
+ this.bitArray = new Uint8Array(bufferLength);
86
+ this.expectedItems = n;
87
+ this.targetFPRate = p;
88
+ this.addedCount = 0;
89
+ }
90
+
91
+ /**
92
+ * Compute k hash bit positions for a given key using double hashing
93
+ * @param {string} key
94
+ * @returns {number[]} Array of bit indices
95
+ */
96
+ _getIndices(key) {
97
+ const str = String(key);
98
+ const h1 = murmur3_32(str, 0);
99
+ const h2 = murmur3_32(str, 0x5bd1e995);
100
+ const indices = new Array(this.numHashes);
101
+
102
+ for (let i = 0; i < this.numHashes; i++) {
103
+ // Double hashing: (h1 + i * h2) % sizeBits
104
+ // In JS bitwise arithmetic can overflow signed 32-bit, so use modulo with double precision
105
+ const combined = (h1 + (i * h2)) >>> 0;
106
+ indices[i] = combined % this.sizeBits;
107
+ }
108
+
109
+ return indices;
110
+ }
111
+
112
+ /**
113
+ * Add a key to the Bloom filter
114
+ * @param {string} key
115
+ */
116
+ add(key) {
117
+ const indices = this._getIndices(key);
118
+ for (let j = 0; j < indices.length; j++) {
119
+ const bitIndex = indices[j];
120
+ const byteIndex = bitIndex >> 3;
121
+ const bitOffset = bitIndex & 7;
122
+ this.bitArray[byteIndex] |= (1 << bitOffset);
123
+ }
124
+ this.addedCount++;
125
+ }
126
+
127
+ /**
128
+ * Test whether a key is likely in the Bloom filter
129
+ * @param {string} key
130
+ * @returns {boolean} false = definitely clean, true = possibly in filter
131
+ */
132
+ has(key) {
133
+ const indices = this._getIndices(key);
134
+ for (let j = 0; j < indices.length; j++) {
135
+ const bitIndex = indices[j];
136
+ const byteIndex = bitIndex >> 3;
137
+ const bitOffset = bitIndex & 7;
138
+ if ((this.bitArray[byteIndex] & (1 << bitOffset)) === 0) {
139
+ return false;
140
+ }
141
+ }
142
+ return true;
143
+ }
144
+
145
+ /**
146
+ * Reset the Bloom filter
147
+ */
148
+ clear() {
149
+ this.bitArray.fill(0);
150
+ this.addedCount = 0;
151
+ }
152
+
153
+ /**
154
+ * Count currently set bits (Hamming weight)
155
+ * @returns {number}
156
+ */
157
+ countSetBits() {
158
+ let count = 0;
159
+ for (let i = 0; i < this.bitArray.length; i++) {
160
+ let byte = this.bitArray[i];
161
+ while (byte > 0) {
162
+ count += byte & 1;
163
+ byte >>= 1;
164
+ }
165
+ }
166
+ return count;
167
+ }
168
+
169
+ /**
170
+ * Diagnostic statistics
171
+ * @returns {Object}
172
+ */
173
+ getStats() {
174
+ const setBits = this.countSetBits();
175
+ const fillRatio = setBits / this.sizeBits;
176
+ // Estimated FP rate based on current fill ratio: (1 - e^(-k*n/m))^k ≈ (fillRatio)^k
177
+ const estimatedFPRate = Math.pow(fillRatio, this.numHashes);
178
+
179
+ return {
180
+ sizeBits: this.sizeBits,
181
+ sizeBytes: this.bitArray.length,
182
+ numHashes: this.numHashes,
183
+ expectedItems: this.expectedItems,
184
+ addedCount: this.addedCount,
185
+ setBits,
186
+ fillPercentage: Number((fillRatio * 100).toFixed(2)),
187
+ estimatedFPRate: Number(estimatedFPRate.toFixed(4)),
188
+ targetFPRate: this.targetFPRate
189
+ };
190
+ }
191
+ }