typesecure 0.1.0 → 0.2.3
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 +163 -100
- package/dist/index.d.mts +143 -224
- package/dist/index.d.ts +143 -224
- package/dist/index.js +391 -657
- package/dist/index.mjs +367 -625
- package/package.json +44 -35
package/README.md
CHANGED
|
@@ -1,21 +1,34 @@
|
|
|
1
1
|
# typesecure
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`typesecure` is a **classification-first security core** for TypeScript projects.
|
|
4
|
+
|
|
5
|
+
Instead of starting with crypto primitives, it starts with what actually causes most security incidents in web apps: **data leaving the boundary it should never cross** (logs, analytics, error trackers, headers, client bundles, etc).
|
|
6
|
+
|
|
7
|
+
You “type” your data as `public | pii | secret | token | credential`, and `typesecure` helps you **enforce** safe handling using TypeScript + runtime checks.
|
|
4
8
|
|
|
5
9
|
## Features
|
|
6
10
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
11
|
+
- **Classification types**: `PublicString`, `PIIString`, `SecretString`, `TokenString`, `CredentialString`.
|
|
12
|
+
- **Runtime validation**: Zod-backed constructors (`secretText()`, `piiText()`, ...).
|
|
13
|
+
- **Redaction**: `redact()` and `safeJsonStringify()` prevent secret/PII leakage.
|
|
14
|
+
- **Policy enforcement**: `defaultPolicy()`, `assertAllowed()`, `audit()` help block unsafe crossings.
|
|
15
|
+
|
|
16
|
+
## Good for / Use when
|
|
17
|
+
|
|
18
|
+
- **You need to stop leaks early**: preventing secrets/PII from ending up in logs, analytics, error trackers, or client bundles.
|
|
19
|
+
- **You want safe defaults**: making insecure behavior harder than secure behavior.
|
|
20
|
+
- **You want guardrails at the boundary**: before logging, emitting telemetry, making network calls, or writing to storage.
|
|
21
|
+
|
|
22
|
+
## Not a fit / Don’t use when
|
|
23
|
+
|
|
24
|
+
- **You need a full security platform** (hosted policy registry, enterprise controls). `typesecure` is a library.
|
|
25
|
+
- **You need production-grade crypto primitives**. Use well-reviewed, purpose-built libraries and treat crypto carefully.
|
|
26
|
+
- **You only want compile-time types with zero runtime behavior**. `typesecure` deliberately includes runtime checks/redaction.
|
|
16
27
|
|
|
17
28
|
## Installation
|
|
18
29
|
|
|
30
|
+
Requires Node.js `>=18.18.0`.
|
|
31
|
+
|
|
19
32
|
```bash
|
|
20
33
|
# Using npm
|
|
21
34
|
npm install typesecure
|
|
@@ -29,113 +42,125 @@ pnpm add typesecure
|
|
|
29
42
|
|
|
30
43
|
## Usage
|
|
31
44
|
|
|
32
|
-
###
|
|
45
|
+
### Classification-first data handling
|
|
33
46
|
|
|
34
47
|
```typescript
|
|
35
|
-
import {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
import {
|
|
49
|
+
piiText,
|
|
50
|
+
secretText,
|
|
51
|
+
token,
|
|
52
|
+
publicText,
|
|
53
|
+
redact,
|
|
54
|
+
safeJsonStringify,
|
|
55
|
+
defaultPolicy,
|
|
56
|
+
assertAllowed,
|
|
57
|
+
policyLog,
|
|
58
|
+
} from "typesecure";
|
|
59
|
+
|
|
60
|
+
const userEmail = piiText("user@example.com");
|
|
61
|
+
const sessionToken = token("abc.def.ghi");
|
|
62
|
+
const dbPassword = secretText(process.env.DB_PASSWORD ?? "");
|
|
63
|
+
|
|
64
|
+
// Redact before logging / serialization
|
|
65
|
+
console.log(redact({ userEmail, sessionToken, dbPassword }));
|
|
66
|
+
console.log(
|
|
67
|
+
safeJsonStringify({ userEmail, sessionToken, dbPassword }, undefined, 2),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Enforce policy before a boundary crossing
|
|
71
|
+
const policy = defaultPolicy();
|
|
72
|
+
assertAllowed(policy, "network", { sessionToken }); // allowed
|
|
73
|
+
// assertAllowed(policy, 'log', { dbPassword }); // throws
|
|
74
|
+
|
|
75
|
+
// Safe logging helper with enforcement
|
|
76
|
+
policyLog(policy, console, "info", publicText("login_ok"), { userEmail });
|
|
57
77
|
```
|
|
58
78
|
|
|
59
|
-
###
|
|
79
|
+
### Express / Next.js examples
|
|
60
80
|
|
|
61
81
|
```typescript
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
82
|
+
// Express middleware example
|
|
83
|
+
import {
|
|
84
|
+
safeLoggerAdapter,
|
|
85
|
+
defaultPolicy,
|
|
86
|
+
assertAllowed,
|
|
87
|
+
token,
|
|
88
|
+
} from "typesecure";
|
|
89
|
+
|
|
90
|
+
const log = safeLoggerAdapter(console);
|
|
91
|
+
const policy = defaultPolicy();
|
|
92
|
+
|
|
93
|
+
app.use((req, _res, next) => {
|
|
94
|
+
const auth = req.headers.authorization?.replace(/^Bearer\s+/i, "");
|
|
95
|
+
if (auth) {
|
|
96
|
+
const t = token(auth);
|
|
97
|
+
assertAllowed(policy, "network", { t });
|
|
98
|
+
log.info({ route: req.path, auth: t }); // will be redacted
|
|
99
|
+
}
|
|
100
|
+
next();
|
|
70
101
|
});
|
|
71
|
-
|
|
72
|
-
// Store hash, salt, and params in your database
|
|
73
|
-
|
|
74
|
-
// Later, verify the password
|
|
75
|
-
const isValid = verifyPassword('userPassword123', hash, salt, params);
|
|
76
102
|
```
|
|
77
103
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
```typescript
|
|
81
|
-
import { timingSafeEqual, generateRandomBytes } from 'typesecure';
|
|
82
|
-
|
|
83
|
-
// Compare strings in constant time to prevent timing attacks
|
|
84
|
-
const isEqual = timingSafeEqual(userProvidedToken, storedToken);
|
|
85
|
-
|
|
86
|
-
// Generate cryptographically secure random bytes
|
|
87
|
-
const randomBytes = generateRandomBytes(32, 'hex');
|
|
88
|
-
```
|
|
104
|
+
## API Reference
|
|
89
105
|
|
|
90
|
-
###
|
|
106
|
+
### Classification
|
|
107
|
+
|
|
108
|
+
- `publicText(value: string): PublicString`
|
|
109
|
+
- `piiText(value: string): PIIString`
|
|
110
|
+
- `secretText(value: string): SecretString`
|
|
111
|
+
- `token(value: string): TokenString`
|
|
112
|
+
- `credential(value: string): CredentialString`
|
|
113
|
+
- `reveal(value): string` (intentionally explicit)
|
|
114
|
+
|
|
115
|
+
### Redaction
|
|
116
|
+
|
|
117
|
+
- `redact(value): value` (deep traversal)
|
|
118
|
+
- `redactText(value): string` (mask sensitive fragments in plain text)
|
|
119
|
+
- `detectText(value): StringDetection[]` (return ranges/kinds for audit workflows)
|
|
120
|
+
- `safeJsonStringify(value): string`
|
|
121
|
+
- `safeLoggerAdapter(consoleLike)`
|
|
122
|
+
- Redaction options:
|
|
123
|
+
- `guessByKey` (default `true`): redact suspicious keys like `password`, `token`, `apiKey`.
|
|
124
|
+
- `guessByValue` (default `true`): auto-detect and redact sensitive-looking values.
|
|
125
|
+
- `useDefaultValueDetector` (default `true`): keep built-in rule-based detectors on/off.
|
|
126
|
+
- `stringDetectors`: add custom detectors (for NER/ML or domain-specific logic).
|
|
127
|
+
- `minDetectionConfidence` (default `0`): ignore low-confidence custom detections.
|
|
128
|
+
- Value detection masks only the sensitive fragments inside a larger string (instead of replacing the whole text), including:
|
|
129
|
+
- PII: email, phone, SSN, date of birth (`YYYY-MM-DD`), IPv4 address, payment card numbers (Luhn-validated).
|
|
130
|
+
- Secrets/tokens: JWTs, private key PEM blocks, GitHub tokens, AWS access keys, Stripe secret keys, OpenAI-style `sk-...` keys, credential pairs (`user:pass`), high-entropy token-like strings.
|
|
131
|
+
|
|
132
|
+
Example custom detector (NER/ML-style integration):
|
|
91
133
|
|
|
92
134
|
```typescript
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const signature = hmac('message', 'secret key', {
|
|
109
|
-
algorithm: 'sha256',
|
|
110
|
-
encoding: 'base64'
|
|
111
|
-
});
|
|
135
|
+
const out = redact(
|
|
136
|
+
{ text: "Customer Jane Doe uses jane@example.com" },
|
|
137
|
+
{
|
|
138
|
+
stringDetectors: [
|
|
139
|
+
(value) => {
|
|
140
|
+
const name = "Jane Doe";
|
|
141
|
+
const idx = value.indexOf(name);
|
|
142
|
+
return idx >= 0
|
|
143
|
+
? [{ start: idx, end: idx + name.length, kind: "pii", confidence: 0.92, source: "ml.ner" }]
|
|
144
|
+
: [];
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
minDetectionConfidence: 0.8,
|
|
148
|
+
},
|
|
149
|
+
);
|
|
112
150
|
```
|
|
113
151
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
### Encryption
|
|
117
|
-
|
|
118
|
-
- `encrypt(text: string, key: string, options?: Partial<EncryptionOptions>): string`
|
|
119
|
-
- `decrypt(encryptedText: string, key: string, options?: Partial<EncryptionOptions>): string`
|
|
120
|
-
- `generateKey(length?: number): string`
|
|
121
|
-
- `getSecurityLevel(options: EncryptionOptions): SecurityLevel`
|
|
122
|
-
|
|
123
|
-
### Secure Password Storage
|
|
152
|
+
### Policy
|
|
124
153
|
|
|
125
|
-
- `
|
|
126
|
-
- `
|
|
127
|
-
- `
|
|
128
|
-
- `
|
|
129
|
-
|
|
130
|
-
### Hashing
|
|
131
|
-
|
|
132
|
-
- `hash(input: string, options?: Partial<HashOptions>): string`
|
|
133
|
-
- `verifyHash(input: string, hashedValue: string, options?: Partial<HashOptions>): boolean`
|
|
134
|
-
- `hmac(input: string, key: string, options?: Partial<HashOptions>): string`
|
|
154
|
+
- `defaultPolicy(): Policy`
|
|
155
|
+
- `assertAllowed(policy, action, data): void`
|
|
156
|
+
- `audit(policy, action, data): AuditEvent`
|
|
157
|
+
- `policyLog(policy, logger, level, ...args): void`
|
|
135
158
|
|
|
136
159
|
## Security Considerations
|
|
137
160
|
|
|
138
|
-
|
|
161
|
+
Security is as much about **preventing leaks** as it is about cryptographic correctness. `typesecure` focuses on preventing accidental secret/PII exposure across common boundaries.
|
|
162
|
+
|
|
163
|
+
If you need cryptography for production-grade requirements, prefer well-reviewed primitives and consult a security professional. For production applications with high security requirements, consider:
|
|
139
164
|
|
|
140
165
|
1. Consulting a security professional
|
|
141
166
|
2. Using specialized security libraries
|
|
@@ -153,13 +178,51 @@ To contribute to this project:
|
|
|
153
178
|
2. Install dependencies with `pnpm install`
|
|
154
179
|
3. Run tests with `pnpm test`
|
|
155
180
|
4. Build the package with `pnpm build`
|
|
181
|
+
5. Run Enron dataset integration tests with `pnpm test:data`
|
|
182
|
+
|
|
183
|
+
### Optional: external dataset setup
|
|
184
|
+
|
|
185
|
+
For larger redaction/policy experiments (Enron + Synthea FHIR), fetch datasets locally:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
pnpm data:setup
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
This command downloads and extracts to:
|
|
192
|
+
|
|
193
|
+
- `data/enron-maildir`
|
|
194
|
+
- `data/synthea_sample_data_fhir_latest`
|
|
195
|
+
|
|
196
|
+
Notes:
|
|
197
|
+
|
|
198
|
+
- `data/` is gitignored and not published to npm.
|
|
199
|
+
- `pnpm test` excludes dataset suites by default.
|
|
200
|
+
- `pnpm test:data` runs Enron dataset tests with verbose output.
|
|
201
|
+
- `pnpm test:data:synthea` runs Synthea-specific dataset tests.
|
|
202
|
+
- `pnpm test:data:all` runs all dataset suites.
|
|
203
|
+
- You can override source URLs with `ENRON_URL=...` and/or `SYNTHEA_FHIR_URL=...`.
|
|
204
|
+
- You can change destination with `DATA_DIR=/path/to/data`.
|
|
205
|
+
|
|
206
|
+
Dataset sources:
|
|
207
|
+
|
|
208
|
+
- Enron: [https://www.cs.cmu.edu/~enron/](https://www.cs.cmu.edu/~enron/)
|
|
209
|
+
- Synthea: [https://github.com/synthetichealth/synthea-sample-data/](https://github.com/synthetichealth/synthea-sample-data/)
|
|
156
210
|
|
|
157
211
|
This project uses TypeScript for type safety, Jest for testing, and ESLint for code quality.
|
|
158
212
|
|
|
213
|
+
## Dataset Acknowledgements
|
|
214
|
+
|
|
215
|
+
We use these public datasets for redaction and policy testing:
|
|
216
|
+
|
|
217
|
+
- [CMU Enron Email Dataset](https://www.cs.cmu.edu/~enron/)
|
|
218
|
+
- [Synthea Sample Data](https://github.com/synthetichealth/synthea-sample-data/)
|
|
219
|
+
|
|
220
|
+
Personal note: I am especially interested in the historical context around Enron, including how it was able to happen and the improvements in governance and controls that followed.
|
|
221
|
+
|
|
159
222
|
## License
|
|
160
223
|
|
|
161
224
|
MIT © [Arvid Berndtsson](https://github.com/arvid-berndtsson)
|
|
162
225
|
|
|
163
226
|
## Contributing
|
|
164
227
|
|
|
165
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
228
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|