@basaltkit/ai 0.9.2 → 0.10.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/index.d.ts +2 -0
- package/dist/index.js +44 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -275,6 +275,8 @@ interface AppFileInfo {
|
|
|
275
275
|
fastifyLoggerConfigured: boolean;
|
|
276
276
|
/** In-memory (non-durable) sources still wired, e.g. `MemoryTenantSource`. */
|
|
277
277
|
memorySources: string[];
|
|
278
|
+
/** Every `<name>Plugin(` factory call in the app file (unfiltered by capability). */
|
|
279
|
+
pluginCalls: string[];
|
|
278
280
|
}
|
|
279
281
|
interface ServerFileInfo {
|
|
280
282
|
path: string;
|
package/dist/index.js
CHANGED
|
@@ -367,7 +367,10 @@ function detectAppFile(reader) {
|
|
|
367
367
|
const memorySources = ["MemoryTenantSource", "MemoryUserSource"].filter(
|
|
368
368
|
(source) => found.content.includes(source)
|
|
369
369
|
);
|
|
370
|
-
|
|
370
|
+
const pluginCalls = [
|
|
371
|
+
...new Set(Array.from(found.content.matchAll(/\b([A-Za-z][A-Za-z0-9]*Plugin)\s*\(/g), (m) => m[1]))
|
|
372
|
+
];
|
|
373
|
+
return { path: found.path, plugins, fastifyLoggerConfigured, memorySources, pluginCalls };
|
|
371
374
|
}
|
|
372
375
|
function detectServerFile(reader) {
|
|
373
376
|
const found = firstExisting(reader, SERVER_CANDIDATES);
|
|
@@ -527,7 +530,47 @@ var DEFAULT_RULES = [
|
|
|
527
530
|
};
|
|
528
531
|
}
|
|
529
532
|
}),
|
|
533
|
+
defineRule({
|
|
534
|
+
id: "missing-security-plugin",
|
|
535
|
+
category: "security",
|
|
536
|
+
check(ctx) {
|
|
537
|
+
if (!ctx.app) return null;
|
|
538
|
+
if (ctx.app.pluginCalls.includes("securityPlugin")) return null;
|
|
539
|
+
return {
|
|
540
|
+
id: "missing-security-plugin",
|
|
541
|
+
title: "No securityPlugin \u2014 responses ship without secure headers",
|
|
542
|
+
severity: "warning",
|
|
543
|
+
category: "security",
|
|
544
|
+
detected: `${ctx.app.path} registers no securityPlugin()`,
|
|
545
|
+
recommended: "add securityPlugin() so secure response headers are set, and enable rate limiting + a CORS allow-list for production",
|
|
546
|
+
reason: "Without it the API returns no security headers (HSTS, X-Frame-Options, nosniff, \u2026) and applies no rate limiting \u2014 a fresh deploy is unprotected at the edge. The security primitives exist but are off until wired.",
|
|
547
|
+
fix: "securityPlugin({ /* headers on by default */ rateLimit: { limit: 120, windowMs: 60_000 } })",
|
|
548
|
+
docs: "/guide/security"
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
}),
|
|
530
552
|
// ── tenancy ───────────────────────────────────────────────────────────────
|
|
553
|
+
defineRule({
|
|
554
|
+
id: "missing-tenant-membership",
|
|
555
|
+
category: "tenancy",
|
|
556
|
+
check(ctx) {
|
|
557
|
+
if (!ctx.app || !ctx.stack.tenancy || !ctx.stack.auth) return null;
|
|
558
|
+
const teamsAvailable = ctx.app.pluginCalls.includes("teamsPlugin") || ctx.installed.includes("@basaltkit/teams");
|
|
559
|
+
if (!teamsAvailable) return null;
|
|
560
|
+
if (ctx.app.pluginCalls.includes("tenantMembershipPlugin")) return null;
|
|
561
|
+
return {
|
|
562
|
+
id: "missing-tenant-membership",
|
|
563
|
+
title: "Tenant is resolved from the request but membership is never enforced",
|
|
564
|
+
severity: "error",
|
|
565
|
+
category: "tenancy",
|
|
566
|
+
detected: `${ctx.app.path} wires tenancy + auth (+ teams) but no tenantMembershipPlugin`,
|
|
567
|
+
recommended: "register tenantMembershipPlugin() so every authenticated, tenant-scoped request verifies the user belongs to the resolved tenant",
|
|
568
|
+
reason: "Resolvers take the tenant from client input (x-tenant-id / Host). Without a membership check, any authenticated user can act on another tenant just by changing that header \u2014 a cross-tenant data breach. Tenant resolution is identification, never authorization.",
|
|
569
|
+
fix: "tenantMembershipPlugin() // central routes opt out with meta: { central: true }",
|
|
570
|
+
docs: "/guide/security"
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
}),
|
|
531
574
|
defineRule({
|
|
532
575
|
id: "tenant-scoping-missing",
|
|
533
576
|
category: "tenancy",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basaltkit/ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "AI-native developer experience for Basalt: a provider-agnostic engine plus `basalt ai` CLI that understands the project's stack, analyzes it, and diagnoses issues — following the framework's own conventions, not inventing new ones.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|