@titanshield/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/TitanShield.d.ts +107 -0
- package/dist/TitanShield.d.ts.map +1 -0
- package/dist/TitanShield.js +248 -0
- package/dist/TitanShield.js.map +1 -0
- package/dist/audit.d.ts +8 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +76 -0
- package/dist/audit.js.map +1 -0
- package/dist/auto.d.ts +12 -0
- package/dist/auto.d.ts.map +1 -0
- package/dist/auto.js +129 -0
- package/dist/auto.js.map +1 -0
- package/dist/badge.d.ts +27 -0
- package/dist/badge.d.ts.map +1 -0
- package/dist/badge.js +127 -0
- package/dist/badge.js.map +1 -0
- package/dist/battle.d.ts +50 -0
- package/dist/battle.d.ts.map +1 -0
- package/dist/battle.js +239 -0
- package/dist/battle.js.map +1 -0
- package/dist/biometrics.d.ts +63 -0
- package/dist/biometrics.d.ts.map +1 -0
- package/dist/biometrics.js +248 -0
- package/dist/biometrics.js.map +1 -0
- package/dist/collective.d.ts +63 -0
- package/dist/collective.d.ts.map +1 -0
- package/dist/collective.js +203 -0
- package/dist/collective.js.map +1 -0
- package/dist/compliance.d.ts +3 -0
- package/dist/compliance.d.ts.map +1 -0
- package/dist/compliance.js +71 -0
- package/dist/compliance.js.map +1 -0
- package/dist/dna.d.ts +82 -0
- package/dist/dna.d.ts.map +1 -0
- package/dist/dna.js +219 -0
- package/dist/dna.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/nlrules.d.ts +68 -0
- package/dist/nlrules.d.ts.map +1 -0
- package/dist/nlrules.js +232 -0
- package/dist/nlrules.js.map +1 -0
- package/dist/prevent.d.ts +119 -0
- package/dist/prevent.d.ts.map +1 -0
- package/dist/prevent.js +380 -0
- package/dist/prevent.js.map +1 -0
- package/dist/quantum.d.ts +105 -0
- package/dist/quantum.d.ts.map +1 -0
- package/dist/quantum.js +269 -0
- package/dist/quantum.js.map +1 -0
- package/dist/scanner.d.ts +61 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +364 -0
- package/dist/scanner.js.map +1 -0
- package/dist/threats.d.ts +10 -0
- package/dist/threats.d.ts.map +1 -0
- package/dist/threats.js +96 -0
- package/dist/threats.js.map +1 -0
- package/dist/types.d.ts +68 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +51 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +59 -0
- package/dist/validate.js.map +1 -0
- package/package.json +33 -0
- package/src/TitanShield.ts +303 -0
- package/src/audit.ts +75 -0
- package/src/auto.ts +137 -0
- package/src/badge.ts +145 -0
- package/src/battle.ts +300 -0
- package/src/biometrics.ts +307 -0
- package/src/collective.ts +269 -0
- package/src/compliance.ts +74 -0
- package/src/dna.ts +304 -0
- package/src/index.ts +59 -0
- package/src/nlrules.ts +297 -0
- package/src/prevent.ts +474 -0
- package/src/quantum.ts +341 -0
- package/src/scanner.ts +431 -0
- package/src/threats.ts +105 -0
- package/src/types.ts +108 -0
- package/src/validate.ts +72 -0
- package/tsconfig.json +26 -0
package/dist/validate.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.z = exports.Schemas = void 0;
|
|
4
|
+
exports.validate = validate;
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
Object.defineProperty(exports, "z", { enumerable: true, get: function () { return zod_1.z; } });
|
|
7
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8
|
+
// Input Validation Module — Zod-powered with XSS / SQLi sanitization
|
|
9
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
10
|
+
// Common XSS / injection patterns
|
|
11
|
+
const XSS_PATTERN = /<script[\s\S]*?>[\s\S]*?<\/script>|javascript:|on\w+\s*=|<iframe|<object|<embed/gi;
|
|
12
|
+
const SQLI_PATTERN = /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|UNION|EXEC|EXECUTE|DECLARE|CAST|CONVERT|ALTER|CREATE)\b)|(-{2}|\/\*|\*\/|;)/gi;
|
|
13
|
+
function sanitizeString(value) {
|
|
14
|
+
return value
|
|
15
|
+
.replace(XSS_PATTERN, '')
|
|
16
|
+
.replace(SQLI_PATTERN, '')
|
|
17
|
+
.trim();
|
|
18
|
+
}
|
|
19
|
+
function sanitizeDeep(input) {
|
|
20
|
+
if (typeof input === 'string')
|
|
21
|
+
return sanitizeString(input);
|
|
22
|
+
if (Array.isArray(input))
|
|
23
|
+
return input.map(sanitizeDeep);
|
|
24
|
+
if (input !== null && typeof input === 'object') {
|
|
25
|
+
return Object.fromEntries(Object.entries(input).map(([k, v]) => [k, sanitizeDeep(v)]));
|
|
26
|
+
}
|
|
27
|
+
return input;
|
|
28
|
+
}
|
|
29
|
+
function validate(schema, input) {
|
|
30
|
+
const sanitized = sanitizeDeep(input);
|
|
31
|
+
const result = schema.safeParse(sanitized);
|
|
32
|
+
if (result.success) {
|
|
33
|
+
return { valid: true, errors: [], sanitized: result.data };
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
valid: false,
|
|
37
|
+
errors: result.error.errors.map(e => ({
|
|
38
|
+
field: e.path.join('.'),
|
|
39
|
+
message: e.message,
|
|
40
|
+
code: e.code,
|
|
41
|
+
})),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// ── Pre-built schemas for common inputs ──────────────────────────────────────
|
|
45
|
+
exports.Schemas = {
|
|
46
|
+
email: zod_1.z.object({ email: zod_1.z.string().email().max(320) }),
|
|
47
|
+
login: zod_1.z.object({
|
|
48
|
+
email: zod_1.z.string().email().max(320),
|
|
49
|
+
password: zod_1.z.string().min(8).max(128),
|
|
50
|
+
}),
|
|
51
|
+
userRegistration: zod_1.z.object({
|
|
52
|
+
email: zod_1.z.string().email().max(320),
|
|
53
|
+
password: zod_1.z.string().min(8).max(128).regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&#])/, 'Password must contain uppercase, lowercase, digit, and special character'),
|
|
54
|
+
displayName: zod_1.z.string().min(2).max(64).regex(/^[a-zA-Z0-9 _-]+$/),
|
|
55
|
+
}),
|
|
56
|
+
text: (maxLen = 500) => zod_1.z.object({ text: zod_1.z.string().max(maxLen) }),
|
|
57
|
+
id: zod_1.z.object({ id: zod_1.z.string().uuid() }),
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":";;;AA6BA,4BAgBC;AA7CD,6BAAmC;AAuE1B,kFAvEA,OAAC,OAuEA;AApEV,gFAAgF;AAChF,qEAAqE;AACrE,gFAAgF;AAEhF,kCAAkC;AAClC,MAAM,WAAW,GAAG,mFAAmF,CAAC;AACxG,MAAM,YAAY,GAAG,oHAAoH,CAAC;AAE1I,SAAS,cAAc,CAAC,KAAa;IACjC,OAAO,KAAK;SACP,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;SACxB,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC;SACzB,IAAI,EAAE,CAAC;AAChB,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAChC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,cAAc,CAAC,KAAK,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACzD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9C,OAAO,MAAM,CAAC,WAAW,CACrB,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CACzF,CAAC;IACN,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAgB,QAAQ,CAAI,MAAoB,EAAE,KAAc;IAC5D,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAE3C,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACjB,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;IAC/D,CAAC;IAED,OAAO;QACH,KAAK,EAAE,KAAK;QACZ,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAClC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;YACvB,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,IAAI,EAAE,CAAC,CAAC,IAAI;SACf,CAAC,CAAC;KACN,CAAC;AACN,CAAC;AAED,gFAAgF;AAEnE,QAAA,OAAO,GAAG;IACnB,KAAK,EAAE,OAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;IAEvD,KAAK,EAAE,OAAC,CAAC,MAAM,CAAC;QACZ,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;QAClC,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;KACvC,CAAC;IAEF,gBAAgB,EAAE,OAAC,CAAC,MAAM,CAAC;QACvB,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;QAClC,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CACtC,iDAAiD,EACjD,0EAA0E,CAC7E;QACD,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,mBAAmB,CAAC;KACpE,CAAC;IAEF,IAAI,EAAE,CAAC,MAAM,GAAG,GAAG,EAAE,EAAE,CAAC,OAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;IAElE,EAAE,EAAE,OAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;CAC1C,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@titanshield/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TitanShieldAI core SDK — AI-powered audit logging, threat detection & compliance",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"dev": "tsc --watch"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"security",
|
|
13
|
+
"audit",
|
|
14
|
+
"threat-detection",
|
|
15
|
+
"compliance",
|
|
16
|
+
"ai",
|
|
17
|
+
"sdk"
|
|
18
|
+
],
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@google/generative-ai": "^0.21.0",
|
|
22
|
+
"crypto": "*",
|
|
23
|
+
"firebase-admin": "^12.0.0",
|
|
24
|
+
"jsonwebtoken": "^9.0.2",
|
|
25
|
+
"zod": "^3.22.4"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/express": "^4.17.25",
|
|
29
|
+
"@types/jsonwebtoken": "^9.0.5",
|
|
30
|
+
"@types/node": "^20.11.5",
|
|
31
|
+
"typescript": "^5.4.5"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import EventEmitter from 'events';
|
|
2
|
+
import { buildStoredEvent, computeBaselineAnomalyScore } from './audit.js';
|
|
3
|
+
import { ThreatEngine } from './threats.js';
|
|
4
|
+
import { validate, Schemas, z } from './validate.js';
|
|
5
|
+
import { computeComplianceScore } from './compliance.js';
|
|
6
|
+
import {
|
|
7
|
+
AccountLockout, IpReputationEngine, SessionFingerprinter,
|
|
8
|
+
securityHeaders, detectBot, detectAdvancedInjection,
|
|
9
|
+
titanPreventionMiddleware, type PreventionConfig,
|
|
10
|
+
} from './prevent.js';
|
|
11
|
+
import { QuantumAuditChain, QuantumRandom } from './quantum.js';
|
|
12
|
+
import { NaturalLanguageRuleParser } from './nlrules.js';
|
|
13
|
+
import { SecurityDNAProfiler } from './dna.js';
|
|
14
|
+
import { CollectiveDefenseNetwork } from './collective.js';
|
|
15
|
+
import { BattleReportGenerator } from './battle.js';
|
|
16
|
+
import { BiometricAnalyzer } from './biometrics.js';
|
|
17
|
+
import type {
|
|
18
|
+
TitanConfig,
|
|
19
|
+
AuditEvent,
|
|
20
|
+
StoredAuditEvent,
|
|
21
|
+
ThreatAlert,
|
|
22
|
+
ComplianceScore,
|
|
23
|
+
ComplianceStandard,
|
|
24
|
+
ValidationResult,
|
|
25
|
+
} from './types.js';
|
|
26
|
+
|
|
27
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
28
|
+
// TitanShield — Main SDK Class
|
|
29
|
+
// The single entry point developers interact with
|
|
30
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
export class TitanShield extends EventEmitter {
|
|
33
|
+
private config: Required<TitanConfig>;
|
|
34
|
+
private threatEngine: ThreatEngine;
|
|
35
|
+
private eventStore: StoredAuditEvent[] = []; // In-memory store (use Firestore in prod)
|
|
36
|
+
private alertStore: ThreatAlert[] = [];
|
|
37
|
+
private rateMap = new Map<string, number[]>(); // key → [timestamps]
|
|
38
|
+
|
|
39
|
+
// v0.2 Prevention Engine
|
|
40
|
+
readonly lockout: AccountLockout;
|
|
41
|
+
readonly ipEngine: IpReputationEngine;
|
|
42
|
+
readonly fingerprinter: SessionFingerprinter;
|
|
43
|
+
|
|
44
|
+
// v0.3 World-First Modules
|
|
45
|
+
readonly quantum: QuantumAuditChain;
|
|
46
|
+
readonly qrng: QuantumRandom;
|
|
47
|
+
readonly nlRules: NaturalLanguageRuleParser;
|
|
48
|
+
readonly dna: SecurityDNAProfiler;
|
|
49
|
+
readonly collective: CollectiveDefenseNetwork;
|
|
50
|
+
readonly battleReport: BattleReportGenerator;
|
|
51
|
+
readonly biometrics: BiometricAnalyzer;
|
|
52
|
+
|
|
53
|
+
constructor(config: TitanConfig) {
|
|
54
|
+
super();
|
|
55
|
+
this.config = {
|
|
56
|
+
env: 'production',
|
|
57
|
+
geminiApiKey: '',
|
|
58
|
+
firestoreCredentials: {},
|
|
59
|
+
webhookUrl: '',
|
|
60
|
+
...config,
|
|
61
|
+
};
|
|
62
|
+
this.threatEngine = new ThreatEngine(this.config.geminiApiKey || undefined);
|
|
63
|
+
|
|
64
|
+
// v0.2 Prevention layers
|
|
65
|
+
this.lockout = new AccountLockout();
|
|
66
|
+
this.ipEngine = new IpReputationEngine();
|
|
67
|
+
this.fingerprinter = new SessionFingerprinter();
|
|
68
|
+
|
|
69
|
+
// v0.3 World-First Modules
|
|
70
|
+
this.quantum = new QuantumAuditChain();
|
|
71
|
+
this.qrng = new QuantumRandom();
|
|
72
|
+
this.nlRules = new NaturalLanguageRuleParser(this.config.geminiApiKey || undefined);
|
|
73
|
+
this.dna = new SecurityDNAProfiler(config.project);
|
|
74
|
+
this.collective = new CollectiveDefenseNetwork(config.project);
|
|
75
|
+
this.battleReport = new BattleReportGenerator(this.config.geminiApiKey || undefined);
|
|
76
|
+
this.biometrics = new BiometricAnalyzer();
|
|
77
|
+
|
|
78
|
+
console.log(
|
|
79
|
+
`[TitanShield] v0.3 — World's First & Best Security System\n` +
|
|
80
|
+
` QUANTUM: ✅ ML-DSA-65 quantum signatures ✅ QRNG true-random tokens\n` +
|
|
81
|
+
` AI: ✅ Natural language rules ✅ Security DNA profiling\n` +
|
|
82
|
+
` ✅ AI threat detection ✅ AI code scanner ready\n` +
|
|
83
|
+
` NETWORK: ✅ Collective defense network ✅ Auto IP blocklist\n` +
|
|
84
|
+
` HUMAN: ✅ Behavioral biometrics ✅ Invisible bot detection\n` +
|
|
85
|
+
` VIRAL: ✅ Monthly battle reports ✅ GitHub security badge\n` +
|
|
86
|
+
` project: ${config.project} | env: ${this.config.env}`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── Core: Audit Log ────────────────────────────────────────────────────────
|
|
91
|
+
/**
|
|
92
|
+
* Log a security-relevant event.
|
|
93
|
+
* Stores it in the tamper-proof chain and runs AI threat analysis asynchronously.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* await shield.log({ event: 'user.login', userId: 'u123', ip: '1.2.3.4', outcome: 'success' });
|
|
97
|
+
*/
|
|
98
|
+
async log(event: AuditEvent): Promise<StoredAuditEvent> {
|
|
99
|
+
// 1. Baseline anomaly scoring
|
|
100
|
+
const { score, factors } = computeBaselineAnomalyScore(event, this.eventStore.slice(-100));
|
|
101
|
+
|
|
102
|
+
// 2. Build and store the event
|
|
103
|
+
const stored = buildStoredEvent(event, this.config.project, this.config.env, {
|
|
104
|
+
anomalyScore: score,
|
|
105
|
+
});
|
|
106
|
+
this.eventStore.push(stored);
|
|
107
|
+
|
|
108
|
+
// Emit for real-time dashboard streaming
|
|
109
|
+
this.emit('event', stored);
|
|
110
|
+
|
|
111
|
+
// 3. AI threat analysis (async — don't block the caller)
|
|
112
|
+
if (score >= 20 || factors.length > 0) {
|
|
113
|
+
setImmediate(async () => {
|
|
114
|
+
try {
|
|
115
|
+
const { anomalyScore, alert } = await this.threatEngine.analyzeEvent(
|
|
116
|
+
{ ...stored, anomalyScore: score },
|
|
117
|
+
this.eventStore.slice(-50)
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// Update the stored event with AI score
|
|
121
|
+
stored.anomalyScore = anomalyScore;
|
|
122
|
+
stored.threatFlag = alert !== null;
|
|
123
|
+
|
|
124
|
+
if (alert) {
|
|
125
|
+
this.alertStore.push(alert);
|
|
126
|
+
this.emit('threat', alert);
|
|
127
|
+
console.warn(`[TitanShield] 🚨 THREAT: ${alert.severity.toUpperCase()} — ${alert.narrative}`);
|
|
128
|
+
|
|
129
|
+
// v0.2: Auto-block the IP when AI detects a high/critical threat
|
|
130
|
+
const ip = event.ip;
|
|
131
|
+
if (ip && (alert.severity === 'critical' || alert.severity === 'high')) {
|
|
132
|
+
this.ipEngine.autoBlockFromThreat(ip, alert.narrative);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Fire webhook if configured
|
|
136
|
+
if (this.config.webhookUrl) {
|
|
137
|
+
this.sendWebhook(alert).catch(() => { });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} catch (e) {
|
|
141
|
+
console.error('[TitanShield] Threat analysis error:', e);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return stored;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ── Input Validation ───────────────────────────────────────────────────────
|
|
150
|
+
/**
|
|
151
|
+
* Validate and sanitize input against a Zod schema.
|
|
152
|
+
* Automatically strips XSS and SQL injection patterns.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* const result = shield.validate(Schemas.login, req.body);
|
|
156
|
+
* if (!result.valid) return res.status(400).json(result.errors);
|
|
157
|
+
*/
|
|
158
|
+
validate<T>(schema: import('zod').ZodSchema<T>, input: unknown): ValidationResult {
|
|
159
|
+
return validate(schema, input);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ── Account Lockout (v0.2) ────────────────────────────────────────────────
|
|
163
|
+
/**
|
|
164
|
+
* Record a failed login — auto-locks account after maxFailedLogins attempts.
|
|
165
|
+
* Returns { locked, attemptsLeft, message } in plain English.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* const status = shield.recordFailedLogin(userId, req.ip);
|
|
169
|
+
* if (status.locked) return res.status(423).json({ error: status.message });
|
|
170
|
+
*/
|
|
171
|
+
recordFailedLogin(identifier: string, ip?: string) {
|
|
172
|
+
const result = this.lockout.recordFailure(identifier, ip);
|
|
173
|
+
if (result.locked) {
|
|
174
|
+
this.log({
|
|
175
|
+
event: 'user.login_failed',
|
|
176
|
+
userId: identifier,
|
|
177
|
+
ip: ip || 'unknown',
|
|
178
|
+
outcome: 'blocked',
|
|
179
|
+
metadata: { reason: 'account_lockout', attemptsLeft: 0 }
|
|
180
|
+
}).catch(() => { });
|
|
181
|
+
}
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Call on successful login to clear lockout state */
|
|
186
|
+
recordSuccessfulLogin(identifier: string) {
|
|
187
|
+
this.lockout.recordSuccess(identifier);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Check if an account is locked before even attempting auth */
|
|
191
|
+
isAccountLocked(identifier: string) {
|
|
192
|
+
return this.lockout.isLocked(identifier);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ── Express Middlewares (v0.2) ────────────────────────────────────────────
|
|
196
|
+
/**
|
|
197
|
+
* Returns Express security headers middleware.
|
|
198
|
+
* Adds CSP, HSTS, X-Frame-Options, Permissions-Policy, and more.
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* app.use(shield.securityHeadersMiddleware());
|
|
202
|
+
*/
|
|
203
|
+
securityHeadersMiddleware(config?: PreventionConfig) {
|
|
204
|
+
return securityHeaders(config);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Returns comprehensive prevention middleware.
|
|
209
|
+
* Combines IP reputation, bot detection, injection prevention.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* app.use(shield.preventionMiddleware());
|
|
213
|
+
*/
|
|
214
|
+
preventionMiddleware(config?: PreventionConfig) {
|
|
215
|
+
return titanPreventionMiddleware(
|
|
216
|
+
this.lockout,
|
|
217
|
+
this.ipEngine,
|
|
218
|
+
config,
|
|
219
|
+
(event, detail, ip) => {
|
|
220
|
+
this.log({ event: event as any, ip, outcome: 'blocked', metadata: { detail } }).catch(() => { });
|
|
221
|
+
}
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ── Rate Limiting ──────────────────────────────────────────────────────────
|
|
226
|
+
/**
|
|
227
|
+
* Check if a key (IP, userId) has exceeded the rate limit.
|
|
228
|
+
* Returns true if the request should be ALLOWED.
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* if (!shield.rateLimit(req.ip, 10, 60_000)) return res.status(429).end();
|
|
232
|
+
*/
|
|
233
|
+
rateLimit(key: string, maxRequests = 100, windowMs = 60_000): boolean {
|
|
234
|
+
const now = Date.now();
|
|
235
|
+
const timestamps = (this.rateMap.get(key) || []).filter(t => now - t < windowMs);
|
|
236
|
+
timestamps.push(now);
|
|
237
|
+
this.rateMap.set(key, timestamps);
|
|
238
|
+
|
|
239
|
+
if (timestamps.length > maxRequests) {
|
|
240
|
+
// Log the rate limit violation
|
|
241
|
+
this.log({
|
|
242
|
+
event: 'security.ip_blocked', ip: key, outcome: 'blocked',
|
|
243
|
+
metadata: { reason: 'rate_limit', count: timestamps.length, windowMs }
|
|
244
|
+
}).catch(() => { });
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ── Query ──────────────────────────────────────────────────────────────────
|
|
251
|
+
/** Get recent audit events */
|
|
252
|
+
getEvents(limit = 100): StoredAuditEvent[] {
|
|
253
|
+
return this.eventStore.slice(-limit).reverse();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Get active threat alerts */
|
|
257
|
+
getAlerts(includesDismissed = false): ThreatAlert[] {
|
|
258
|
+
return includesDismissed ? this.alertStore : this.alertStore.filter(a => !a.dismissed);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Dismiss a threat alert */
|
|
262
|
+
dismissAlert(alertId: string): boolean {
|
|
263
|
+
const alert = this.alertStore.find(a => a.id === alertId);
|
|
264
|
+
if (alert) { alert.dismissed = true; return true; }
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ── Compliance ─────────────────────────────────────────────────────────────
|
|
269
|
+
/** Compute compliance score across all standards */
|
|
270
|
+
async complianceScore(): Promise<ComplianceScore> {
|
|
271
|
+
return computeComplianceScore(this.eventStore);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ── Stats ──────────────────────────────────────────────────────────────────
|
|
275
|
+
stats() {
|
|
276
|
+
const events = this.eventStore;
|
|
277
|
+
return {
|
|
278
|
+
totalEvents: events.length,
|
|
279
|
+
threats: this.alertStore.filter(a => !a.dismissed).length,
|
|
280
|
+
eventsLast24h: events.filter(e => Date.now() - e.timestamp.getTime() < 86_400_000).length,
|
|
281
|
+
avgAnomalyScore: events.length
|
|
282
|
+
? Math.round(events.reduce((s, e) => s + e.anomalyScore, 0) / events.length)
|
|
283
|
+
: 0,
|
|
284
|
+
topEventTypes: [...events.reduce((m, e) => {
|
|
285
|
+
m.set(e.event, (m.get(e.event) || 0) + 1); return m;
|
|
286
|
+
}, new Map<string, number>()).entries()]
|
|
287
|
+
.sort((a, b) => b[1] - a[1]).slice(0, 5),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// ── Webhook ────────────────────────────────────────────────────────────────
|
|
292
|
+
private async sendWebhook(alert: ThreatAlert): Promise<void> {
|
|
293
|
+
const body = JSON.stringify({
|
|
294
|
+
text: `🚨 *TitanShield Alert* [${alert.severity.toUpperCase()}]\n*${alert.narrative}*\n_Action: ${alert.recommendedAction}_`,
|
|
295
|
+
alert,
|
|
296
|
+
});
|
|
297
|
+
await fetch(this.config.webhookUrl, {
|
|
298
|
+
method: 'POST',
|
|
299
|
+
headers: { 'Content-Type': 'application/json' },
|
|
300
|
+
body,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
package/src/audit.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import type { AuditEvent, StoredAuditEvent } from './types.js';
|
|
3
|
+
|
|
4
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
// Audit Module — Tamper-proof event logging with hash chain
|
|
6
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
// In-memory prev hash (server restarts reset it — use Firestore for persistence)
|
|
9
|
+
let prevHash = '0000000000000000000000000000000000000000000000000000000000000000';
|
|
10
|
+
|
|
11
|
+
export function computeEventHash(prevHash: string, event: AuditEvent, timestamp: Date): string {
|
|
12
|
+
const payload = JSON.stringify({ prev: prevHash, event, ts: timestamp.toISOString() });
|
|
13
|
+
return crypto.createHash('sha256').update(payload).digest('hex');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function buildStoredEvent(
|
|
17
|
+
event: AuditEvent,
|
|
18
|
+
project: string,
|
|
19
|
+
env: string,
|
|
20
|
+
overrides: Partial<StoredAuditEvent> = {}
|
|
21
|
+
): StoredAuditEvent {
|
|
22
|
+
const timestamp = new Date();
|
|
23
|
+
const id = `ts_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
|
|
24
|
+
const hash = computeEventHash(prevHash, event, timestamp);
|
|
25
|
+
prevHash = hash; // advance chain
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
id,
|
|
29
|
+
project,
|
|
30
|
+
env,
|
|
31
|
+
timestamp,
|
|
32
|
+
hash,
|
|
33
|
+
severityScore: 0,
|
|
34
|
+
anomalyScore: 0,
|
|
35
|
+
threatFlag: false,
|
|
36
|
+
...event,
|
|
37
|
+
...overrides,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Anomaly heuristics — upgrades to AI scoring when Gemini key is present
|
|
42
|
+
export function computeBaselineAnomalyScore(
|
|
43
|
+
event: AuditEvent,
|
|
44
|
+
recentEvents: StoredAuditEvent[]
|
|
45
|
+
): { score: number; factors: string[] } {
|
|
46
|
+
const factors: string[] = [];
|
|
47
|
+
let score = 0;
|
|
48
|
+
|
|
49
|
+
// High-risk event types
|
|
50
|
+
const highRiskEvents = ['user.login_failed', 'data.delete', 'data.export', 'admin.user_elevated', 'api.key_revoked'];
|
|
51
|
+
if (highRiskEvents.includes(event.event)) { score += 30; factors.push('high_risk_event'); }
|
|
52
|
+
|
|
53
|
+
// Brute force: >5 failed logins from same IP in last 10 events
|
|
54
|
+
if (event.event === 'user.login_failed' && event.ip) {
|
|
55
|
+
const sameIpFailures = recentEvents.filter(e => e.event === 'user.login_failed' && e.ip === event.ip);
|
|
56
|
+
if (sameIpFailures.length >= 4) { score += 40; factors.push('brute_force_pattern'); }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// High frequency: >20 events from same user in last minute
|
|
60
|
+
if (event.userId) {
|
|
61
|
+
const recentUserEvents = recentEvents.filter(
|
|
62
|
+
e => e.userId === event.userId && Date.now() - e.timestamp.getTime() < 60_000
|
|
63
|
+
);
|
|
64
|
+
if (recentUserEvents.length >= 20) { score += 35; factors.push('high_frequency'); }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Unusual hour (2am-5am UTC)
|
|
68
|
+
const hour = new Date().getUTCHours();
|
|
69
|
+
if (hour >= 2 && hour <= 5) { score += 10; factors.push('unusual_hour'); }
|
|
70
|
+
|
|
71
|
+
// Failed outcome
|
|
72
|
+
if (event.outcome === 'failure') { score += 15; factors.push('failed_outcome'); }
|
|
73
|
+
|
|
74
|
+
return { score: Math.min(score, 100), factors };
|
|
75
|
+
}
|
package/src/auto.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
// @titanshield/auto — World's First Zero-Code Security Auto-Instrumentation
|
|
5
|
+
//
|
|
6
|
+
// HOW TO USE: Just add ONE flag when starting your server. That's it.
|
|
7
|
+
//
|
|
8
|
+
// node -r @titanshield/auto your-server.js
|
|
9
|
+
//
|
|
10
|
+
// OR require it at the very top of your entry file:
|
|
11
|
+
//
|
|
12
|
+
// require('@titanshield/auto'); // ← ONE LINE. Everything else: automatic.
|
|
13
|
+
//
|
|
14
|
+
// TitanShield will automatically:
|
|
15
|
+
// ✅ Log every login, logout, and failed attempt
|
|
16
|
+
// ✅ Block bad guys who try too many times
|
|
17
|
+
// ✅ Clean up dangerous text before it reaches your code
|
|
18
|
+
// ✅ Detect and alert on suspicious patterns
|
|
19
|
+
// ✅ Track compliance events
|
|
20
|
+
//
|
|
21
|
+
// No configuration. No code changes. No PhD required.
|
|
22
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
import { TitanShield } from './TitanShield.js';
|
|
25
|
+
|
|
26
|
+
const DEFAULT_CONFIG = {
|
|
27
|
+
apiKey: process.env.TITANSHIELD_API_KEY || 'ts_auto_key',
|
|
28
|
+
project: process.env.TITANSHIELD_PROJECT || process.env.npm_package_name || 'my-app',
|
|
29
|
+
env: (process.env.NODE_ENV as 'development' | 'staging' | 'production') || 'development',
|
|
30
|
+
geminiApiKey: process.env.GEMINI_API_KEY,
|
|
31
|
+
webhookUrl: process.env.TITANSHIELD_WEBHOOK_URL,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Global singleton — shared across the entire app
|
|
35
|
+
export const shield = new TitanShield(DEFAULT_CONFIG);
|
|
36
|
+
|
|
37
|
+
// ── XSS / SQLi auto-sanitizer ─────────────────────────────────────────────────
|
|
38
|
+
const XSS = /<script[\s\S]*?>[\s\S]*?<\/script>|javascript:|on\w+\s*=|<iframe|<object/gi;
|
|
39
|
+
const SQLI = /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|UNION|EXEC|DECLARE)\b)|(-{2}|\/\*|\*\/)/gi;
|
|
40
|
+
|
|
41
|
+
function deepSanitize(obj: unknown): unknown {
|
|
42
|
+
if (typeof obj === 'string') return obj.replace(XSS, '').replace(SQLI, '').trim();
|
|
43
|
+
if (Array.isArray(obj)) return obj.map(deepSanitize);
|
|
44
|
+
if (obj && typeof obj === 'object') {
|
|
45
|
+
return Object.fromEntries(
|
|
46
|
+
Object.entries(obj as Record<string, unknown>).map(([k, v]) => [k, deepSanitize(v)])
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return obj;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ── Express middleware factory ────────────────────────────────────────────────
|
|
53
|
+
// Call shield.middleware() to get Express middleware, OR use shield.protect(app)
|
|
54
|
+
|
|
55
|
+
export function titanMiddleware() {
|
|
56
|
+
return async function titanShieldMiddleware(req: Request, res: Response, next: NextFunction) {
|
|
57
|
+
const ip = (req.headers['x-forwarded-for'] as string)?.split(',')[0] || req.ip || '0.0.0.0';
|
|
58
|
+
const ua = req.headers['user-agent'] || '';
|
|
59
|
+
|
|
60
|
+
// 1. Auto rate-limit (100 req/min per IP by default)
|
|
61
|
+
const limit = parseInt(process.env.TITANSHIELD_RATE_LIMIT || '100');
|
|
62
|
+
if (!shield.rateLimit(ip, limit, 60_000)) {
|
|
63
|
+
await shield.log({
|
|
64
|
+
event: 'security.ip_blocked', ip, userAgent: ua, outcome: 'blocked',
|
|
65
|
+
metadata: { reason: 'rate_limit', path: req.path }
|
|
66
|
+
});
|
|
67
|
+
res.status(429).json({
|
|
68
|
+
error: 'Slow down! You\'re making too many requests.',
|
|
69
|
+
retryAfter: '60 seconds',
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 2. Auto-sanitize request body (strip XSS + SQLi silently)
|
|
75
|
+
if (req.body && typeof req.body === 'object') {
|
|
76
|
+
req.body = deepSanitize(req.body);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 3. Auto-detect and log auth events from common patterns
|
|
80
|
+
const path = req.path.toLowerCase();
|
|
81
|
+
const isLoginRoute = /\/(login|signin|auth|token)/.test(path);
|
|
82
|
+
const isLogoutRoute = /\/(logout|signout)/.test(path);
|
|
83
|
+
const isSignupRoute = /\/(signup|register|create.?account)/.test(path);
|
|
84
|
+
|
|
85
|
+
// Hook into response to capture outcome
|
|
86
|
+
const originalJson = res.json.bind(res);
|
|
87
|
+
res.json = function (body: unknown): Response {
|
|
88
|
+
const statusCode = res.statusCode;
|
|
89
|
+
const success = statusCode < 400;
|
|
90
|
+
|
|
91
|
+
if (isLoginRoute && req.method === 'POST') {
|
|
92
|
+
const userId = (req.body as Record<string, string>)?.email || (req.body as Record<string, string>)?.username;
|
|
93
|
+
shield.log({
|
|
94
|
+
event: success ? 'user.login' : 'user.login_failed',
|
|
95
|
+
userId, ip, userAgent: ua,
|
|
96
|
+
outcome: success ? 'success' : 'failure',
|
|
97
|
+
metadata: { path: req.path, statusCode }
|
|
98
|
+
}).catch(() => { });
|
|
99
|
+
} else if (isLogoutRoute && success) {
|
|
100
|
+
shield.log({ event: 'user.logout', ip, userAgent: ua, outcome: 'success' }).catch(() => { });
|
|
101
|
+
} else if (isSignupRoute && req.method === 'POST' && success) {
|
|
102
|
+
const userId = (req.body as Record<string, string>)?.email;
|
|
103
|
+
shield.log({ event: 'user.signup', userId, ip, userAgent: ua, outcome: 'success' }).catch(() => { });
|
|
104
|
+
} else if (!success && statusCode >= 500) {
|
|
105
|
+
// Log server errors as security-relevant events
|
|
106
|
+
shield.log({
|
|
107
|
+
event: 'security.threat_detected', ip, userAgent: ua, outcome: 'failure',
|
|
108
|
+
metadata: { statusCode, path: req.path, method: req.method }
|
|
109
|
+
}).catch(() => { });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return originalJson(body);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
next();
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── protect(app) — Wraps an existing Express app with ONE call ────────────────
|
|
120
|
+
export function protect(app: { use: (...args: unknown[]) => unknown }) {
|
|
121
|
+
app.use(titanMiddleware());
|
|
122
|
+
console.log(`\n🛡️ [TitanShieldAI] Auto-protection active!`);
|
|
123
|
+
console.log(` Project: ${DEFAULT_CONFIG.project} | Env: ${DEFAULT_CONFIG.env}`);
|
|
124
|
+
console.log(` ✅ Rate limiting: ON ✅ XSS/SQLi sanitization: ON ✅ Audit logging: ON`);
|
|
125
|
+
console.log(` ✅ AI threat detection: ${DEFAULT_CONFIG.geminiApiKey ? 'ON (Gemini)' : 'ON (heuristic)'}`);
|
|
126
|
+
console.log(` Dashboard: run 'titanshield dashboard' to view\n`);
|
|
127
|
+
return app;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// When required as -r flag, print startup banner
|
|
131
|
+
console.log(`\n🛡️ TitanShieldAI auto-instrumentation loaded.`);
|
|
132
|
+
console.log(` Call shield.protect(app) to activate — or use titanMiddleware() in your routes.`);
|
|
133
|
+
console.log(` Zero config needed. Titan tough. Shield strong.\n`);
|
|
134
|
+
|
|
135
|
+
// Re-export shield instance for direct use
|
|
136
|
+
export { TitanShield } from './TitanShield.js';
|
|
137
|
+
export { Schemas } from './validate.js';
|