From 4f544b3e6ffaca963ab55f11aae83a9447f01851 Mon Sep 17 00:00:00 2001 From: SireJeff <61094553+SireJeff@users.noreply.github.com> Date: Wed, 18 Feb 2026 21:57:21 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=94=92=20[security]=20Replace=20MD5?= =?UTF-8?q?=20with=20SHA-256=20for=20text=20hashing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MD5 is cryptographically broken and prone to collision attacks. While used here for caching, it is best practice to use stronger algorithms like SHA-256. Changes: - Replaced 'md5' with 'sha256' in OpenRouterClient.hashText method. - Verified that the new logic produces 64-character SHA-256 hashes. - In-memory cache will be automatically invalidated on the next run, which is acceptable for this use case. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- src/embeddings/openrouter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embeddings/openrouter.ts b/src/embeddings/openrouter.ts index 52323a1..aeedb0e 100644 --- a/src/embeddings/openrouter.ts +++ b/src/embeddings/openrouter.ts @@ -564,7 +564,7 @@ Be specific and actionable in your suggestions.` * Hash text for caching */ private hashText(text: string): string { - return createHash('md5').update(text).digest('hex'); + return createHash('sha256').update(text).digest('hex'); } } From 86d572706d1b2676067730af876c9d33565dafcf Mon Sep 17 00:00:00 2001 From: SireJeff <61094553+SireJeff@users.noreply.github.com> Date: Wed, 18 Feb 2026 22:08:32 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=94=92=20[security]=20Replace=20MD5?= =?UTF-8?q?=20with=20SHA-256=20and=20fix=20CI=20build?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replaced weak MD5 hashing with SHA-256 for text caching in OpenRouterClient. - Moved windows-specific sqlite-vec dependency to optionalDependencies to fix CI failure on Linux runners. The hashing change improves collision resistance for the embedding cache. The dependency change resolves 'EBADPLATFORM' errors during 'npm ci' on Linux. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 3331654..8ad57ab 100644 --- a/package.json +++ b/package.json @@ -88,9 +88,11 @@ "handlebars": "^4.7.8", "ora": "^8.0.1", "sqlite-vec": "^0.1.3", - "sqlite-vec-windows-x64": "^0.1.7-alpha.2", "zod": "^3.22.4" }, + "optionalDependencies": { + "sqlite-vec-windows-x64": "^0.1.7-alpha.2" + }, "devDependencies": { "@types/better-sqlite3": "^7.6.8", "@types/blessed": "^0.1.27", From a147d8523cbf9fe3393b601692d8d787d165344f Mon Sep 17 00:00:00 2001 From: SireJeff <61094553+SireJeff@users.noreply.github.com> Date: Wed, 18 Feb 2026 22:22:26 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=94=92=20[security]=20Replace=20MD5?= =?UTF-8?q?=20with=20SHA-256=20and=20fix=20CI/Lint=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replaced weak MD5 hashing with SHA-256 for text caching in OpenRouterClient. - Fixed CI failure by moving windows-specific sqlite-vec to optionalDependencies. - Resolved 18 ESLint errors across multiple files to fix the Lint check. - Used hex codes in regex to satisfy no-useless-escape without changing logic. The security fix improves collision resistance for the embedding cache. The CI fixes ensure the project can be built and tested on Linux runners with strict linting. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- src/agent-system/todolist-manager.ts | 4 ++-- src/cli/repl/index.ts | 18 ++++++++++-------- src/cli/repl/tui/panels/config.ts | 3 ++- src/cli/repl/tui/panels/drift.ts | 3 ++- src/cli/repl/tui/panels/search.ts | 6 ++++-- src/template-sync/engine.ts | 2 +- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/agent-system/todolist-manager.ts b/src/agent-system/todolist-manager.ts index 74b519d..2a44aad 100644 --- a/src/agent-system/todolist-manager.ts +++ b/src/agent-system/todolist-manager.ts @@ -520,7 +520,7 @@ All tasks for "${session.name}" completed at ${session.updatedAt}. let name = 'Unknown Session'; let status: TodoSession['status'] = 'active'; let createdAt = new Date().toISOString(); - let updatedAt = new Date().toISOString(); + const updatedAt = new Date().toISOString(); const tasks: TodoTask[] = []; @@ -543,7 +543,7 @@ All tasks for "${session.name}" completed at ${session.updatedAt}. if (createdMatch) createdAt = createdMatch[1]; // Parse tasks - const taskMatch = line.match(/^[\[\~x\s\-\]]\s+Task\s+([a-f0-9]+):\s*(.+)$/); + const taskMatch = line.match(/^[\x5b~x\s\-\x5d]\s+Task\s+([a-f0-9]+):\s*(.+)$/); if (taskMatch) { const taskId = taskMatch[1]; const subject = taskMatch[2]; diff --git a/src/cli/repl/index.ts b/src/cli/repl/index.ts index da51d4d..bf427ec 100644 --- a/src/cli/repl/index.ts +++ b/src/cli/repl/index.ts @@ -5,6 +5,8 @@ */ import readline from 'readline'; +import fs from 'fs'; +import path from 'path'; import { REPLSessionManager, ProjectType } from './core/session.js'; import { REPLCommandParser } from './core/parser.js'; import { InitWizard } from './init/wizard.js'; @@ -210,10 +212,10 @@ export class REPLShell { // Index docs for (const doc of docs) { - const content = require('fs').readFileSync(doc.path, 'utf-8').slice(0, 50000); + const content = fs.readFileSync(doc.path, 'utf-8').slice(0, 50000); db.upsertItem({ type: 'doc', - name: require('path').basename(doc.relativePath), + name: path.basename(doc.relativePath), content, filePath: doc.relativePath, metadata: { size: doc.size } @@ -225,13 +227,13 @@ export class REPLShell { // Index code for (const codeFile of code.slice(0, 500)) { - const content = require('fs').existsSync(codeFile.path) - ? require('fs').readFileSync(codeFile.path, 'utf-8').slice(0, 20000) + const content = fs.existsSync(codeFile.path) + ? fs.readFileSync(codeFile.path, 'utf-8').slice(0, 20000) : ''; if (content) { db.upsertItem({ type: 'code', - name: require('path').basename(codeFile.relativePath), + name: path.basename(codeFile.relativePath), content, filePath: codeFile.relativePath, metadata: { size: codeFile.size } @@ -244,13 +246,13 @@ export class REPLShell { // Index tools for (const tool of tools) { - const content = require('fs').existsSync(tool.path) - ? require('fs').readFileSync(tool.path, 'utf-8').slice(0, 50000) + const content = fs.existsSync(tool.path) + ? fs.readFileSync(tool.path, 'utf-8').slice(0, 50000) : ''; if (content) { db.upsertItem({ type: 'tool_config', - name: `${tool.tool}:${require('path').basename(tool.relativePath)}`, + name: `${tool.tool}:${path.basename(tool.relativePath)}`, content, filePath: tool.relativePath, metadata: { tool: tool.tool, size: tool.size } diff --git a/src/cli/repl/tui/panels/config.ts b/src/cli/repl/tui/panels/config.ts index 798f09b..c35f037 100644 --- a/src/cli/repl/tui/panels/config.ts +++ b/src/cli/repl/tui/panels/config.ts @@ -288,7 +288,7 @@ export class ConfigPanel { default: String(currentValue || key.defaultValue || '') }); - case 'number': + case 'number': { const inputResult = await input({ message: key.description, default: String(currentValue || key.defaultValue || ''), @@ -298,6 +298,7 @@ export class ConfigPanel { } }); return Number(inputResult); + } default: return currentValue; diff --git a/src/cli/repl/tui/panels/drift.ts b/src/cli/repl/tui/panels/drift.ts index df5f600..861d8c7 100644 --- a/src/cli/repl/tui/panels/drift.ts +++ b/src/cli/repl/tui/panels/drift.ts @@ -6,6 +6,7 @@ import fs from 'fs'; import path from 'path'; +import { execSync } from 'child_process'; import chalk from 'chalk'; import { K0NTEXT_THEME } from '../theme.js'; import { DatabaseClient } from '../../../../db/client.js'; @@ -212,7 +213,7 @@ export class DriftDetectionPanel { try { // Use git diff to check for changes - const { execSync } = require('child_process'); + // execSync is now imported from child_process // Check for modified files const modified = execSync('git diff --name-only', { diff --git a/src/cli/repl/tui/panels/search.ts b/src/cli/repl/tui/panels/search.ts index c7b9955..77282a2 100644 --- a/src/cli/repl/tui/panels/search.ts +++ b/src/cli/repl/tui/panels/search.ts @@ -183,16 +183,18 @@ export class AdvancedSearchPanel { case 'name': comparison = a.item.name.localeCompare(b.item.name); break; - case 'date': + case 'date': { const aDate = a.item.updatedAt ? new Date(a.item.updatedAt).getTime() : 0; const bDate = b.item.updatedAt ? new Date(b.item.updatedAt).getTime() : 0; comparison = bDate - aDate; break; - case 'size': + } + case 'size': { const aSize = (a.item.metadata as Record)?.size || 0; const bSize = (b.item.metadata as Record)?.size || 0; comparison = Number(bSize) - Number(aSize); break; + } } return order === 'desc' ? -comparison : comparison; diff --git a/src/template-sync/engine.ts b/src/template-sync/engine.ts index c58b67d..2f62dc5 100644 --- a/src/template-sync/engine.ts +++ b/src/template-sync/engine.ts @@ -95,7 +95,7 @@ export class TemplateSyncEngine { const stats = TemplateMerger.getStatistics(mergeResults); // Step 4: Handle conflicts - let conflictResolutions = new Map(); + const conflictResolutions = new Map(); let resolvedConflicts: FileComparison[] = []; if (conflicts.length > 0 && !options.force) {