Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions packages/core/src/security/security-scanner.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {
SecurityVulnerability,
SecurityScanResult
KernelSecurityVulnerability,
KernelSecurityScanResult
} from '@objectstack/spec/kernel';
import type { ObjectLogger } from '../logger.js';

Expand Down Expand Up @@ -42,10 +42,10 @@ export class PluginSecurityScanner {
private logger: ObjectLogger;

// Known vulnerabilities database (CVE cache)
private vulnerabilityDb = new Map<string, SecurityVulnerability>();
private vulnerabilityDb = new Map<string, KernelSecurityVulnerability>();

// Scan results cache
private scanResults = new Map<string, SecurityScanResult>();
private scanResults = new Map<string, KernelSecurityScanResult>();

private passThreshold: number = 70;

Expand All @@ -59,7 +59,7 @@ export class PluginSecurityScanner {
/**
* Perform a comprehensive security scan on a plugin
*/
async scan(target: ScanTarget): Promise<SecurityScanResult> {
async scan(target: ScanTarget): Promise<KernelSecurityScanResult> {
this.logger.info('Starting security scan', {
pluginId: target.pluginId,
version: target.version
Expand Down Expand Up @@ -91,7 +91,7 @@ export class PluginSecurityScanner {
// Calculate security score (0-100, higher is better)
const score = this.calculateSecurityScore(issues);

const result: SecurityScanResult = {
const result: KernelSecurityScanResult = {
timestamp: new Date().toISOString(),
scanner: { name: 'ObjectStack Security Scanner', version: '1.0.0' },
status: score >= this.passThreshold ? 'passed' : 'failed',
Expand Down Expand Up @@ -309,7 +309,7 @@ export class PluginSecurityScanner {
addVulnerability(
packageName: string,
version: string,
vulnerability: SecurityVulnerability
vulnerability: KernelSecurityVulnerability
): void {
const key = `${packageName}@${version}`;
this.vulnerabilityDb.set(key, vulnerability);
Expand All @@ -324,7 +324,7 @@ export class PluginSecurityScanner {
/**
* Get scan result from cache
*/
getScanResult(pluginId: string, version: string): SecurityScanResult | undefined {
getScanResult(pluginId: string, version: string): KernelSecurityScanResult | undefined {
return this.scanResults.get(`${pluginId}:${version}`);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/metadata/src/loaders/memory-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class MemoryLoader implements MetadataLoader {
if (await this.exists(type, name)) {
return {
size: 0, // In-memory
mtime: new Date(),
mtime: new Date().toISOString(),
format: 'json',
};
}
Expand Down
2 changes: 1 addition & 1 deletion packages/metadata/src/loaders/remote-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export class RemoteLoader implements MetadataLoader {

return {
size: Number(response.headers.get('content-length') || 0),
mtime: new Date(response.headers.get('last-modified') || Date.now()),
mtime: new Date(response.headers.get('last-modified') || Date.now()).toISOString(),
format: 'json',
};
Comment on lines 105 to 108
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new Date(response.headers.get('last-modified') || Date.now()).toISOString() will throw a RangeError if last-modified is present but not parseable as a date. Consider guarding against invalid dates (e.g., check Number.isNaN(date.getTime()) and omit mtime or fall back to new Date().toISOString()).

Copilot uses AI. Check for mistakes.
}
Expand Down
2 changes: 1 addition & 1 deletion packages/metadata/src/metadata-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export class MetadataManager {
try {
const result = await loader.load(type, name, options);
if (result.data) {
return result.data;
return result.data as T;
}
} catch (e) {
this.logger.warn(`Loader ${loader.contract.name} failed to load ${type}:${name}`, { error: e });
Expand Down
18 changes: 9 additions & 9 deletions packages/objectql/src/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,13 +437,13 @@ export class ObjectQL implements IDataEngine {
await this.triggerHooks('beforeFind', hookContext);

try {
const result = await driver.find(object, hookContext.input.ast, hookContext.input.options);
const result = await driver.find(object, hookContext.input.ast as QueryAST, hookContext.input.options as any);

hookContext.event = 'afterFind';
hookContext.result = result;
await this.triggerHooks('afterFind', hookContext);

return hookContext.result;
return hookContext.result as any[];
Comment on lines +440 to +446
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HookContext in @objectstack/spec documents find input as { query, options }, but this code reads hookContext.input.ast. That means beforeFind hooks written against the spec (e.g. mutating context.input.query) won’t affect the executed query. Consider standardizing on input.query (or supporting both query and legacy ast when reading from the context) before calling the driver.

Copilot uses AI. Check for mistakes.
} catch (e) {
this.logger.error('Find operation failed', e as Error, { object });
throw e;
Expand Down Expand Up @@ -480,13 +480,13 @@ export class ObjectQL implements IDataEngine {
if (Array.isArray(hookContext.input.data)) {
// Bulk Create
if (driver.bulkCreate) {
result = await driver.bulkCreate(object, hookContext.input.data, hookContext.input.options);
result = await driver.bulkCreate(object, hookContext.input.data as any[], hookContext.input.options as any);
} else {
// Fallback loop
result = await Promise.all(hookContext.input.data.map((item: any) => driver.create(object, item, hookContext.input.options)));
result = await Promise.all((hookContext.input.data as any[]).map((item: any) => driver.create(object, item, hookContext.input.options as any)));
}
} else {
result = await driver.create(object, hookContext.input.data, hookContext.input.options);
result = await driver.create(object, hookContext.input.data, hookContext.input.options as any);
}
Comment on lines +483 to 490
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec’s HookContext convention for inserts is input.doc, but this code relies on hookContext.input.data. Hooks expecting context.input.doc won’t be able to validate/transform the inserted document. Consider switching to doc (or reading doc ?? data for backward compatibility) throughout the insert flow.

Copilot uses AI. Check for mistakes.

hookContext.event = 'afterInsert';
Expand Down Expand Up @@ -529,11 +529,11 @@ export class ObjectQL implements IDataEngine {
let result;
if (hookContext.input.id) {
// Single update by ID
result = await driver.update(object, hookContext.input.id, hookContext.input.data, hookContext.input.options);
result = await driver.update(object, hookContext.input.id as string, hookContext.input.data, hookContext.input.options as any);
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hookContext.input.id is asserted to string when passed to driver.update, but the driver contract accepts id: any and some drivers may use non-string IDs. Prefer avoiding the as string assertion here (or explicitly coercing/validating if ObjectQL requires string IDs).

Suggested change
result = await driver.update(object, hookContext.input.id as string, hookContext.input.data, hookContext.input.options as any);
result = await driver.update(object, hookContext.input.id, hookContext.input.data, hookContext.input.options as any);

Copilot uses AI. Check for mistakes.
} else if (options?.multi && driver.updateMany) {
// Bulk update by Query
const ast = this.toQueryAST(object, { filter: options.filter });
result = await driver.updateMany(object, ast, hookContext.input.data, hookContext.input.options);
result = await driver.updateMany(object, ast, hookContext.input.data, hookContext.input.options as any);
} else {
Comment on lines +532 to 537
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update hooks are conventionally passed the payload in context.input.doc (per @objectstack/spec), but this implementation uses context.input.data. This makes it easy for hook authors to use the wrong key and have their changes ignored. Consider standardizing on doc (or supporting doc ?? data when reading).

Copilot uses AI. Check for mistakes.
throw new Error('Update requires an ID or options.multi=true');
}
Expand Down Expand Up @@ -572,10 +572,10 @@ export class ObjectQL implements IDataEngine {
try {
let result;
if (hookContext.input.id) {
result = await driver.delete(object, hookContext.input.id, hookContext.input.options);
result = await driver.delete(object, hookContext.input.id as string, hookContext.input.options as any);
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hookContext.input.id is asserted to string here, but driver IDs are typed as any (and may be numbers/UUID objects depending on the driver). The as string assertion can mislead callers/maintainers and hides mismatches. Prefer leaving it as unknown narrowed/cast to the driver’s expected id type (typically any), or validate/coerce explicitly if a string ID is required.

Suggested change
result = await driver.delete(object, hookContext.input.id as string, hookContext.input.options as any);
// Pass ID through without falsely asserting it is a string; drivers accept any ID type.
result = await driver.delete(object, hookContext.input.id as any, hookContext.input.options as any);

Copilot uses AI. Check for mistakes.
} else if (options?.multi && driver.deleteMany) {
const ast = this.toQueryAST(object, { filter: options.filter });
result = await driver.deleteMany(object, ast, hookContext.input.options);
result = await driver.deleteMany(object, ast, hookContext.input.options as any);
} else {
throw new Error('Delete requires an ID or options.multi=true');
}
Expand Down
Loading