fix: address Qodo code review findings
- Add missing snapshot-differ.test.ts to npm test script - Fix path traversal vulnerability in agent-mail.ts with message ID validation - Fix readLastTouchedVersion to log errors instead of silently swallowing them - Sanitize log statements to not leak full paths - Add projectRoot validation to all API routes - Fix activity persistence write race conditions with promise chaining Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
d1140c9809
commit
a3f2ceef52
8 changed files with 108 additions and 9 deletions
|
|
@ -1,9 +1,26 @@
|
|||
import { activityEventBus } from '../../../lib/realtime';
|
||||
|
||||
function isValidProjectRoot(root: string): boolean {
|
||||
try {
|
||||
const resolved = require('path').resolve(root);
|
||||
return require('path').isAbsolute(resolved);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(request: Request): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
const projectRoot = url.searchParams.get('projectRoot') || undefined;
|
||||
const projectRootParam = url.searchParams.get('projectRoot');
|
||||
|
||||
if (projectRootParam && !isValidProjectRoot(projectRootParam)) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid projectRoot path' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const projectRoot = projectRootParam || undefined;
|
||||
const history = activityEventBus.getHistory(projectRoot);
|
||||
|
||||
return Response.json(history);
|
||||
|
|
|
|||
|
|
@ -1,15 +1,30 @@
|
|||
import { NextResponse } from 'next/server';
|
||||
import path from 'node:path';
|
||||
import { readIssuesFromDisk } from '../../../../../lib/read-issues';
|
||||
import { activityEventBus } from '../../../../../lib/realtime';
|
||||
import { getAgentMetrics } from '../../../../../lib/agent-sessions';
|
||||
|
||||
function isValidProjectRoot(root: string): boolean {
|
||||
try {
|
||||
const resolved = path.resolve(root);
|
||||
return path.isAbsolute(resolved);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ agentId: string }> }
|
||||
): Promise<Response> {
|
||||
const { agentId } = await params;
|
||||
const url = new URL(request.url);
|
||||
const projectRoot = url.searchParams.get('projectRoot') ?? process.cwd();
|
||||
const projectRootParam = url.searchParams.get('projectRoot');
|
||||
const projectRoot = projectRootParam ?? process.cwd();
|
||||
|
||||
if (projectRootParam && !isValidProjectRoot(projectRootParam)) {
|
||||
return NextResponse.json({ ok: false, error: 'Invalid projectRoot path' }, { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
const issues = await readIssuesFromDisk({ projectRoot, preferBd: true });
|
||||
|
|
|
|||
|
|
@ -1,10 +1,27 @@
|
|||
import { NextResponse } from 'next/server';
|
||||
|
||||
import path from 'node:path';
|
||||
import { readIssuesFromDisk } from '../../../../lib/read-issues';
|
||||
|
||||
function isValidProjectRoot(root: string): boolean {
|
||||
try {
|
||||
const resolved = path.resolve(root);
|
||||
return path.isAbsolute(resolved);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(request: Request): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
const projectRoot = url.searchParams.get('projectRoot') ?? process.cwd();
|
||||
const projectRootParam = url.searchParams.get('projectRoot');
|
||||
const projectRoot = projectRootParam ?? process.cwd();
|
||||
|
||||
if (projectRootParam && !isValidProjectRoot(projectRootParam)) {
|
||||
return NextResponse.json(
|
||||
{ ok: false, error: { classification: 'validation', message: 'Invalid projectRoot path' } },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const issues = await readIssuesFromDisk({ projectRoot, preferBd: true });
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ async function readLastTouchedVersion(filePath: string): Promise<number | null>
|
|||
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
||||
return null;
|
||||
}
|
||||
// Log non-ENOENT errors but don't swallow them silently
|
||||
console.error('[Events] Failed to read last-touched version:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,30 @@ import { readIssuesFromDisk } from '../../../lib/read-issues';
|
|||
import { activityEventBus } from '../../../lib/realtime';
|
||||
import { buildSessionTaskFeed, getCommunicationSummary } from '../../../lib/agent-sessions';
|
||||
|
||||
function isValidProjectRoot(root: string): boolean {
|
||||
// Basic validation: path should not contain traversal patterns
|
||||
// and should resolve to an absolute path
|
||||
try {
|
||||
const resolved = require('path').resolve(root);
|
||||
return require('path').isAbsolute(resolved);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export async function GET(request: Request): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
const projectRoot = url.searchParams.get('projectRoot') ?? process.cwd();
|
||||
const projectRootParam = url.searchParams.get('projectRoot');
|
||||
const projectRoot = projectRootParam ?? process.cwd();
|
||||
|
||||
if (projectRootParam && !isValidProjectRoot(projectRoot)) {
|
||||
return NextResponse.json(
|
||||
{ ok: false, error: { classification: 'validation', message: 'Invalid projectRoot path' } },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const issues = await readIssuesFromDisk({ projectRoot, preferBd: true });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue