cipher-security 2.0.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/bin/cipher.js +566 -0
- package/lib/api/billing.js +321 -0
- package/lib/api/compliance.js +693 -0
- package/lib/api/controls.js +1401 -0
- package/lib/api/index.js +49 -0
- package/lib/api/marketplace.js +467 -0
- package/lib/api/openai-proxy.js +383 -0
- package/lib/api/server.js +685 -0
- package/lib/autonomous/feedback-loop.js +554 -0
- package/lib/autonomous/framework.js +512 -0
- package/lib/autonomous/index.js +97 -0
- package/lib/autonomous/leaderboard.js +594 -0
- package/lib/autonomous/modes/architect.js +412 -0
- package/lib/autonomous/modes/blue.js +386 -0
- package/lib/autonomous/modes/incident.js +684 -0
- package/lib/autonomous/modes/privacy.js +369 -0
- package/lib/autonomous/modes/purple.js +294 -0
- package/lib/autonomous/modes/recon.js +250 -0
- package/lib/autonomous/parallel.js +587 -0
- package/lib/autonomous/researcher.js +583 -0
- package/lib/autonomous/runner.js +955 -0
- package/lib/autonomous/scheduler.js +615 -0
- package/lib/autonomous/task-parser.js +127 -0
- package/lib/autonomous/validators/forensic.js +266 -0
- package/lib/autonomous/validators/osint.js +216 -0
- package/lib/autonomous/validators/privacy.js +296 -0
- package/lib/autonomous/validators/purple.js +298 -0
- package/lib/autonomous/validators/sigma.js +248 -0
- package/lib/autonomous/validators/threat-model.js +363 -0
- package/lib/benchmark/agent.js +119 -0
- package/lib/benchmark/baselines.js +43 -0
- package/lib/benchmark/builder.js +143 -0
- package/lib/benchmark/config.js +35 -0
- package/lib/benchmark/coordinator.js +91 -0
- package/lib/benchmark/index.js +20 -0
- package/lib/benchmark/llm.js +58 -0
- package/lib/benchmark/models.js +137 -0
- package/lib/benchmark/reporter.js +103 -0
- package/lib/benchmark/runner.js +103 -0
- package/lib/benchmark/sandbox.js +96 -0
- package/lib/benchmark/scorer.js +32 -0
- package/lib/benchmark/solver.js +166 -0
- package/lib/benchmark/tools.js +62 -0
- package/lib/bot/bot.js +238 -0
- package/lib/brand.js +105 -0
- package/lib/commands.js +100 -0
- package/lib/complexity.js +377 -0
- package/lib/config.js +213 -0
- package/lib/gateway/client.js +309 -0
- package/lib/gateway/commands.js +991 -0
- package/lib/gateway/config-validate.js +109 -0
- package/lib/gateway/gateway.js +367 -0
- package/lib/gateway/index.js +62 -0
- package/lib/gateway/mode.js +309 -0
- package/lib/gateway/plugins.js +222 -0
- package/lib/gateway/prompt.js +214 -0
- package/lib/mcp/server.js +262 -0
- package/lib/memory/compressor.js +425 -0
- package/lib/memory/engine.js +763 -0
- package/lib/memory/evolution.js +668 -0
- package/lib/memory/index.js +58 -0
- package/lib/memory/orchestrator.js +506 -0
- package/lib/memory/retriever.js +515 -0
- package/lib/memory/synthesizer.js +333 -0
- package/lib/pipeline/async-scanner.js +510 -0
- package/lib/pipeline/binary-analysis.js +1043 -0
- package/lib/pipeline/dom-xss-scanner.js +435 -0
- package/lib/pipeline/github-actions.js +792 -0
- package/lib/pipeline/index.js +124 -0
- package/lib/pipeline/osint.js +498 -0
- package/lib/pipeline/sarif.js +373 -0
- package/lib/pipeline/scanner.js +880 -0
- package/lib/pipeline/template-manager.js +525 -0
- package/lib/pipeline/xss-scanner.js +353 -0
- package/lib/setup-wizard.js +288 -0
- package/package.json +31 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
// Copyright (c) 2026 defconxt. All rights reserved.
|
|
2
|
+
// Licensed under AGPL-3.0 — see LICENSE file for details.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* CIPHER Billing Engine — usage-based metering middleware.
|
|
6
|
+
*
|
|
7
|
+
* Tracks API usage per client via SQLite: request counting,
|
|
8
|
+
* credit consumption, tier enforcement, and quota checks.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
import { join, dirname } from 'node:path';
|
|
13
|
+
import { mkdirSync } from 'node:fs';
|
|
14
|
+
|
|
15
|
+
/** @type {typeof import('better-sqlite3')} */
|
|
16
|
+
let Database;
|
|
17
|
+
|
|
18
|
+
/** Billing plan tiers. */
|
|
19
|
+
export const BillingTier = Object.freeze({
|
|
20
|
+
FREE: 'free',
|
|
21
|
+
STARTER: 'starter',
|
|
22
|
+
PROFESSIONAL: 'professional',
|
|
23
|
+
ENTERPRISE: 'enterprise',
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Tier limits keyed by BillingTier value.
|
|
28
|
+
* Each entry: { requestsPerHour, scansPerDay, memoryEntries, skillSearchesPerHour,
|
|
29
|
+
* complianceReportsPerDay, marketplaceDownloadsPerDay, maxResponseSizeKb }
|
|
30
|
+
*/
|
|
31
|
+
export const TIER_LIMITS = {
|
|
32
|
+
[BillingTier.FREE]: {
|
|
33
|
+
requestsPerHour: 60,
|
|
34
|
+
scansPerDay: 3,
|
|
35
|
+
memoryEntries: 100,
|
|
36
|
+
skillSearchesPerHour: 20,
|
|
37
|
+
complianceReportsPerDay: 1,
|
|
38
|
+
marketplaceDownloadsPerDay: 5,
|
|
39
|
+
maxResponseSizeKb: 256,
|
|
40
|
+
},
|
|
41
|
+
[BillingTier.STARTER]: {
|
|
42
|
+
requestsPerHour: 500,
|
|
43
|
+
scansPerDay: 20,
|
|
44
|
+
memoryEntries: 5000,
|
|
45
|
+
skillSearchesPerHour: 200,
|
|
46
|
+
complianceReportsPerDay: 10,
|
|
47
|
+
marketplaceDownloadsPerDay: 50,
|
|
48
|
+
maxResponseSizeKb: 1024,
|
|
49
|
+
},
|
|
50
|
+
[BillingTier.PROFESSIONAL]: {
|
|
51
|
+
requestsPerHour: 5000,
|
|
52
|
+
scansPerDay: 100,
|
|
53
|
+
memoryEntries: 50000,
|
|
54
|
+
skillSearchesPerHour: 2000,
|
|
55
|
+
complianceReportsPerDay: 50,
|
|
56
|
+
marketplaceDownloadsPerDay: 500,
|
|
57
|
+
maxResponseSizeKb: 4096,
|
|
58
|
+
},
|
|
59
|
+
[BillingTier.ENTERPRISE]: {
|
|
60
|
+
requestsPerHour: 50000,
|
|
61
|
+
scansPerDay: 1000,
|
|
62
|
+
memoryEntries: 500000,
|
|
63
|
+
skillSearchesPerHour: 20000,
|
|
64
|
+
complianceReportsPerDay: 500,
|
|
65
|
+
marketplaceDownloadsPerDay: 5000,
|
|
66
|
+
maxResponseSizeKb: 16384,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/** Credit costs per endpoint type. */
|
|
71
|
+
export const ENDPOINT_CREDITS = {
|
|
72
|
+
scan: 10.0,
|
|
73
|
+
diff: 2.0,
|
|
74
|
+
secrets: 2.0,
|
|
75
|
+
'memory/store': 1.0,
|
|
76
|
+
'memory/search': 1.0,
|
|
77
|
+
score: 5.0,
|
|
78
|
+
compliance: 5.0,
|
|
79
|
+
workflow: 3.0,
|
|
80
|
+
skills: 0.5,
|
|
81
|
+
'skills/search': 0.5,
|
|
82
|
+
leaderboard: 0.5,
|
|
83
|
+
health: 0.0,
|
|
84
|
+
stats: 0.0,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Usage-based billing and metering engine backed by SQLite.
|
|
89
|
+
*/
|
|
90
|
+
export class MeteringEngine {
|
|
91
|
+
/**
|
|
92
|
+
* @param {string} [dbPath] - Path to SQLite database file. Default: ~/.cipher/billing.db
|
|
93
|
+
*/
|
|
94
|
+
constructor(dbPath) {
|
|
95
|
+
if (!Database) {
|
|
96
|
+
Database = require('better-sqlite3');
|
|
97
|
+
}
|
|
98
|
+
const resolved = dbPath || join(homedir(), '.cipher', 'billing.db');
|
|
99
|
+
mkdirSync(dirname(resolved), { recursive: true });
|
|
100
|
+
this._db = new Database(resolved);
|
|
101
|
+
this._db.pragma('journal_mode = WAL');
|
|
102
|
+
this._clientTiers = new Map();
|
|
103
|
+
this._initDb();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
_initDb() {
|
|
107
|
+
this._db.exec(`
|
|
108
|
+
CREATE TABLE IF NOT EXISTS usage_events (
|
|
109
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
110
|
+
client_id TEXT NOT NULL,
|
|
111
|
+
endpoint TEXT NOT NULL,
|
|
112
|
+
credits REAL NOT NULL DEFAULT 1.0,
|
|
113
|
+
response_bytes INTEGER DEFAULT 0,
|
|
114
|
+
timestamp REAL NOT NULL,
|
|
115
|
+
metadata TEXT DEFAULT '{}'
|
|
116
|
+
);
|
|
117
|
+
CREATE INDEX IF NOT EXISTS idx_usage_client_ts
|
|
118
|
+
ON usage_events(client_id, timestamp);
|
|
119
|
+
CREATE INDEX IF NOT EXISTS idx_usage_endpoint
|
|
120
|
+
ON usage_events(endpoint, timestamp);
|
|
121
|
+
|
|
122
|
+
CREATE TABLE IF NOT EXISTS client_tiers (
|
|
123
|
+
client_id TEXT PRIMARY KEY,
|
|
124
|
+
tier TEXT NOT NULL DEFAULT 'free',
|
|
125
|
+
created_at REAL NOT NULL,
|
|
126
|
+
updated_at REAL NOT NULL
|
|
127
|
+
);
|
|
128
|
+
`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Set or update a client's billing tier.
|
|
133
|
+
* @param {string} clientId
|
|
134
|
+
* @param {string} tier - BillingTier value
|
|
135
|
+
*/
|
|
136
|
+
setClientTier(clientId, tier) {
|
|
137
|
+
const now = Date.now() / 1000;
|
|
138
|
+
this._db
|
|
139
|
+
.prepare(
|
|
140
|
+
`INSERT INTO client_tiers (client_id, tier, created_at, updated_at)
|
|
141
|
+
VALUES (?, ?, ?, ?)
|
|
142
|
+
ON CONFLICT(client_id) DO UPDATE SET tier=?, updated_at=?`,
|
|
143
|
+
)
|
|
144
|
+
.run(clientId, tier, now, now, tier, now);
|
|
145
|
+
this._clientTiers.set(clientId, tier);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get a client's billing tier.
|
|
150
|
+
* @param {string} clientId
|
|
151
|
+
* @returns {string} BillingTier value
|
|
152
|
+
*/
|
|
153
|
+
getClientTier(clientId) {
|
|
154
|
+
if (this._clientTiers.has(clientId)) {
|
|
155
|
+
return this._clientTiers.get(clientId);
|
|
156
|
+
}
|
|
157
|
+
const row = this._db
|
|
158
|
+
.prepare('SELECT tier FROM client_tiers WHERE client_id = ?')
|
|
159
|
+
.get(clientId);
|
|
160
|
+
const tier = row ? row.tier : BillingTier.FREE;
|
|
161
|
+
this._clientTiers.set(clientId, tier);
|
|
162
|
+
return tier;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Record a usage event.
|
|
167
|
+
* @param {object} record
|
|
168
|
+
* @param {string} record.clientId
|
|
169
|
+
* @param {string} record.endpoint
|
|
170
|
+
* @param {number} [record.timestamp]
|
|
171
|
+
* @param {number} [record.creditsConsumed]
|
|
172
|
+
* @param {number} [record.responseSizeBytes]
|
|
173
|
+
* @param {object} [record.metadata]
|
|
174
|
+
*/
|
|
175
|
+
recordUsage(record) {
|
|
176
|
+
const ts = record.timestamp ?? Date.now() / 1000;
|
|
177
|
+
const credits = record.creditsConsumed ?? 1.0;
|
|
178
|
+
this._db
|
|
179
|
+
.prepare(
|
|
180
|
+
`INSERT INTO usage_events (client_id, endpoint, credits, response_bytes, timestamp, metadata)
|
|
181
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
182
|
+
)
|
|
183
|
+
.run(
|
|
184
|
+
record.clientId,
|
|
185
|
+
record.endpoint,
|
|
186
|
+
credits,
|
|
187
|
+
record.responseSizeBytes ?? 0,
|
|
188
|
+
ts,
|
|
189
|
+
JSON.stringify(record.metadata ?? {}),
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Check if a client has quota remaining for an endpoint.
|
|
195
|
+
* @param {string} clientId
|
|
196
|
+
* @param {string} endpoint
|
|
197
|
+
* @returns {{ allowed: boolean, remaining: number, limit: number, resetsInS: number, reason?: string }}
|
|
198
|
+
*/
|
|
199
|
+
checkQuota(clientId, endpoint) {
|
|
200
|
+
const tier = this.getClientTier(clientId);
|
|
201
|
+
const limits = TIER_LIMITS[tier] || TIER_LIMITS[BillingTier.FREE];
|
|
202
|
+
const now = Date.now() / 1000;
|
|
203
|
+
const hourAgo = now - 3600;
|
|
204
|
+
const dayAgo = now - 86400;
|
|
205
|
+
|
|
206
|
+
// Hourly request limit
|
|
207
|
+
const hourlyRow = this._db
|
|
208
|
+
.prepare('SELECT COUNT(*) as cnt FROM usage_events WHERE client_id = ? AND timestamp > ?')
|
|
209
|
+
.get(clientId, hourAgo);
|
|
210
|
+
const hourlyCount = hourlyRow?.cnt ?? 0;
|
|
211
|
+
|
|
212
|
+
if (hourlyCount >= limits.requestsPerHour) {
|
|
213
|
+
return {
|
|
214
|
+
allowed: false,
|
|
215
|
+
remaining: 0,
|
|
216
|
+
limit: limits.requestsPerHour,
|
|
217
|
+
resetsInS: 3600,
|
|
218
|
+
reason: 'hourly request limit exceeded',
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Scan-specific daily limit
|
|
223
|
+
if (endpoint === 'scan') {
|
|
224
|
+
const scanRow = this._db
|
|
225
|
+
.prepare(
|
|
226
|
+
"SELECT COUNT(*) as cnt FROM usage_events WHERE client_id = ? AND endpoint = 'scan' AND timestamp > ?",
|
|
227
|
+
)
|
|
228
|
+
.get(clientId, dayAgo);
|
|
229
|
+
const dailyScans = scanRow?.cnt ?? 0;
|
|
230
|
+
if (dailyScans >= limits.scansPerDay) {
|
|
231
|
+
return {
|
|
232
|
+
allowed: false,
|
|
233
|
+
remaining: 0,
|
|
234
|
+
limit: limits.scansPerDay,
|
|
235
|
+
resetsInS: 86400,
|
|
236
|
+
reason: 'daily scan limit exceeded',
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
allowed: true,
|
|
241
|
+
remaining: limits.scansPerDay - dailyScans,
|
|
242
|
+
limit: limits.scansPerDay,
|
|
243
|
+
resetsInS: 86400,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
allowed: true,
|
|
249
|
+
remaining: limits.requestsPerHour - hourlyCount,
|
|
250
|
+
limit: limits.requestsPerHour,
|
|
251
|
+
resetsInS: 3600,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Get usage summary for a client over a time period.
|
|
257
|
+
* @param {string} clientId
|
|
258
|
+
* @param {number} [periodHours=24]
|
|
259
|
+
* @returns {object}
|
|
260
|
+
*/
|
|
261
|
+
getUsageSummary(clientId, periodHours = 24) {
|
|
262
|
+
const tier = this.getClientTier(clientId);
|
|
263
|
+
const now = Date.now() / 1000;
|
|
264
|
+
const periodStart = now - periodHours * 3600;
|
|
265
|
+
|
|
266
|
+
const rows = this._db
|
|
267
|
+
.prepare(
|
|
268
|
+
`SELECT endpoint, COUNT(*) as cnt, SUM(credits) as total_credits
|
|
269
|
+
FROM usage_events
|
|
270
|
+
WHERE client_id = ? AND timestamp > ?
|
|
271
|
+
GROUP BY endpoint`,
|
|
272
|
+
)
|
|
273
|
+
.all(clientId, periodStart);
|
|
274
|
+
|
|
275
|
+
const byEndpoint = {};
|
|
276
|
+
let totalRequests = 0;
|
|
277
|
+
let totalCredits = 0;
|
|
278
|
+
let totalScans = 0;
|
|
279
|
+
|
|
280
|
+
for (const row of rows) {
|
|
281
|
+
byEndpoint[row.endpoint] = row.cnt;
|
|
282
|
+
totalRequests += row.cnt;
|
|
283
|
+
totalCredits += row.total_credits;
|
|
284
|
+
if (row.endpoint === 'scan') totalScans = row.cnt;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const limits = TIER_LIMITS[tier] || TIER_LIMITS[BillingTier.FREE];
|
|
288
|
+
return {
|
|
289
|
+
clientId,
|
|
290
|
+
tier,
|
|
291
|
+
periodStart,
|
|
292
|
+
periodEnd: now,
|
|
293
|
+
totalRequests,
|
|
294
|
+
totalScans,
|
|
295
|
+
totalCredits,
|
|
296
|
+
byEndpoint,
|
|
297
|
+
quotaRemaining: {
|
|
298
|
+
requestsHourly: Math.max(0, limits.requestsPerHour - totalRequests),
|
|
299
|
+
scansDaily: Math.max(0, limits.scansPerDay - totalScans),
|
|
300
|
+
},
|
|
301
|
+
overage: totalRequests > limits.requestsPerHour,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get the credit cost for an endpoint.
|
|
307
|
+
* @param {string} endpoint
|
|
308
|
+
* @returns {number}
|
|
309
|
+
*/
|
|
310
|
+
getCreditsForEndpoint(endpoint) {
|
|
311
|
+
for (const [ep, credits] of Object.entries(ENDPOINT_CREDITS)) {
|
|
312
|
+
if (endpoint.endsWith(ep)) return credits;
|
|
313
|
+
}
|
|
314
|
+
return 1.0;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/** Close database connection. */
|
|
318
|
+
close() {
|
|
319
|
+
this._db.close();
|
|
320
|
+
}
|
|
321
|
+
}
|