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.
- package/README.md +772 -163
- package/dist/components/secure-card/secure-card.css +427 -0
- package/dist/components/secure-card/secure-card.d.ts +73 -0
- package/dist/components/secure-card/secure-card.d.ts.map +1 -0
- package/dist/components/secure-card/secure-card.js +787 -0
- package/dist/components/secure-card/secure-card.js.map +1 -0
- package/dist/components/secure-datetime/secure-datetime.d.ts.map +1 -1
- package/dist/components/secure-datetime/secure-datetime.js +6 -19
- package/dist/components/secure-datetime/secure-datetime.js.map +1 -1
- package/dist/components/secure-file-upload/secure-file-upload.d.ts.map +1 -1
- package/dist/components/secure-file-upload/secure-file-upload.js +0 -16
- package/dist/components/secure-file-upload/secure-file-upload.js.map +1 -1
- package/dist/components/secure-form/secure-form.d.ts.map +1 -1
- package/dist/components/secure-form/secure-form.js +126 -6
- package/dist/components/secure-form/secure-form.js.map +1 -1
- package/dist/components/secure-input/secure-input.d.ts.map +1 -1
- package/dist/components/secure-input/secure-input.js +6 -21
- package/dist/components/secure-input/secure-input.js.map +1 -1
- package/dist/components/secure-select/secure-select.d.ts.map +1 -1
- package/dist/components/secure-select/secure-select.js +6 -19
- package/dist/components/secure-select/secure-select.js.map +1 -1
- package/dist/components/secure-telemetry-provider/secure-telemetry-provider.d.ts +63 -0
- package/dist/components/secure-telemetry-provider/secure-telemetry-provider.d.ts.map +1 -0
- package/dist/components/secure-telemetry-provider/secure-telemetry-provider.js +198 -0
- package/dist/components/secure-telemetry-provider/secure-telemetry-provider.js.map +1 -0
- package/dist/components/secure-textarea/secure-textarea.d.ts.map +1 -1
- package/dist/components/secure-textarea/secure-textarea.js +6 -19
- package/dist/components/secure-textarea/secure-textarea.js.map +1 -1
- package/dist/core/base-component.d.ts +23 -1
- package/dist/core/base-component.d.ts.map +1 -1
- package/dist/core/base-component.js +111 -0
- package/dist/core/base-component.js.map +1 -1
- package/dist/core/security-config.d.ts.map +1 -1
- package/dist/core/security-config.js +5 -21
- package/dist/core/security-config.js.map +1 -1
- package/dist/core/types.d.ts +129 -8
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/package.json +4 -2
- package/package.json +13 -1
package/README.md
CHANGED
|
@@ -1,49 +1,138 @@
|
|
|
1
1
|
# secure-ui-components
|
|
2
2
|
|
|
3
|
-
Security-first
|
|
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
|
-
##
|
|
7
|
+
## Features
|
|
8
8
|
|
|
9
|
-
- **
|
|
10
|
-
- **4-Tier Security System
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
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
|
-
|
|
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
|
-
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Quick Start
|
|
24
106
|
|
|
25
|
-
###
|
|
107
|
+
### With telemetry (recommended)
|
|
26
108
|
|
|
27
|
-
|
|
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
|
-
|
|
31
|
-
|
|
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
|
-
|
|
127
|
+
### Bundler (Vite, Webpack, Rollup)
|
|
35
128
|
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
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
|
-
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Security Tiers
|
|
71
156
|
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
86
|
-
|
|
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
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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
|
-
|
|
112
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
126
|
-
|
|
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
|
-
|
|
138
|
-
|
|
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="
|
|
143
|
-
name="
|
|
144
|
-
type="
|
|
145
|
-
min="
|
|
146
|
-
max="
|
|
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
|
-
|
|
151
|
-
Accessible submit button with loading state and security integration.
|
|
583
|
+
---
|
|
152
584
|
|
|
153
|
-
|
|
154
|
-
<secure-submit-button label="Submit"></secure-submit-button>
|
|
155
|
-
```
|
|
585
|
+
### `<secure-table>`
|
|
156
586
|
|
|
157
|
-
|
|
158
|
-
Data table with sorting, filtering, and pagination.
|
|
587
|
+
Data table with sorting, filtering, pagination, and column-level data masking.
|
|
159
588
|
|
|
160
|
-
|
|
161
|
-
|
|
589
|
+
**Properties**
|
|
590
|
+
|
|
591
|
+
```js
|
|
162
592
|
table.data = [
|
|
163
|
-
{ id: 1, name: '
|
|
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',
|
|
596
|
+
{ key: 'id', label: 'ID', sortable: true },
|
|
168
597
|
{ key: 'name', label: 'Name', sortable: true, filterable: true },
|
|
169
|
-
{ key: '
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: '
|
|
834
|
+
--secure-ui-font-family-base: 'Inter', sans-serif;
|
|
183
835
|
}
|
|
184
836
|
```
|
|
185
837
|
|
|
186
|
-
###
|
|
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
|
-
|
|
858
|
+
**Available parts on all components:** `container`, `label`, `wrapper`, `input` / `textarea` / `select`, `error`
|
|
206
859
|
|
|
207
|
-
|
|
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
|
-
|
|
210
|
-
<!-- PUBLIC: Basic validation -->
|
|
211
|
-
<secure-input security-tier="public"></secure-input>
|
|
862
|
+
---
|
|
212
863
|
|
|
213
|
-
|
|
214
|
-
<secure-input security-tier="authenticated"></secure-input>
|
|
864
|
+
## Per-Component Imports
|
|
215
865
|
|
|
216
|
-
|
|
217
|
-
<secure-input security-tier="sensitive"></secure-input>
|
|
866
|
+
Import only what you need:
|
|
218
867
|
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
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
|
-
|
|
235
|
-
|
|
882
|
+
```js
|
|
883
|
+
import 'secure-ui-components';
|
|
236
884
|
```
|
|
237
885
|
|
|
238
|
-
|
|
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
|
-
|
|
261
|
-
const el = document.querySelector('secure-input');
|
|
888
|
+
## Testing
|
|
262
889
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
896
|
+
---
|
|
271
897
|
|
|
272
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
906
|
+
## Contributing
|
|
295
907
|
|
|
296
|
-
Contributions are welcome
|
|
908
|
+
Contributions are welcome. Please see the main repository for guidelines.
|
|
297
909
|
|
|
298
|
-
##
|
|
910
|
+
## License
|
|
299
911
|
|
|
300
|
-
MIT
|
|
912
|
+
MIT โ see LICENSE file for details.
|
|
301
913
|
|
|
302
|
-
##
|
|
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)
|