Skip to content
Open
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
11 changes: 11 additions & 0 deletions e2e/global-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { execSync } from "node:child_process";
import path from "node:path";

/**
* Bundle e2e runs `packages/deepsec/dist/cli.mjs`. Always rebuild before the
* e2e project so tests never execute a stale dist/ from an older checkout.
*/
export default function globalSetup(): void {
const root = path.resolve(import.meta.dirname, "..");
execSync("pnpm bundle", { cwd: root, stdio: "inherit", env: process.env });
}
1 change: 1 addition & 0 deletions e2e/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
name: "e2e",
globalSetup: ["./global-setup.ts"],
include: ["**/*.test.ts"],
testTimeout: 30_000,
// The live-sandbox test spawns the bundled CLI for ~5+ minutes
Expand Down
1 change: 1 addition & 0 deletions knip.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"e2e/bundle.test.ts",
"e2e/pipeline.test.ts",
"e2e/pipeline-sandbox.test.ts",
"e2e/global-setup.ts",
"e2e/vitest.config.ts"
]
},
Expand Down
25 changes: 15 additions & 10 deletions packages/core/src/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ export function getDataRoot(): string {
return process.env.DEEPSEC_DATA_ROOT || "data";
}

/** Root segment for joining per-project paths; normalize `\` so posix.join is stable on Windows. */
function dataRootPosix(): string {
return getDataRoot().replace(/\\/g, "/");
}

// Reject empty, '.', '..', absolute paths, null bytes, and any path
// separator. Used at every entry point that joins user-supplied segments
// onto a per-project mirror so a `../`-laced projectId/runId can't escape
Expand Down Expand Up @@ -53,55 +58,55 @@ export function assertSafeFilePath(filePath: string): void {

export function dataDir(projectId: string): string {
assertSafeSegment(projectId, "projectId");
return path.join(getDataRoot(), projectId);
return path.posix.join(dataRootPosix(), projectId);
}

export function projectConfigPath(projectId: string): string {
return path.join(dataDir(projectId), "project.json");
return path.posix.join(dataDir(projectId), "project.json");
}

// --- File records (permanent per-file mirror) ---

export function filesDir(projectId: string): string {
return path.join(dataDir(projectId), "files");
return path.posix.join(dataDir(projectId), "files");
}

export function fileRecordPath(projectId: string, filePath: string): string {
assertSafeFilePath(filePath);
return path.join(filesDir(projectId), filePath + ".json");
return path.posix.join(filesDir(projectId), filePath + ".json");
}

// --- Runs (lightweight metadata) ---

export function runsDir(projectId: string): string {
return path.join(dataDir(projectId), "runs");
return path.posix.join(dataDir(projectId), "runs");
}

export function runMetaPath(projectId: string, runId: string): string {
assertSafeSegment(runId, "runId");
return path.join(runsDir(projectId), runId + ".json");
return path.posix.join(runsDir(projectId), runId + ".json");
}

// --- Reports ---

export function reportsDir(projectId: string): string {
return path.join(dataDir(projectId), "reports");
return path.posix.join(dataDir(projectId), "reports");
}

export function reportJsonPath(projectId: string, runId?: string): string {
if (runId !== undefined) assertSafeSegment(runId, "runId");
const name = runId ? `report-${runId}.json` : "report.json";
return path.join(reportsDir(projectId), name);
return path.posix.join(reportsDir(projectId), name);
}

export function reportMdPath(projectId: string, runId?: string): string {
if (runId !== undefined) assertSafeSegment(runId, "runId");
const name = runId ? `report-${runId}.md` : "report.md";
return path.join(reportsDir(projectId), name);
return path.posix.join(reportsDir(projectId), name);
}

export function reportCsvPath(projectId: string, runId?: string): string {
if (runId !== undefined) assertSafeSegment(runId, "runId");
const name = runId ? `report-${runId}.csv` : "report.csv";
return path.join(reportsDir(projectId), name);
return path.posix.join(reportsDir(projectId), name);
}
7 changes: 2 additions & 5 deletions packages/deepsec/src/commands/init-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,8 @@ export function registerProject(opts: {
const workspaceDir = fs.realpathSync(path.resolve(opts.workspaceDir));
const targetAbs = requireExistingDir(opts.targetRoot, "<target-root>");
const id = validateProjectId(opts.id ?? path.basename(targetAbs));
// Normalize to POSIX separators: `targetRel` gets written into
// deepsec.config.ts (committed to VCS) and SETUP.md, so a Windows
// contributor adding a project would otherwise produce `..\foo\bar`
// that's ugly cross-platform and noisy in diffs. Both Node path APIs
// accept "/" on Windows.
// Always POSIX `root:` in deepsec.config.ts so scaffolds match across OS
// and tools that consume the file see a single separator convention.
const targetRel = path.relative(workspaceDir, targetAbs).split(path.sep).join("/");

const configPath = findConfigInWorkspace(workspaceDir);
Expand Down
7 changes: 2 additions & 5 deletions packages/scanner/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,8 @@ export class RegexScannerDriver implements ScannerDriver {
nodir: true,
absolute: false,
});
// glob returns native separators on Windows ("src\api\foo.ts").
// Record paths require POSIX separators (assertSafeFilePath rejects
// "\"), so normalize once here before anything reads or writes records.
const files = rawFiles.map((f) => f.replaceAll("\\", "/"));
globCache.set(key, files);
const posixPaths = files.map((p) => p.replace(/\\/g, "/"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Merge conflict resolution left three references to a renamed variable files (now rawFiles), causing a ReferenceError that crashes every scan.

Fix on Vercel

globCache.set(key, posixPaths);
yield {
type: "matcher_done" as const,
message: `Found ${files.length} files`,
Expand Down