Skip to content

Commit bc925b8

Browse files
author
jonnyamsp
committed
chore: add AI Security SDK v1.0.0
1 parent 09d7cee commit bc925b8

1 file changed

Lines changed: 218 additions & 0 deletions

File tree

lib/ai-sec-sdk.ts

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/**
2+
* AI Security SDK for Runtime Telemetry
3+
* Auto-bundled by Agents360
4+
*/
5+
6+
// SDK Version - used to check if SDK needs updating
7+
export const SDK_VERSION = "1.0.0";
8+
9+
export interface AgentSecurityConfig {
10+
projectId?: string;
11+
ingestionUrl?: string;
12+
agentKey?: string;
13+
agentId?: string;
14+
fetchPoliciesIntervalMs?: number;
15+
}
16+
17+
export interface LlmCallContext {
18+
provider: string;
19+
model?: string;
20+
region?: string;
21+
route?: string;
22+
tenantId?: string;
23+
prompt: string;
24+
response?: string;
25+
}
26+
27+
export interface HttpCallContext {
28+
host: string;
29+
path?: string;
30+
ipAddress?: string;
31+
route?: string;
32+
tenantId?: string;
33+
payloadSample?: string;
34+
}
35+
36+
export interface LogEventContext {
37+
level: "debug" | "info" | "warn" | "error";
38+
message: string;
39+
route?: string;
40+
tenantId?: string;
41+
}
42+
43+
export interface PolicyViolationContext {
44+
violationType: string;
45+
severity: "low" | "medium" | "high" | "critical";
46+
route?: string;
47+
tenantId?: string;
48+
details?: Record<string, unknown>;
49+
}
50+
51+
interface PiiFlags {
52+
email: boolean;
53+
phone: boolean;
54+
generic_id: boolean;
55+
credit_card?: boolean;
56+
ssn?: boolean;
57+
address?: boolean;
58+
}
59+
60+
interface SecretFlags {
61+
api_key_pattern: boolean;
62+
bearer_token?: boolean;
63+
password_pattern?: boolean;
64+
}
65+
66+
interface TelemetryPayload {
67+
catalog_agent_id?: string;
68+
event_type: string;
69+
provider?: string;
70+
model?: string;
71+
region?: string;
72+
host?: string;
73+
path?: string;
74+
ip_address?: string;
75+
route?: string;
76+
tenant_id?: string;
77+
prompt_length?: number;
78+
response_length?: number;
79+
pii_flags?: PiiFlags | Record<string, boolean>;
80+
secret_flags?: SecretFlags | Record<string, boolean>;
81+
policy_flags?: Record<string, boolean>;
82+
metadata?: Record<string, unknown>;
83+
}
84+
85+
let currentConfig: AgentSecurityConfig | null = null;
86+
let policyFetchInterval: ReturnType<typeof setInterval> | null = null;
87+
88+
function classifyTextForPii(text: string): PiiFlags {
89+
if (!text) return { email: false, phone: false, generic_id: false };
90+
return {
91+
email: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i.test(text),
92+
phone: /\+?\d[\d\s\-()]{7,}/.test(text),
93+
generic_id: /\b\d{8,}\b/.test(text),
94+
credit_card: /\b(?:\d{4}[-\s]?){3}\d{4}\b/.test(text),
95+
ssn: /\b\d{3}[-\s]?\d{2}[-\s]?\d{4}\b/.test(text),
96+
address: /\b\d+\s+[\w\s]+(?:street|st|avenue|ave|road|rd|drive|dr|lane|ln|way|court|ct|boulevard|blvd)\b/i.test(text),
97+
};
98+
}
99+
100+
function classifyTextForSecrets(text: string): SecretFlags {
101+
if (!text) return { api_key_pattern: false };
102+
return {
103+
api_key_pattern: /(sk-[A-Za-z0-9]{20,}|api[_-]?key\s*[:=]\s*['"]?[A-Za-z0-9]{16,})/i.test(text),
104+
bearer_token: /bearer\s+[A-Za-z0-9\-._~+\/]+=*/i.test(text),
105+
password_pattern: /(password|passwd|pwd)\s*[:=]\s*['"][^'"]{4,}/i.test(text),
106+
};
107+
}
108+
109+
function getConfigOrThrow(): AgentSecurityConfig {
110+
if (!currentConfig) throw new Error("[ai-sec-sdk] SDK not initialized. Call initAgentSecurity() first.");
111+
if (!currentConfig.ingestionUrl) throw new Error("[ai-sec-sdk] Missing ingestionUrl.");
112+
if (!currentConfig.agentKey) throw new Error("[ai-sec-sdk] Missing agentKey.");
113+
return currentConfig;
114+
}
115+
116+
function startPolicyFetchLoop(intervalMs: number): void {
117+
if (policyFetchInterval) clearInterval(policyFetchInterval);
118+
policyFetchInterval = setInterval(async () => {
119+
try {
120+
console.debug("[ai-sec-sdk] Policy fetch placeholder");
121+
} catch (err) {
122+
console.error("[ai-sec-sdk] Failed to fetch policies", err);
123+
}
124+
}, intervalMs);
125+
}
126+
127+
async function sendTelemetry(eventPayload: TelemetryPayload): Promise<void> {
128+
const cfg = getConfigOrThrow();
129+
if (cfg.agentId && !eventPayload.catalog_agent_id) eventPayload.catalog_agent_id = cfg.agentId;
130+
try {
131+
const response = await fetch(cfg.ingestionUrl!, {
132+
method: "POST",
133+
headers: {
134+
"Content-Type": "application/json",
135+
"X-Agent-Key": cfg.agentKey || "",
136+
"X-Project-Id": cfg.projectId || "",
137+
},
138+
body: JSON.stringify(eventPayload),
139+
});
140+
if (!response.ok) console.error("[ai-sec-sdk] Telemetry request failed:", response.status);
141+
} catch (err) {
142+
console.error("[ai-sec-sdk] Failed to send telemetry", err);
143+
}
144+
}
145+
146+
export function initAgentSecurity(config?: AgentSecurityConfig): void {
147+
const envConfig: AgentSecurityConfig = {
148+
projectId: typeof process !== 'undefined' ? process.env?.AI_SEC_PROJECT_ID : undefined,
149+
ingestionUrl: typeof process !== 'undefined' ? process.env?.AI_SEC_INGEST_URL : undefined,
150+
agentKey: typeof process !== 'undefined' ? process.env?.AI_SEC_AGENT_KEY : undefined,
151+
agentId: typeof process !== 'undefined' ? process.env?.AI_SEC_AGENT_ID : undefined,
152+
fetchPoliciesIntervalMs: 60000,
153+
};
154+
currentConfig = { ...envConfig, ...config };
155+
console.log("[ai-sec-sdk] Initialized");
156+
if (currentConfig.fetchPoliciesIntervalMs && currentConfig.fetchPoliciesIntervalMs > 0) {
157+
startPolicyFetchLoop(currentConfig.fetchPoliciesIntervalMs);
158+
}
159+
}
160+
161+
export async function recordLlmCall(ctx: LlmCallContext): Promise<void> {
162+
await sendTelemetry({
163+
event_type: "llm_call",
164+
provider: ctx.provider,
165+
model: ctx.model,
166+
region: ctx.region,
167+
route: ctx.route,
168+
tenant_id: ctx.tenantId,
169+
prompt_length: ctx.prompt.length,
170+
response_length: ctx.response?.length,
171+
pii_flags: classifyTextForPii(ctx.prompt),
172+
secret_flags: classifyTextForSecrets(ctx.prompt),
173+
});
174+
}
175+
176+
export async function recordHttpCall(ctx: HttpCallContext): Promise<void> {
177+
await sendTelemetry({
178+
event_type: "http_call",
179+
host: ctx.host,
180+
path: ctx.path,
181+
ip_address: ctx.ipAddress,
182+
route: ctx.route,
183+
tenant_id: ctx.tenantId,
184+
pii_flags: ctx.payloadSample ? classifyTextForPii(ctx.payloadSample) : {},
185+
secret_flags: ctx.payloadSample ? classifyTextForSecrets(ctx.payloadSample) : {},
186+
});
187+
}
188+
189+
export async function recordLogEvent(ctx: LogEventContext): Promise<void> {
190+
await sendTelemetry({
191+
event_type: "log_event",
192+
route: ctx.route,
193+
tenant_id: ctx.tenantId,
194+
metadata: {
195+
level: ctx.level,
196+
pii_flags: classifyTextForPii(ctx.message),
197+
secret_flags: classifyTextForSecrets(ctx.message),
198+
},
199+
});
200+
}
201+
202+
export async function recordPolicyViolation(ctx: PolicyViolationContext): Promise<void> {
203+
await sendTelemetry({
204+
event_type: "policy_violation",
205+
route: ctx.route,
206+
tenant_id: ctx.tenantId,
207+
policy_flags: { [ctx.violationType]: true },
208+
metadata: { severity: ctx.severity, details: ctx.details },
209+
});
210+
}
211+
212+
export function shutdownAgentSecurity(): void {
213+
if (policyFetchInterval) { clearInterval(policyFetchInterval); policyFetchInterval = null; }
214+
currentConfig = null;
215+
console.log("[ai-sec-sdk] Shutdown complete");
216+
}
217+
218+
export { classifyTextForPii, classifyTextForSecrets };

0 commit comments

Comments
 (0)