mirror of
https://github.com/ruvnet/RuView
synced 2026-07-30 18:41:42 +00:00
2b7853b18f
* feat(ruview): add secure community metaharness flywheel * fix(ruview): canonicalize manifest line endings
122 lines
6.2 KiB
JavaScript
122 lines
6.2 KiB
JavaScript
// SPDX-License-Identifier: MIT
|
|
// Reviewable shared repository knowledge. Canonical records are committed JSONL;
|
|
// private vector indexes/transcripts are deliberately outside this package.
|
|
|
|
import { createHash } from 'node:crypto';
|
|
import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
export const CORPUS_PATH = join(ROOT, 'brain', 'corpus', 'core.jsonl');
|
|
const SECRET = /(-----BEGIN [A-Z ]*PRIVATE KEY-----|(?:api[_-]?key|token|password|secret)\s*[:=]\s*\S+)/i;
|
|
const INJECTION = /\b(ignore (?:all|the|previous)|system prompt|developer message|execute this|run this command)\b/i;
|
|
const EVIDENCE = new Set(['REPOSITORY', 'POLICY', 'ADR', 'MEASURED', 'SYNTHETIC']);
|
|
|
|
function sha256(text) {
|
|
return createHash('sha256').update(text).digest('hex');
|
|
}
|
|
|
|
export function validateBrainRecord(record, { canonical = false } = {}) {
|
|
const errors = [];
|
|
if (!record || typeof record !== 'object' || Array.isArray(record)) return ['record must be an object'];
|
|
for (const key of ['id', 'title', 'content', 'evidence']) {
|
|
if (typeof record[key] !== 'string' || !record[key].trim()) errors.push(`${key} must be a non-empty string`);
|
|
}
|
|
if (!/^[a-z0-9][a-z0-9-]{2,63}$/.test(record.id || '')) errors.push('id must be a lowercase slug');
|
|
if (!EVIDENCE.has(record.evidence)) errors.push(`unsupported evidence: ${record.evidence}`);
|
|
if (!record.source || typeof record.source.path !== 'string' || !Number.isInteger(record.source.line) || record.source.line < 1) {
|
|
errors.push('source.path and positive source.line are required');
|
|
} else if (isAbsolute(record.source.path) || record.source.path.split(/[\\/]/).includes('..') || /^[A-Za-z]:/.test(record.source.path)) {
|
|
errors.push('source.path must be repository-relative without traversal');
|
|
}
|
|
if (!Array.isArray(record.tags) || record.tags.some((tag) => typeof tag !== 'string')) errors.push('tags must be strings');
|
|
if ((record.content || '').length > 8192) errors.push('content exceeds 8192 characters');
|
|
if ((record.title || '').length > 200) errors.push('title exceeds 200 characters');
|
|
if (canonical && record.reviewed !== true) errors.push('canonical records must be reviewed');
|
|
const combined = `${record.title || ''}\n${record.content || ''}`;
|
|
if (SECRET.test(combined)) errors.push('record appears to contain a secret');
|
|
if (INJECTION.test(combined)) errors.push('record contains instruction-like prompt injection');
|
|
return errors;
|
|
}
|
|
|
|
export function loadBrain(path = CORPUS_PATH) {
|
|
const raw = readFileSync(path, 'utf8').replace(/\r\n/g, '\n');
|
|
if (Buffer.byteLength(raw) > 1_048_576) throw new Error('brain corpus exceeds 1 MiB');
|
|
const records = raw.split('\n').filter(Boolean).map((line, index) => {
|
|
if (Buffer.byteLength(line) > 16_384) throw new Error(`brain line ${index + 1}: exceeds 16 KiB`);
|
|
let record;
|
|
try { record = JSON.parse(line); } catch (error) { throw new Error(`brain line ${index + 1}: ${error.message}`); }
|
|
const errors = validateBrainRecord(record, { canonical: true });
|
|
if (errors.length) throw new Error(`brain line ${index + 1}: ${errors.join('; ')}`);
|
|
return Object.freeze(record);
|
|
});
|
|
if (records.length > 1000) throw new Error('brain corpus exceeds 1000 records');
|
|
const ids = new Set();
|
|
for (const record of records) {
|
|
if (ids.has(record.id)) throw new Error(`duplicate brain id: ${record.id}`);
|
|
ids.add(record.id);
|
|
}
|
|
return { records, digest: sha256(raw), bytes: Buffer.byteLength(raw) };
|
|
}
|
|
|
|
function terms(value) {
|
|
return new Set(String(value).toLowerCase().match(/[a-z0-9][a-z0-9_-]{1,}/g) || []);
|
|
}
|
|
|
|
export function searchBrain(query, { limit = 8, path = CORPUS_PATH } = {}) {
|
|
const wanted = terms(query);
|
|
if (!wanted.size) return [];
|
|
const { records, digest } = loadBrain(path);
|
|
return records.map((record) => {
|
|
const title = terms(record.title);
|
|
const body = terms(record.content);
|
|
const tags = new Set(record.tags.map((tag) => tag.toLowerCase()));
|
|
let score = 0;
|
|
for (const term of wanted) score += title.has(term) ? 5 : tags.has(term) ? 3 : body.has(term) ? 1 : 0;
|
|
return { ...record, score, citation: `${record.source.path}:${record.source.line}`, corpusDigest: digest };
|
|
}).filter((record) => record.score > 0)
|
|
.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
|
|
.slice(0, Math.max(1, Math.min(Number(limit) || 8, 25)));
|
|
}
|
|
|
|
export function verifyBrain({ repo = process.cwd(), path = CORPUS_PATH } = {}) {
|
|
const root = resolve(repo);
|
|
const { records, digest, bytes } = loadBrain(path);
|
|
const findings = [];
|
|
for (const record of records) {
|
|
const source = resolve(root, record.source.path);
|
|
const rel = relative(root, source);
|
|
if (isAbsolute(rel) || rel.startsWith('..') || !existsSync(source)) {
|
|
findings.push({ id: record.id, reason: 'source_missing', source: record.source.path });
|
|
} else {
|
|
const real = realpathSync(source);
|
|
const realRel = relative(realpathSync(root), real);
|
|
if (isAbsolute(realRel) || realRel.startsWith('..')) {
|
|
findings.push({ id: record.id, reason: 'source_escape', source: record.source.path });
|
|
} else {
|
|
const sourceLines = readFileSync(real, 'utf8').split(/\r?\n/);
|
|
if (record.source.line > sourceLines.length) {
|
|
findings.push({ id: record.id, reason: 'source_line_missing', source: record.source.path, line: record.source.line });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return { ok: findings.length === 0, records: records.length, digest, bytes, findings };
|
|
}
|
|
|
|
export function makeProposal(input) {
|
|
const record = {
|
|
id: String(input.id || '').trim(),
|
|
title: String(input.title || '').trim(),
|
|
content: String(input.content || '').trim(),
|
|
source: { path: String(input.sourcePath || '').trim(), line: Number(input.sourceLine) },
|
|
evidence: String(input.evidence || 'REPOSITORY').toUpperCase(),
|
|
tags: String(input.tags || '').split(',').map((tag) => tag.trim()).filter(Boolean),
|
|
contributor: String(input.contributor || '').trim() || 'unknown',
|
|
reviewed: false,
|
|
};
|
|
const errors = validateBrainRecord(record);
|
|
return errors.length ? { ok: false, errors } : { ok: true, proposal: record, jsonl: JSON.stringify(record) };
|
|
}
|