secure-ui-components 0.1.0-beta.1 โ†’ 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.
Files changed (43) hide show
  1. package/README.md +772 -163
  2. package/dist/components/secure-card/secure-card.css +427 -0
  3. package/dist/components/secure-card/secure-card.d.ts +73 -0
  4. package/dist/components/secure-card/secure-card.d.ts.map +1 -0
  5. package/dist/components/secure-card/secure-card.js +787 -0
  6. package/dist/components/secure-card/secure-card.js.map +1 -0
  7. package/dist/components/secure-datetime/secure-datetime.d.ts.map +1 -1
  8. package/dist/components/secure-datetime/secure-datetime.js +6 -19
  9. package/dist/components/secure-datetime/secure-datetime.js.map +1 -1
  10. package/dist/components/secure-file-upload/secure-file-upload.d.ts.map +1 -1
  11. package/dist/components/secure-file-upload/secure-file-upload.js +0 -16
  12. package/dist/components/secure-file-upload/secure-file-upload.js.map +1 -1
  13. package/dist/components/secure-form/secure-form.d.ts.map +1 -1
  14. package/dist/components/secure-form/secure-form.js +126 -6
  15. package/dist/components/secure-form/secure-form.js.map +1 -1
  16. package/dist/components/secure-input/secure-input.d.ts.map +1 -1
  17. package/dist/components/secure-input/secure-input.js +6 -21
  18. package/dist/components/secure-input/secure-input.js.map +1 -1
  19. package/dist/components/secure-select/secure-select.d.ts.map +1 -1
  20. package/dist/components/secure-select/secure-select.js +6 -19
  21. package/dist/components/secure-select/secure-select.js.map +1 -1
  22. package/dist/components/secure-telemetry-provider/secure-telemetry-provider.d.ts +63 -0
  23. package/dist/components/secure-telemetry-provider/secure-telemetry-provider.d.ts.map +1 -0
  24. package/dist/components/secure-telemetry-provider/secure-telemetry-provider.js +198 -0
  25. package/dist/components/secure-telemetry-provider/secure-telemetry-provider.js.map +1 -0
  26. package/dist/components/secure-textarea/secure-textarea.d.ts.map +1 -1
  27. package/dist/components/secure-textarea/secure-textarea.js +6 -19
  28. package/dist/components/secure-textarea/secure-textarea.js.map +1 -1
  29. package/dist/core/base-component.d.ts +23 -1
  30. package/dist/core/base-component.d.ts.map +1 -1
  31. package/dist/core/base-component.js +111 -0
  32. package/dist/core/base-component.js.map +1 -1
  33. package/dist/core/security-config.d.ts.map +1 -1
  34. package/dist/core/security-config.js +5 -21
  35. package/dist/core/security-config.js.map +1 -1
  36. package/dist/core/types.d.ts +129 -8
  37. package/dist/core/types.d.ts.map +1 -1
  38. package/dist/index.d.ts +3 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +2 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/package.json +4 -2
  43. package/package.json +13 -1
package/README.md CHANGED
@@ -1,49 +1,138 @@
1
1
  # secure-ui-components
2
2
 
3
- Security-first web component library with zero dependencies.
3
+ Security-first Web Component library with built-in behavioral telemetry. Zero dependencies.
4
4
 
5
5
  **[Live Demo](https://barryprender.github.io/Secure-UI/)** โ€” Try all components in your browser.
6
6
 
7
- ## ๐Ÿ”’ Features
7
+ ## Features
8
8
 
9
- - **8 Secure Components**: Input, Textarea, Select, Form, File Upload, DateTime, Table, Submit Button
10
- - **4-Tier Security System**: PUBLIC, AUTHENTICATED, SENSITIVE, CRITICAL
11
- - **Progressive Enhancement**: Works without JavaScript
12
- - **Zero Dependencies**: Pure TypeScript, no runtime dependencies
13
- - **Fully Customizable**: CSS Design Tokens + CSS Parts API
14
- - **SSR Friendly**: Components render meaningful markup without JavaScript
15
- - **Comprehensive Testing**: Unit tests and security tests included
9
+ - **10 Secure Components** โ€” Input, Textarea, Select, Form, File Upload, DateTime, Table, Submit Button, Card, Telemetry Provider
10
+ - **4-Tier Security System** โ€” `public`, `authenticated`, `sensitive`, `critical`
11
+ - **Behavioral Telemetry** โ€” Every field collects typing patterns, paste detection, dwell time, and correction signals automatically
12
+ - **Risk Scoring** โ€” `<secure-form>` aggregates field signals into a session-level risk score at submission
13
+ - **Signed Envelopes** โ€” `<secure-telemetry-provider>` detects automation/headless browsers and signs every submission with HMAC-SHA-256
14
+ - **Zero Dependencies** โ€” Pure TypeScript, no runtime dependencies
15
+ - **Progressive Enhancement** โ€” All components render meaningful markup and work without JavaScript
16
+ - **CSP-Safe** โ€” Styles loaded via `<link>` from `'self'`; no `unsafe-inline` required
17
+ - **SSR Friendly** โ€” Adopts server-rendered markup on upgrade; no document access in constructors
18
+ - **Fully Customisable** โ€” CSS Design Tokens + `::part()` API
19
+ - **Comprehensive Testing** โ€” 869 tests, 80%+ branch coverage
16
20
 
17
- ## ๐Ÿ“ฆ Installation
21
+ ---
22
+
23
+ ## Philosophy: Security Telemetry as a First-Class Primitive
24
+
25
+ Traditional form security stops at validation and CSRF protection. Secure-UI goes further โ€” every form submission carries a behavioral fingerprint that travels alongside the user's data, giving the server the context it needs to distinguish real users from bots and credential stuffers in a single atomic request.
26
+
27
+ ```
28
+ Field interaction โ†’ Behavioral signals โ†’ Risk score โ†’ Signed envelope
29
+ (SecureBaseComponent) (SecureForm) (SecureForm) (SecureTelemetryProvider)
30
+ ```
31
+
32
+ **Layer 1 โ€” Field-level signals** (`SecureBaseComponent`)
33
+ Every secure field silently records: dwell time from focus to first keystroke, typing velocity, correction count (backspace/delete), paste detection, autofill detection, focus count, and blur-without-change patterns.
34
+
35
+ **Layer 2 โ€” Session aggregation** (`<secure-form>`)
36
+ At submission, the form queries `getFieldTelemetry()` from every child field, produces per-field snapshots, and computes a composite risk score from 0โ€“100. The telemetry payload travels alongside form data in a single `fetch` request as `_telemetry`.
37
+
38
+ **Layer 3 โ€” Environmental signals** (`<secure-telemetry-provider>`)
39
+ An optional overlay that wraps `<secure-form>`. Monitors for WebDriver/headless flags, DOM script injection (via `MutationObserver`), devtools, suspicious screen dimensions, and pointer/keyboard activity. Signs the final envelope with HMAC-SHA-256 so the server can detect replay attacks.
40
+
41
+ **What the server receives (enhanced submission):**
42
+
43
+ ```json
44
+ {
45
+ "email": "user@example.com",
46
+ "password": "...",
47
+ "_telemetry": {
48
+ "sessionDuration": 14320,
49
+ "fieldCount": 2,
50
+ "riskScore": 5,
51
+ "riskSignals": [],
52
+ "submittedAt": "2026-03-12T18:30:00.000Z",
53
+ "fields": [
54
+ {
55
+ "fieldName": "email",
56
+ "fieldType": "secure-input",
57
+ "dwell": 420,
58
+ "completionTime": 3100,
59
+ "velocity": 4.2,
60
+ "corrections": 1,
61
+ "pasteDetected": false,
62
+ "autofillDetected": false,
63
+ "focusCount": 1,
64
+ "blurWithoutChange": 0
65
+ }
66
+ ],
67
+ "_env": {
68
+ "nonce": "a3f9...",
69
+ "issuedAt": "2026-03-12T18:30:00.000Z",
70
+ "environment": {
71
+ "webdriverDetected": false,
72
+ "headlessDetected": false,
73
+ "mouseMovementDetected": true,
74
+ "pointerType": "mouse"
75
+ },
76
+ "signature": "7d3a..."
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ **Risk signals**
83
+
84
+ | Signal | Condition | Score |
85
+ |--------|-----------|-------|
86
+ | `session_too_fast` | Submitted in under 3 s | +30 |
87
+ | `session_fast` | Submitted in under 8 s | +10 |
88
+ | `all_fields_pasted` | All fields pasted, no keystrokes | +25 |
89
+ | `field_filled_without_focus` | Any field has `focusCount = 0` | +15 |
90
+ | `high_velocity_typing` | Any field velocity > 15 ks/s | +15 |
91
+ | `form_probing` | Field focused/blurred > 1ร— with no input | +10 |
92
+ | `high_correction_count` | Any field with > 5 corrections | +5 |
93
+ | `autofill_detected` | All fields autofilled (trust signal) | โˆ’10 |
94
+
95
+ ---
96
+
97
+ ## Installation
18
98
 
19
99
  ```bash
20
100
  npm install secure-ui-components
21
101
  ```
22
102
 
23
- ## ๐Ÿš€ Quick Start
103
+ ---
104
+
105
+ ## Quick Start
24
106
 
25
- ### Bundler (Vite, Webpack, Rollup, etc.)
107
+ ### With telemetry (recommended)
26
108
 
27
- Import the components you need โ€” each import auto-registers its custom element:
109
+ ```html
110
+ <secure-telemetry-provider signing-key="your-per-session-secret">
111
+ <secure-form action="/api/login" method="POST" csrf-token="..." enhance>
112
+ <secure-input label="Email" name="email" type="email" required security-tier="authenticated"></secure-input>
113
+ <secure-input label="Password" name="password" type="password" required security-tier="critical"></secure-input>
114
+ <secure-submit-button label="Sign in" loading-label="Signing inโ€ฆ"></secure-submit-button>
115
+ </secure-form>
116
+ </secure-telemetry-provider>
117
+ ```
28
118
 
29
119
  ```js
30
- import 'secure-ui-components/secure-input';
31
- import 'secure-ui-components/secure-form';
120
+ document.querySelector('secure-form').addEventListener('secure-form-submit', (e) => {
121
+ const { formData, telemetry } = e.detail;
122
+ console.log('Risk score:', telemetry.riskScore);
123
+ console.log('Risk signals:', telemetry.riskSignals);
124
+ });
32
125
  ```
33
126
 
34
- Then use them in your HTML:
127
+ ### Bundler (Vite, Webpack, Rollup)
35
128
 
36
- ```html
37
- <secure-input
38
- label="Email Address"
39
- name="email"
40
- type="email"
41
- required
42
- security-tier="authenticated"
43
- ></secure-input>
129
+ ```js
130
+ import 'secure-ui-components/secure-input';
131
+ import 'secure-ui-components/secure-form';
132
+ import 'secure-ui-components/secure-telemetry-provider';
44
133
  ```
45
134
 
46
- ### CDN / Vanilla HTML (no bundler)
135
+ ### CDN / Vanilla HTML
47
136
 
48
137
  ```html
49
138
  <!DOCTYPE html>
@@ -52,13 +141,7 @@ Then use them in your HTML:
52
141
  <link rel="stylesheet" href="https://unpkg.com/secure-ui-components/dist/styles/tokens.css">
53
142
  </head>
54
143
  <body>
55
- <secure-input
56
- label="Email Address"
57
- name="email"
58
- type="email"
59
- required
60
- security-tier="authenticated"
61
- ></secure-input>
144
+ <secure-input label="Email" name="email" type="email" required></secure-input>
62
145
 
63
146
  <script type="module">
64
147
  import 'https://unpkg.com/secure-ui-components/dist/index.js';
@@ -67,10 +150,77 @@ Then use them in your HTML:
67
150
  </html>
68
151
  ```
69
152
 
70
- ## ๐Ÿงฉ Available Components
153
+ ---
154
+
155
+ ## Security Tiers
71
156
 
72
- ### SecureInput
73
- Text input with security features and validation.
157
+ All components accept a `security-tier` attribute. The default is `critical`.
158
+
159
+ | Tier | Level | Masking | Autocomplete | Rate Limit | Audit |
160
+ |------|-------|---------|--------------|------------|-------|
161
+ | `public` | 1 | off | on | off | minimal |
162
+ | `authenticated` | 2 | off | on | off | changes + submission |
163
+ | `sensitive` | 3 | partial (last 4 chars) | off | 10/min | full |
164
+ | `critical` | 4 | full | off | 5/min | full |
165
+
166
+ ```html
167
+ <secure-input security-tier="public" label="Username" name="username"></secure-input>
168
+ <secure-input security-tier="sensitive" label="Card Number" name="card"></secure-input>
169
+ <secure-input security-tier="critical" type="password" label="Password" name="password"></secure-input>
170
+ ```
171
+
172
+ The `security-tier` attribute is **immutable after connection** โ€” changes after `connectedCallback` are silently ignored (fail-secure).
173
+
174
+ ---
175
+
176
+ ## Components
177
+
178
+ ### `<secure-input>`
179
+
180
+ Text input with XSS prevention, masking, password strength validation, rate limiting, and automatic telemetry collection.
181
+
182
+ **Attributes**
183
+
184
+ | Attribute | Type | Description |
185
+ |-----------|------|-------------|
186
+ | `label` | string | Visible field label |
187
+ | `name` | string | Form field name |
188
+ | `type` | string | Input type โ€” `text`, `password`, `email`, `tel`, `number`, `url`, `search` |
189
+ | `placeholder` | string | Placeholder text |
190
+ | `required` | boolean | Mark as required |
191
+ | `disabled` | boolean | Disable the field |
192
+ | `readonly` | boolean | Make read-only |
193
+ | `pattern` | string | Regex validation pattern |
194
+ | `minlength` | number | Minimum character length |
195
+ | `maxlength` | number | Maximum character length |
196
+ | `autocomplete` | string | Autocomplete hint (overridden to `off` for sensitive/critical) |
197
+ | `value` | string | Initial value |
198
+ | `security-tier` | string | `public` \| `authenticated` \| `sensitive` \| `critical` |
199
+
200
+ **Properties & Methods**
201
+
202
+ ```js
203
+ const el = document.querySelector('secure-input');
204
+
205
+ el.value // get current value (unmasked)
206
+ el.value = 'foo' // set value programmatically
207
+ el.valid // boolean โ€” passes all validation rules
208
+ el.name // field name string
209
+ el.getAuditLog() // AuditLogEntry[]
210
+ el.clearAuditLog()
211
+ el.getFieldTelemetry() // FieldTelemetry โ€” behavioral signals for this field
212
+ el.focus()
213
+ el.blur()
214
+ ```
215
+
216
+ **Events**
217
+
218
+ | Event | Detail |
219
+ |-------|--------|
220
+ | `secure-input` | `{ name, value, masked, tier }` |
221
+ | `secure-audit` | `{ event, tier, timestamp, โ€ฆ }` |
222
+
223
+ **Example**
74
224
 
75
225
  ```html
76
226
  <secure-input
@@ -82,48 +232,290 @@ Text input with security features and validation.
82
232
  ></secure-input>
83
233
  ```
84
234
 
85
- ### SecureTextarea
86
- Multi-line text input with character counting.
235
+ ---
236
+
237
+ ### `<secure-textarea>`
238
+
239
+ Multi-line input with real-time character counter, rate limiting, and automatic telemetry collection.
240
+
241
+ **Attributes:** `label`, `name`, `placeholder`, `required`, `disabled`, `readonly`, `minlength`, `maxlength`, `rows`, `cols`, `wrap`, `value`, `security-tier`
242
+
243
+ **Properties & Methods:** `value`, `name`, `valid`, `getAuditLog()`, `clearAuditLog()`, `getFieldTelemetry()`, `focus()`, `blur()`
244
+
245
+ **Events:** `secure-textarea` โ†’ `{ name, value, tier }`
87
246
 
88
247
  ```html
89
248
  <secure-textarea
90
249
  label="Description"
91
250
  name="description"
92
- rows="5"
93
251
  maxlength="500"
252
+ rows="5"
94
253
  ></secure-textarea>
95
254
  ```
96
255
 
97
- ### SecureSelect
98
- Dropdown select with XSS prevention.
256
+ ---
257
+
258
+ ### `<secure-select>`
259
+
260
+ Dropdown with option whitelist validation โ€” prevents value injection. Telemetry collected on `change` events.
261
+
262
+ **Attributes:** `label`, `name`, `required`, `disabled`, `multiple`, `size`, `value`, `security-tier`
263
+
264
+ **Properties & Methods**
265
+
266
+ ```js
267
+ el.value // current value (comma-separated for multiple)
268
+ el.selectedOptions // string[] of selected values
269
+ el.valid
270
+ el.name
271
+ el.addOption(value, text, selected?)
272
+ el.removeOption(value)
273
+ el.clearOptions()
274
+ el.getAuditLog()
275
+ el.getFieldTelemetry()
276
+ el.focus()
277
+ el.blur()
278
+ ```
279
+
280
+ **Events:** `secure-select` โ†’ `{ name, value, tier }`
99
281
 
100
282
  ```html
101
- <secure-select
102
- label="Country"
103
- name="country"
104
- required
105
- >
283
+ <secure-select label="Country" name="country" required>
284
+ <option value="">Select a country</option>
106
285
  <option value="us">United States</option>
107
286
  <option value="uk">United Kingdom</option>
108
287
  </secure-select>
109
288
  ```
110
289
 
111
- ### SecureForm
112
- Form container with CSRF protection.
290
+ Light DOM `<option>` children are transferred to the shadow DOM automatically. Only values added via `<option>` or `addOption()` are accepted.
291
+
292
+ ---
293
+
294
+ ### `<secure-form>`
295
+
296
+ Form container with CSRF protection, field validation, behavioral telemetry aggregation, and optional fetch-enhanced submission.
297
+
298
+ > `<secure-form>` uses **light DOM** (no Shadow DOM) for native form submission compatibility.
299
+
300
+ **Attributes**
301
+
302
+ | Attribute | Description |
303
+ |-----------|-------------|
304
+ | `action` | Form submission URL |
305
+ | `method` | HTTP method (default `POST`) |
306
+ | `enctype` | Encoding type |
307
+ | `csrf-token` | CSRF token value injected as a hidden field |
308
+ | `csrf-field-name` | Hidden field name (default `csrf_token`) |
309
+ | `csrf-header-name` | Also send CSRF token in this request header |
310
+ | `novalidate` | Disable browser constraint validation |
311
+ | `enhance` | Enable fetch-based JSON submission instead of native |
312
+ | `security-tier` | Security tier |
313
+
314
+ **Properties & Methods**
315
+
316
+ ```js
317
+ el.valid // true if all secure child fields pass validation
318
+ el.securityTier
319
+ el.getData() // { fieldName: value, โ€ฆ } including CSRF token
320
+ el.reset()
321
+ el.submit() // programmatic submit (triggers validation + telemetry)
322
+ ```
323
+
324
+ **Events**
325
+
326
+ | Event | Detail |
327
+ |-------|--------|
328
+ | `secure-form-submit` | `{ formData, formElement, telemetry, preventDefault() }` โ€” cancelable |
329
+ | `secure-form-success` | `{ formData, response, telemetry }` โ€” only when `enhance` is set |
330
+
331
+ **`telemetry` shape** (`SessionTelemetry`):
332
+
333
+ ```ts
334
+ {
335
+ sessionDuration: number; // ms from form mount to submission
336
+ fieldCount: number;
337
+ fields: FieldTelemetrySnapshot[];
338
+ riskScore: number; // 0โ€“100
339
+ riskSignals: string[]; // e.g. ['session_too_fast', 'all_fields_pasted']
340
+ submittedAt: string; // ISO 8601
341
+ }
342
+ ```
343
+
344
+ **Submission modes**
345
+
346
+ - **Without `enhance`** โ€” native browser form submission. Values from shadow DOM inputs are synced to hidden `<input type="hidden">` fields automatically. Telemetry is available in `secure-form-submit` but not sent to the server.
347
+ - **With `enhance`** โ€” intercepts submit, validates all fields, sends `{ ...formData, _telemetry }` as JSON via `fetch`. Full telemetry payload travels to the server in the same request.
348
+
349
+ **Example**
113
350
 
114
351
  ```html
115
- <secure-form
116
- action="/api/submit"
117
- method="post"
118
- csrf-token="your-token"
119
- security-tier="sensitive"
120
- >
121
- <!-- Your form fields here -->
352
+ <secure-form action="/api/register" method="POST" csrf-token="abc123" enhance>
353
+ <secure-input label="Email" name="email" type="email" required></secure-input>
354
+ <secure-input label="Password" name="password" type="password" required security-tier="critical"></secure-input>
355
+ <secure-submit-button label="Register"></secure-submit-button>
122
356
  </secure-form>
123
357
  ```
124
358
 
125
- ### SecureFileUpload
126
- File upload with drag-drop and validation.
359
+ ```js
360
+ form.addEventListener('secure-form-submit', (e) => {
361
+ const { formData, telemetry } = e.detail;
362
+
363
+ // Block high-risk submissions before they reach your server
364
+ if (telemetry.riskScore >= 50) {
365
+ e.detail.preventDefault();
366
+ showChallenge();
367
+ return;
368
+ }
369
+
370
+ // Log signals for your fraud pipeline
371
+ analytics.track('form_submit', {
372
+ risk: telemetry.riskScore,
373
+ signals: telemetry.riskSignals,
374
+ });
375
+ });
376
+ ```
377
+
378
+ ---
379
+
380
+ ### `<secure-telemetry-provider>`
381
+
382
+ Optional overlay that wraps `<secure-form>` to add environmental signals and HMAC-SHA-256 signing to every submission envelope.
383
+
384
+ > Place this as the outer wrapper. It monitors the entire document for automation markers and DOM tampering during the session.
385
+
386
+ **Attributes**
387
+
388
+ | Attribute | Description |
389
+ |-----------|-------------|
390
+ | `signing-key` | HMAC-SHA-256 key โ€” must also be known server-side to verify the signature |
391
+
392
+ **Properties & Methods**
393
+
394
+ ```js
395
+ const provider = document.querySelector('secure-telemetry-provider');
396
+
397
+ provider.collectSignals() // EnvironmentalSignals โ€” point-in-time snapshot
398
+ provider.getEnvironmentalSignals() // alias for collectSignals()
399
+ provider.sign(signals) // Promise<SignedTelemetryEnvelope>
400
+ ```
401
+
402
+ **What it detects**
403
+
404
+ | Signal | Description |
405
+ |--------|-------------|
406
+ | `webdriverDetected` | `navigator.webdriver` present or truthy |
407
+ | `headlessDetected` | `HeadlessChrome` in userAgent or missing `window.chrome` |
408
+ | `domMutationDetected` | New `<script>` element injected after page load |
409
+ | `injectedScriptCount` | Count of dynamically added `<script>` elements |
410
+ | `devtoolsOpen` | `outerWidth โˆ’ innerWidth > 160` or `outerHeight โˆ’ innerHeight > 160` |
411
+ | `suspiciousScreenSize` | Screen width or height is zero or < 100px |
412
+ | `pointerType` | Last pointer event type: `mouse` \| `touch` \| `pen` \| `none` |
413
+ | `mouseMovementDetected` | Any `mousemove` event fired during session |
414
+ | `keyboardActivityDetected` | Any `keydown` event fired during session |
415
+
416
+ **How the envelope is injected**
417
+
418
+ The provider listens for `secure-form-submit` on itself (bubbles from the nested form). It calls `sign()` asynchronously and attaches the result as `detail.telemetry._env`. Since it mutates the same object reference, any handler that awaits a microtask after the event fires will see `_env` populated.
419
+
420
+ **Signed envelope shape** (`SignedTelemetryEnvelope`):
421
+
422
+ ```ts
423
+ {
424
+ nonce: string; // 32-char random hex โ€” detect replays
425
+ issuedAt: string; // ISO 8601
426
+ environment: EnvironmentalSignals;
427
+ signature: string; // HMAC-SHA-256 hex over nonce.issuedAt.JSON(environment)
428
+ }
429
+ ```
430
+
431
+ **Security notes**
432
+
433
+ - The `signing-key` is a *symmetric* secret. For strong guarantees, rotate it per-session via a server nonce endpoint rather than hardcoding it as an attribute.
434
+ - All signals are heuristic โ€” a determined attacker can spoof them. The value is raising the cost of scripted attacks.
435
+ - In non-secure contexts (`http://`) `SubtleCrypto` is unavailable; the signature will be an empty string. The server should treat unsigned envelopes with reduced trust.
436
+
437
+ **Example**
438
+
439
+ ```html
440
+ <secure-telemetry-provider signing-key="per-session-server-issued-key">
441
+ <secure-form action="/api/login" enhance csrf-token="...">
442
+ <secure-input label="Email" name="email" type="email" required></secure-input>
443
+ <secure-input label="Password" name="password" type="password" required security-tier="critical"></secure-input>
444
+ <secure-submit-button label="Sign in"></secure-submit-button>
445
+ </secure-form>
446
+ </secure-telemetry-provider>
447
+ ```
448
+
449
+ ```js
450
+ document.querySelector('secure-form').addEventListener('secure-form-submit', async (e) => {
451
+ const { telemetry } = e.detail;
452
+
453
+ // _env is populated async by the provider โ€” wait a microtask
454
+ await Promise.resolve();
455
+
456
+ if (telemetry._env) {
457
+ console.log('Nonce:', telemetry._env.nonce);
458
+ console.log('Signature:', telemetry._env.signature);
459
+ // Verify signature server-side with the same key
460
+ }
461
+ });
462
+ ```
463
+
464
+ ---
465
+
466
+ ### `<secure-file-upload>`
467
+
468
+ Drag-and-drop file upload with content validation and optional malware scan hook.
469
+
470
+ **Attributes**
471
+
472
+ | Attribute | Description |
473
+ |-----------|-------------|
474
+ | `label` | Visible label |
475
+ | `name` | Field name |
476
+ | `accept` | Accepted MIME types / extensions (e.g. `image/*,.pdf`) |
477
+ | `max-size` | Max file size in bytes |
478
+ | `multiple` | Allow multiple files |
479
+ | `required` | Mark as required |
480
+ | `capture` | Camera capture hint |
481
+ | `security-tier` | Security tier |
482
+
483
+ **Size limits by tier (when `max-size` is not set)**
484
+
485
+ | Tier | Limit |
486
+ |------|-------|
487
+ | `public` | 20 MB |
488
+ | `authenticated` | 10 MB |
489
+ | `sensitive` | 5 MB |
490
+ | `critical` | 2 MB |
491
+
492
+ **Properties & Methods**
493
+
494
+ ```js
495
+ el.files // FileList | null
496
+ el.valid
497
+ el.name
498
+ el.hasScanHook // boolean
499
+ el.scanning // boolean โ€” true while scan hook is running
500
+ el.clear()
501
+ el.setScanHook(async (file) => { return { valid: true } })
502
+ el.getAuditLog()
503
+ ```
504
+
505
+ **Scan hook**
506
+
507
+ ```js
508
+ upload.setScanHook(async (file) => {
509
+ const result = await myApi.scanFile(file);
510
+ return { valid: result.clean, reason: result.threat };
511
+ });
512
+ ```
513
+
514
+ **Events:** `secure-file-upload` โ†’ `{ name, files: File[], tier }`
515
+
516
+ **Content validation (critical tier):** Magic number verification for JPEG, PNG, and PDF files.
517
+
518
+ **Filename validation:** Blocks path traversal (e.g. `../`), null bytes, and dangerous config file names.
127
519
 
128
520
  ```html
129
521
  <secure-file-upload
@@ -131,62 +523,323 @@ File upload with drag-drop and validation.
131
523
  name="document"
132
524
  accept="image/*,.pdf"
133
525
  max-size="5242880"
526
+ security-tier="sensitive"
134
527
  ></secure-file-upload>
135
528
  ```
136
529
 
137
- ### SecureDateTime
138
- Date and time picker with range validation.
530
+ ---
531
+
532
+ ### `<secure-datetime>`
533
+
534
+ Date and time picker with format validation, range enforcement, and timezone display.
535
+
536
+ **Attributes**
537
+
538
+ | Attribute | Description |
539
+ |-----------|-------------|
540
+ | `label` | Visible label |
541
+ | `name` | Field name |
542
+ | `type` | `date` \| `time` \| `datetime-local` \| `month` \| `week` (default `date`) |
543
+ | `min` | Minimum date/time (ISO format) |
544
+ | `max` | Maximum date/time (ISO format) |
545
+ | `step` | Step value |
546
+ | `value` | Initial value (ISO format) |
547
+ | `required` | Mark as required |
548
+ | `disabled` | Disable the field |
549
+ | `readonly` | Make read-only |
550
+ | `show-timezone` | Display UTC offset alongside the input |
551
+ | `security-tier` | Security tier |
552
+
553
+ **Properties & Methods**
554
+
555
+ ```js
556
+ el.value // ISO string
557
+ el.value = '2025-06-15' // set value
558
+ el.valid
559
+ el.name
560
+ el.getValueAsDate() // Date | null
561
+ el.setValueFromDate(date) // accepts Date object, sets formatted value
562
+ el.getAuditLog()
563
+ el.getFieldTelemetry()
564
+ el.focus()
565
+ el.blur()
566
+ ```
567
+
568
+ **Events:** `secure-datetime` โ†’ `{ name, value, type, tier }`
569
+
570
+ **CRITICAL tier:** Year must be between 1900 and 2100.
139
571
 
140
572
  ```html
141
573
  <secure-datetime
142
- label="Birth Date"
143
- name="birthdate"
144
- type="date"
145
- min="1900-01-01"
146
- max="2025-12-31"
574
+ label="Appointment"
575
+ name="appointment"
576
+ type="datetime-local"
577
+ min="2025-01-01T00:00"
578
+ max="2030-12-31T23:59"
579
+ required
147
580
  ></secure-datetime>
148
581
  ```
149
582
 
150
- ### SecureSubmitButton
151
- Accessible submit button with loading state and security integration.
583
+ ---
152
584
 
153
- ```html
154
- <secure-submit-button label="Submit"></secure-submit-button>
155
- ```
585
+ ### `<secure-table>`
156
586
 
157
- ### SecureTable
158
- Data table with sorting, filtering, and pagination.
587
+ Data table with sorting, filtering, pagination, and column-level data masking.
159
588
 
160
- ```javascript
161
- const table = document.querySelector('secure-table');
589
+ **Properties**
590
+
591
+ ```js
162
592
  table.data = [
163
- { id: 1, name: 'John', email: 'john@example.com' },
164
- { id: 2, name: 'Jane', email: 'jane@example.com' }
593
+ { id: 1, name: 'Alice', ssn: '123-45-6789' },
165
594
  ];
166
595
  table.columns = [
167
- { key: 'id', label: 'ID', sortable: true },
596
+ { key: 'id', label: 'ID', sortable: true },
168
597
  { key: 'name', label: 'Name', sortable: true, filterable: true },
169
- { key: 'email', label: 'Email', sortable: true, tier: 'sensitive' }
598
+ { key: 'ssn', label: 'SSN', tier: 'critical' }, // masked
599
+ ];
600
+ ```
601
+
602
+ **Column definition**
603
+
604
+ | Property | Type | Description |
605
+ |----------|------|-------------|
606
+ | `key` | string | Data object key |
607
+ | `label` | string | Column header text |
608
+ | `sortable` | boolean | Enable click-to-sort |
609
+ | `filterable` | boolean | Include in global search |
610
+ | `tier` | string | Mask values at this tier level |
611
+ | `width` | string | CSS column width |
612
+ | `render` | `(value, row, key) => string` | Custom cell renderer |
613
+
614
+ **Column masking**
615
+
616
+ | Column `tier` | Behaviour |
617
+ |---------------|-----------|
618
+ | `sensitive` | Last 4 characters visible, rest masked (`โ€ขโ€ขโ€ขโ€ข4567`) |
619
+ | `critical` | Fully masked (`โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข`) |
620
+
621
+ **Events:** `table-action` โ†’ `{ action, โ€ฆdata-attributes }` โ€” fired when an element with `[data-action]` inside a cell is clicked.
622
+
623
+ **Progressive enhancement:** Place a `<table slot="table">` with `data-key` attributes on `<th>` elements inside `<secure-table>` โ€” the component reads columns and data from the server-rendered markup.
624
+
625
+ ```js
626
+ table.columns = [
627
+ {
628
+ key: 'status',
629
+ label: 'Status',
630
+ render: (value) => `<span class="badge">${value}</span>`
631
+ }
170
632
  ];
171
633
  ```
172
634
 
173
- ## ๐ŸŽจ Customization
635
+ ---
636
+
637
+ ### `<secure-card>`
638
+
639
+ Composite credit card form with a live 3D card preview, automatic card type detection, Luhn validation, expiry checking, and aggregate telemetry across all four fields. All inputs render inside a single closed Shadow DOM.
640
+
641
+ **Security model:**
642
+ - Full PAN and CVC are never included in events, audit logs, or hidden form inputs
643
+ - CVC uses native `type="password"` masking โ€” never visible on screen
644
+ - Card number is masked to last-4 on blur
645
+ - Security tier is locked to `critical` and cannot be changed
646
+ - All sensitive state is wiped on `disconnectedCallback`
647
+ - Telemetry from all four inputs (number, expiry, CVC, name) is aggregated into one composite behavioral fingerprint
648
+
649
+ > Raw card data must be passed directly to a PCI-compliant payment processor SDK (e.g. Stripe.js, Braintree). Use `getCardData()` for that handoff โ€” never send raw card numbers or CVCs to your own server.
650
+
651
+ **Attributes**
652
+
653
+ | Attribute | Type | Description |
654
+ |-----------|------|-------------|
655
+ | `label` | string | Legend text displayed above the fields |
656
+ | `name` | string | Base name for hidden form inputs |
657
+ | `show-name` | boolean | Show the optional cardholder name field |
658
+ | `disabled` | boolean | Disable all fields |
659
+ | `required` | boolean | Mark fields as required |
660
+
661
+ **Properties & Methods**
662
+
663
+ ```js
664
+ const card = document.querySelector('secure-card');
665
+
666
+ card.valid // true when all visible fields pass validation
667
+ card.cardType // 'visa' | 'mastercard' | 'amex' | 'discover' | 'diners' | 'jcb' | 'unknown'
668
+ card.last4 // last 4 digits โ€” safe to display and log
669
+ card.name // value of the name attribute
670
+ card.getCardData() // { number, expiry, cvc, name } | null โ€” for payment SDK only
671
+ card.getFieldTelemetry() // composite behavioral signals across all 4 card inputs
672
+ card.reset()
673
+ card.focus()
674
+ card.getAuditLog()
675
+ ```
676
+
677
+ **Events**
678
+
679
+ | Event | Detail |
680
+ |-------|--------|
681
+ | `secure-card` | `{ name, cardType, last4, expiryMonth, expiryYear, cardholderName, valid, tier }` |
682
+ | `secure-audit` | `{ event, tier, timestamp, โ€ฆ }` |
683
+
684
+ Note: the `secure-card` event detail intentionally omits the full PAN and CVC.
685
+
686
+ **CSS Parts**
687
+
688
+ | Part | Element |
689
+ |------|---------|
690
+ | `container` | Outer wrapper |
691
+ | `label` | Legend element |
692
+ | `wrapper` | Input wrapper (per field group) |
693
+ | `number-input` | Card number `<input>` |
694
+ | `expiry-input` | Expiry `<input>` |
695
+ | `cvc-input` | CVC `<input>` |
696
+ | `name-input` | Cardholder name `<input>` |
697
+ | `error` | Error message container (per field) |
698
+
699
+ **Card type detection**
700
+
701
+ | Type | Detected prefix |
702
+ |------|----------------|
703
+ | Visa | `4` |
704
+ | Mastercard | `51โ€“55`, `2221โ€“2720` |
705
+ | Amex | `34`, `37` |
706
+ | Discover | `6011`, `65xx` |
707
+ | Diners | `300โ€“305`, `36`, `38` |
708
+ | JCB | `2131`, `1800`, `35xxx` |
709
+
710
+ **Form participation**
711
+
712
+ Three hidden inputs are created in the light DOM:
713
+ - `{name}` โ€” last 4 digits only (not full PAN)
714
+ - `{name}-expiry` โ€” MM/YY string
715
+ - `{name}-holder` โ€” cardholder name
716
+
717
+ No hidden input is created for CVC.
718
+
719
+ **Example**
720
+
721
+ ```html
722
+ <secure-card name="payment" label="Card details" show-name></secure-card>
723
+ ```
724
+
725
+ ```js
726
+ // When the user clicks Pay โ€” pass directly to your payment SDK
727
+ payButton.addEventListener('click', async () => {
728
+ const data = card.getCardData();
729
+ if (!data) return;
730
+ const token = await stripe.createToken({ number: data.number, exp_month: ..., cvc: data.cvc });
731
+ // Send token.id to your server โ€” never data.number or data.cvc
732
+ });
733
+ ```
734
+
735
+ ---
736
+
737
+ ### `<secure-submit-button>`
738
+
739
+ Accessible submit button with loading state and automatic form-validity gating.
740
+
741
+ **Attributes:** `label`, `loading-label`, `disabled`, `security-tier`
174
742
 
175
- ### Using Design Tokens
743
+ **Properties**
744
+
745
+ ```js
746
+ el.disabled
747
+ el.label
748
+ el.getAuditLog()
749
+ ```
750
+
751
+ **Behaviour**
752
+
753
+ - For `authenticated`, `sensitive`, and `critical` tiers: button remains disabled until all required `<secure-input>`, `<secure-textarea>`, `<secure-select>`, and `<secure-datetime>` siblings report `valid = true`.
754
+ - Shows a spinner and `loading-label` during form submission.
755
+ - Rate-limited at `sensitive` / `critical` tiers.
756
+
757
+ ```html
758
+ <secure-submit-button label="Submit" loading-label="Submittingโ€ฆ"></secure-submit-button>
759
+ ```
760
+
761
+ ---
762
+
763
+ ## Common Attributes
764
+
765
+ All field components support:
766
+
767
+ | Attribute | Type | Description |
768
+ |-----------|------|-------------|
769
+ | `label` | string | Visible field label |
770
+ | `name` | string | Form field name |
771
+ | `required` | boolean | Mark field as required |
772
+ | `disabled` | boolean | Disable the field |
773
+ | `readonly` | boolean | Make the field read-only |
774
+ | `security-tier` | string | `public` \| `authenticated` \| `sensitive` \| `critical` (default: `critical`) |
775
+
776
+ ## Common Properties & Methods
777
+
778
+ All field components expose these in addition to component-specific methods:
779
+
780
+ ```js
781
+ el.value // get/set current value
782
+ el.valid // boolean โ€” passes all validation rules
783
+ el.name // field name string
784
+ el.securityTier // current security tier
785
+ el.getAuditLog() // AuditLogEntry[]
786
+ el.clearAuditLog()
787
+ el.getFieldTelemetry() // FieldTelemetry โ€” behavioral signals (no raw values)
788
+ el.focus()
789
+ el.blur()
790
+ ```
791
+
792
+ **`FieldTelemetry` shape:**
793
+
794
+ ```ts
795
+ {
796
+ dwell: number; // ms from focus to first keystroke
797
+ completionTime: number; // ms from first keystroke to blur
798
+ velocity: number; // keystrokes per second
799
+ corrections: number; // backspace / delete event count
800
+ pasteDetected: boolean;
801
+ autofillDetected: boolean;
802
+ focusCount: number;
803
+ blurWithoutChange: number; // focused but left without typing
804
+ }
805
+ ```
806
+
807
+ ## Common Events
808
+
809
+ | Event | Fired by | Detail |
810
+ |-------|----------|--------|
811
+ | `secure-input` | `<secure-input>` | `{ name, value, masked, tier }` |
812
+ | `secure-textarea` | `<secure-textarea>` | `{ name, value, tier }` |
813
+ | `secure-select` | `<secure-select>` | `{ name, value, tier }` |
814
+ | `secure-datetime` | `<secure-datetime>` | `{ name, value, type, tier }` |
815
+ | `secure-file-upload` | `<secure-file-upload>` | `{ name, files, tier }` |
816
+ | `secure-form-submit` | `<secure-form>` | `{ formData, formElement, telemetry, preventDefault() }` |
817
+ | `secure-form-success` | `<secure-form>` | `{ formData, response, telemetry }` |
818
+ | `secure-card` | `<secure-card>` | `{ name, cardType, last4, expiryMonth, expiryYear, cardholderName, valid, tier }` |
819
+ | `secure-audit` | all components | `{ event, tier, timestamp, โ€ฆ }` |
820
+ | `table-action` | `<secure-table>` | `{ action, row }` |
821
+
822
+ ---
823
+
824
+ ## Customisation
825
+
826
+ ### CSS Design Tokens
827
+
828
+ Override tokens at `:root` to theme all components globally:
176
829
 
177
830
  ```css
178
831
  :root {
179
- /* Override global colors */
180
- --secure-ui-color-primary: #your-brand-color;
832
+ --secure-ui-color-primary: #6366f1;
181
833
  --secure-ui-input-border-radius: 8px;
182
- --secure-ui-font-family-base: 'Your Font', sans-serif;
834
+ --secure-ui-font-family-base: 'Inter', sans-serif;
183
835
  }
184
836
  ```
185
837
 
186
- ### Using CSS Parts API
838
+ ### CSS Parts API
839
+
840
+ Style internal elements directly:
187
841
 
188
842
  ```css
189
- /* Style internal elements */
190
843
  secure-input::part(label) {
191
844
  font-weight: 700;
192
845
  text-transform: uppercase;
@@ -202,109 +855,65 @@ secure-input::part(error) {
202
855
  }
203
856
  ```
204
857
 
205
- See the [Customization Guide](https://github.com/Barryprender/Secure-UI/blob/main/secure-ui-components/docs/customization.md) for a complete styling guide with examples.
858
+ **Available parts on all components:** `container`, `label`, `wrapper`, `input` / `textarea` / `select`, `error`
206
859
 
207
- ## ๐Ÿ” Security Tiers
860
+ See the [Customization Guide](https://github.com/Barryprender/Secure-UI/blob/main/secure-ui-components/docs/customization.md) for a full token reference.
208
861
 
209
- ```html
210
- <!-- PUBLIC: Basic validation -->
211
- <secure-input security-tier="public"></secure-input>
862
+ ---
212
863
 
213
- <!-- AUTHENTICATED: Enhanced validation, detailed logging -->
214
- <secure-input security-tier="authenticated"></secure-input>
864
+ ## Per-Component Imports
215
865
 
216
- <!-- SENSITIVE: Strict validation, autocomplete disabled -->
217
- <secure-input security-tier="sensitive"></secure-input>
866
+ Import only what you need:
218
867
 
219
- <!-- CRITICAL: Maximum security, masking, rate limiting -->
220
- <secure-input security-tier="critical" type="password"></secure-input>
868
+ ```js
869
+ import 'secure-ui-components/secure-input';
870
+ import 'secure-ui-components/secure-textarea';
871
+ import 'secure-ui-components/secure-select';
872
+ import 'secure-ui-components/secure-form';
873
+ import 'secure-ui-components/secure-file-upload';
874
+ import 'secure-ui-components/secure-datetime';
875
+ import 'secure-ui-components/secure-table';
876
+ import 'secure-ui-components/secure-card';
877
+ import 'secure-ui-components/secure-telemetry-provider';
221
878
  ```
222
879
 
223
- ## ๐Ÿงช Testing
224
-
225
- All components include comprehensive tests:
226
-
227
- ```bash
228
- # Run all tests
229
- npm test
230
-
231
- # Run tests in watch mode
232
- npm run test:watch
880
+ Or import everything at once:
233
881
 
234
- # Run tests with coverage report
235
- npm run test:coverage
882
+ ```js
883
+ import 'secure-ui-components';
236
884
  ```
237
885
 
238
- ## ๐Ÿ“– Documentation
239
-
240
- - [Customization Guide](https://github.com/Barryprender/Secure-UI/blob/main/secure-ui-components/docs/customization.md) - Complete styling guide
241
- - [Architecture](https://github.com/Barryprender/Secure-UI/blob/main/secure-ui-components/docs/ARCHITECTURE.md) - Technical architecture details
242
-
243
- ## ๐Ÿ“ API Reference
244
-
245
- ### Common Attributes
246
-
247
- All components support:
248
-
249
- | Attribute | Type | Description |
250
- |-----------|------|-------------|
251
- | `label` | `string` | Visible field label |
252
- | `name` | `string` | Form field name |
253
- | `required` | `boolean` | Mark field as required |
254
- | `disabled` | `boolean` | Disable the field |
255
- | `readonly` | `boolean` | Make the field read-only |
256
- | `security-tier` | `string` | Security level: `public`, `authenticated`, `sensitive`, `critical` (default: `critical`) |
257
-
258
- ### Common Properties / Methods
886
+ ---
259
887
 
260
- ```js
261
- const el = document.querySelector('secure-input');
888
+ ## Testing
262
889
 
263
- el.value // get current value (unmasked)
264
- el.value = 'foo' // set value programmatically
265
- el.valid // boolean โ€” passes all validation rules
266
- el.name // field name string
267
- el.getAuditLog() // returns array of security audit log entries
890
+ ```bash
891
+ npm test # run all tests
892
+ npm run test:watch # watch mode
893
+ npm run test:coverage # coverage report
268
894
  ```
269
895
 
270
- `secure-file-upload` also exposes:
896
+ ---
271
897
 
272
- ```js
273
- el.files // FileList | null
274
- el.clear() // clear selected files
275
- el.hasScanHook // boolean
276
- el.scanning // boolean โ€” true while scan hook is running
277
- el.setScanHook(async (file) => { return { valid: true } })
278
- ```
898
+ ## Documentation
279
899
 
280
- ### Common Events
900
+ - [Customization Guide](https://github.com/Barryprender/Secure-UI/blob/main/secure-ui-components/docs/customization.md)
901
+ - [Architecture](https://github.com/Barryprender/Secure-UI/blob/main/secure-ui-components/docs/ARCHITECTURE.md)
902
+ - [Live Demo](https://barryprender.github.io/Secure-UI/)
281
903
 
282
- | Event | Fired by | Detail |
283
- |-------|----------|--------|
284
- | `secure-input` | `secure-input` | `{ name, value, masked, tier }` |
285
- | `secure-textarea` | `secure-textarea` | `{ name, value, tier }` |
286
- | `secure-select` | `secure-select` | `{ name, value, tier }` |
287
- | `secure-datetime` | `secure-datetime` | `{ name, value, type, tier }` |
288
- | `secure-file-upload` | `secure-file-upload` | `{ name, files, tier }` |
289
- | `secure-form-submit` | `secure-form` | `{ formData, formElement, preventDefault }` |
290
- | `secure-form-success` | `secure-form` | `{ formData, response }` |
291
- | `secure-audit` | all components | `{ event, tier, timestamp, โ€ฆ }` |
292
- | `table-action` | `secure-table` | `{ action, row }` |
904
+ ---
293
905
 
294
- ## ๐Ÿค Contributing
906
+ ## Contributing
295
907
 
296
- Contributions are welcome! Please see the main repository for guidelines.
908
+ Contributions are welcome. Please see the main repository for guidelines.
297
909
 
298
- ## ๐Ÿ“„ License
910
+ ## License
299
911
 
300
- MIT License - see LICENSE file for details.
912
+ MIT โ€” see LICENSE file for details.
301
913
 
302
- ## ๐Ÿ”— Links
914
+ ## Links
303
915
 
304
916
  - [GitHub Repository](https://github.com/Barryprender/Secure-UI)
917
+ - [npm Package](https://www.npmjs.com/package/secure-ui-components)
305
918
  - [Live Demo](https://barryprender.github.io/Secure-UI/)
306
-
307
- ## ๐Ÿ†˜ Support
308
-
309
919
  - [Issue Tracker](https://github.com/Barryprender/Secure-UI/issues)
310
- - [Discussions](https://github.com/Barryprender/Secure-UI/discussions)