Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 3 additions & 1 deletion gitnexus/src/core/lbug/lbug-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
isOpenRetryExhausted,
isWalCorruptionError,
openLbugConnection,
toNativeSafePath,
WAL_RECOVERY_SUGGESTION,
waitForWindowsHandleRelease,
type LbugConnectionHandle,
Expand Down Expand Up @@ -386,7 +387,8 @@ const runWithSessionLock = async <T>(operation: () => Promise<T>): Promise<T> =>
}
};

const normalizeCopyPath = (filePath: string): string => filePath.replace(/\\/g, '/');
const normalizeCopyPath = (filePath: string): string =>
toNativeSafePath(filePath).replace(/\\/g, '/');

const closeQueryResult = async (result: lbug.QueryResult): Promise<void> => {
try {
Expand Down
37 changes: 36 additions & 1 deletion gitnexus/src/core/lbug/lbug-config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,43 @@
import fs from 'fs/promises';
import os from 'os';
import path from 'path';
import { execFileSync } from 'child_process';
import type lbug from '@ladybugdb/core';
import { logger } from '../logger.js';

// ─── Windows non-ASCII path workaround ────────────────────────────────────────
//
// KuzuDB's native C++ layer on Windows uses ANSI file APIs (fopen, not
// _wfopen). When the path contains CJK or other non-ASCII characters,
// the UTF-8 bytes from Node.js are misinterpreted as the system's Active
// Code Page (e.g. GBK on Chinese Windows), producing a garbled path that
// the OS cannot resolve — "Error 3: The system cannot find the path."
//
// Workaround: convert to the 8.3 short-name form which is all-ASCII.
// Falls back to the original path if short names are unavailable.

const NON_ASCII_RE = /[^\x00-\x7F]/;

export function toNativeSafePath(p: string): string {
if (process.platform !== 'win32') return p;
if (!NON_ASCII_RE.test(p)) return p;
try {
const result = execFileSync('cmd.exe', ['/c', `for %I in ("${p}") do @echo %~sI`], {
encoding: 'utf-8',
timeout: 5000,
windowsHide: true,
stdio: ['pipe', 'pipe', 'pipe'],
});

Check failure

Code scanning / CodeQL

Uncontrolled command line Critical

This command line depends on a
user-provided value
.

Check warning

Code scanning / CodeQL

Indirect uncontrolled command line Medium

This command depends on an unsanitized
command-line argument
.
This command depends on an unsanitized
environment variable
.
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
const shortPath = result.trim();
if (shortPath && !NON_ASCII_RE.test(shortPath)) {
return shortPath;
}
} catch {
// 8.3 short names unavailable or cmd failed — fall through
}
return p;
}

/**
* Shared configuration for `@ladybugdb/core` `Database` construction.
*
Expand Down Expand Up @@ -351,10 +385,11 @@
databasePath: string,
options: LbugDatabaseOptions = {},
): Promise<LbugConnectionHandle> {
const safePath = toNativeSafePath(databasePath);
let db: lbug.Database | undefined;
try {
db = await openWithLockRetry(
() => createLbugDatabase(lbugModule, databasePath, options),
() => createLbugDatabase(lbugModule, safePath, options),
databasePath,
);
return { db, conn: new lbugModule.Connection(db) };
Expand Down
Loading