164 lines
4.4 KiB
JavaScript
164 lines
4.4 KiB
JavaScript
import {
|
|
NOTEBOOK_ROOT
|
|
} from "./chunk-74TMTGBG.js";
|
|
|
|
// shared/errors/index.ts
|
|
var AppError = class extends Error {
|
|
constructor(code, message, statusCode = 500, details) {
|
|
super(message);
|
|
this.code = code;
|
|
this.name = "AppError";
|
|
this.statusCode = statusCode;
|
|
this.details = details;
|
|
}
|
|
statusCode;
|
|
details;
|
|
toJSON() {
|
|
return {
|
|
name: this.name,
|
|
code: this.code,
|
|
message: this.message,
|
|
statusCode: this.statusCode,
|
|
details: this.details
|
|
};
|
|
}
|
|
};
|
|
var ValidationError = class extends AppError {
|
|
constructor(message, details) {
|
|
super("VALIDATION_ERROR", message, 400, details);
|
|
this.name = "ValidationError";
|
|
}
|
|
};
|
|
var NotFoundError = class extends AppError {
|
|
constructor(message = "Resource not found", details) {
|
|
super("NOT_FOUND", message, 404, details);
|
|
this.name = "NotFoundError";
|
|
}
|
|
};
|
|
var AccessDeniedError = class extends AppError {
|
|
constructor(message = "Access denied", details) {
|
|
super("ACCESS_DENIED", message, 403, details);
|
|
this.name = "AccessDeniedError";
|
|
}
|
|
};
|
|
var BadRequestError = class extends AppError {
|
|
constructor(message, details) {
|
|
super("BAD_REQUEST", message, 400, details);
|
|
this.name = "BadRequestError";
|
|
}
|
|
};
|
|
var NotADirectoryError = class extends AppError {
|
|
constructor(message = "\u4E0D\u662F\u76EE\u5F55", details) {
|
|
super("NOT_A_DIRECTORY", message, 400, details);
|
|
this.name = "NotADirectoryError";
|
|
}
|
|
};
|
|
var AlreadyExistsError = class extends AppError {
|
|
constructor(message = "Resource already exists", details) {
|
|
super("ALREADY_EXISTS", message, 409, details);
|
|
this.name = "AlreadyExistsError";
|
|
}
|
|
};
|
|
var ForbiddenError = class extends AppError {
|
|
constructor(message = "\u7981\u6B62\u8BBF\u95EE", details) {
|
|
super("FORBIDDEN", message, 403, details);
|
|
this.name = "ForbiddenError";
|
|
}
|
|
};
|
|
var UnsupportedMediaTypeError = class extends AppError {
|
|
constructor(message = "\u4E0D\u652F\u6301\u7684\u5A92\u4F53\u7C7B\u578B", details) {
|
|
super("UNSUPPORTED_MEDIA_TYPE", message, 415, details);
|
|
this.name = "UnsupportedMediaTypeError";
|
|
}
|
|
};
|
|
var ResourceLockedError = class extends AppError {
|
|
constructor(message = "\u8D44\u6E90\u5DF2\u9501\u5B9A", details) {
|
|
super("RESOURCE_LOCKED", message, 423, details);
|
|
this.name = "ResourceLockedError";
|
|
}
|
|
};
|
|
var InternalError = class extends AppError {
|
|
constructor(message = "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF", details) {
|
|
super("INTERNAL_ERROR", message, 500, details);
|
|
this.name = "InternalError";
|
|
}
|
|
};
|
|
function isAppError(error) {
|
|
return error instanceof AppError;
|
|
}
|
|
function isNodeError(error) {
|
|
return error instanceof Error && "code" in error;
|
|
}
|
|
|
|
// api/utils/pathSafety.ts
|
|
import path from "path";
|
|
var DANGEROUS_PATTERNS = [
|
|
/\.\./,
|
|
/\0/,
|
|
/%2e%2e[%/]/i,
|
|
/%252e%252e[%/]/i,
|
|
/\.\.%2f/i,
|
|
/\.\.%5c/i,
|
|
/%c0%ae/i,
|
|
/%c1%9c/i,
|
|
/%c0%ae%c0%ae/i,
|
|
/%c1%9c%c1%9c/i,
|
|
/\.\.%c0%af/i,
|
|
/\.\.%c1%9c/i,
|
|
/%252e/i,
|
|
/%uff0e/i,
|
|
/%u002e/i
|
|
];
|
|
var DOUBLE_ENCODE_PATTERNS = [
|
|
/%25[0-9a-fA-F]{2}/,
|
|
/%[0-9a-fA-F]{2}%[0-9a-fA-F]{2}/
|
|
];
|
|
var normalizeRelPath = (input) => {
|
|
const trimmed = input.replace(/\0/g, "").trim();
|
|
return trimmed.replace(/^[/\\]+/, "");
|
|
};
|
|
var containsPathTraversal = (input) => {
|
|
const decoded = decodeURIComponentSafe(input);
|
|
return DANGEROUS_PATTERNS.some((pattern) => pattern.test(input) || pattern.test(decoded));
|
|
};
|
|
var containsDoubleEncoding = (input) => {
|
|
return DOUBLE_ENCODE_PATTERNS.some((pattern) => pattern.test(input));
|
|
};
|
|
var hasPathSecurityIssues = (input) => {
|
|
return containsPathTraversal(input) || containsDoubleEncoding(input);
|
|
};
|
|
var decodeURIComponentSafe = (input) => {
|
|
try {
|
|
return decodeURIComponent(input);
|
|
} catch {
|
|
return input;
|
|
}
|
|
};
|
|
var resolveNotebookPath = (relPath) => {
|
|
if (hasPathSecurityIssues(relPath)) {
|
|
throw new AccessDeniedError("Path traversal detected");
|
|
}
|
|
const safeRelPath = normalizeRelPath(relPath);
|
|
const notebookRoot = path.resolve(NOTEBOOK_ROOT);
|
|
const fullPath = path.resolve(notebookRoot, safeRelPath);
|
|
if (!fullPath.startsWith(notebookRoot)) {
|
|
throw new AccessDeniedError("Access denied");
|
|
}
|
|
return { safeRelPath, fullPath };
|
|
};
|
|
|
|
export {
|
|
ValidationError,
|
|
NotFoundError,
|
|
BadRequestError,
|
|
NotADirectoryError,
|
|
AlreadyExistsError,
|
|
ForbiddenError,
|
|
UnsupportedMediaTypeError,
|
|
ResourceLockedError,
|
|
InternalError,
|
|
isAppError,
|
|
isNodeError,
|
|
resolveNotebookPath
|
|
};
|