Remove unused isValidHttpUrl function and related tests

Remove commented-out model validation regex from constants
```
This commit is contained in:
2026-05-07 00:02:32 +02:00
parent cec8302dbe
commit 75fa07f148
4 changed files with 124 additions and 168 deletions
-2
View File
@@ -7,5 +7,3 @@ export const DEFAULT_SETTINGS = {
maxMessageHistory: 50,
lastIndexTime: 0,
};
// Model validation regex - lowercase letters, numbers, dashes, underscores only
+124 -135
View File
@@ -1,176 +1,165 @@
"use strict";
'use strict';
// src/utils.ts
Object.defineProperty(exports, "__esModule", { value: true });
Object.defineProperty(exports, '__esModule', { value: true });
exports.Logger = void 0;
exports.validateOllamaUrl = validateOllamaUrl;
exports.validateModelName = validateModelName;
exports.validatePluginSettings = validatePluginSettings;
exports.safeParseJson = safeParseJson;
exports.sanitizeFilePath = sanitizeFilePath;
exports.isValidHttpUrl = isValidHttpUrl;
// ==================== Logger ====================
var LogLevel;
(function (LogLevel) {
LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
LogLevel[LogLevel["INFO"] = 1] = "INFO";
LogLevel[LogLevel["WARN"] = 2] = "WARN";
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
LogLevel[(LogLevel['DEBUG'] = 0)] = 'DEBUG';
LogLevel[(LogLevel['INFO'] = 1)] = 'INFO';
LogLevel[(LogLevel['WARN'] = 2)] = 'WARN';
LogLevel[(LogLevel['ERROR'] = 3)] = 'ERROR';
})(LogLevel || (LogLevel = {}));
const SEVERITY_ORDER = {
debug: LogLevel.DEBUG,
info: LogLevel.INFO,
warn: LogLevel.WARN,
error: LogLevel.ERROR,
debug: LogLevel.DEBUG,
info: LogLevel.INFO,
warn: LogLevel.WARN,
error: LogLevel.ERROR,
};
class Logger {
static setLevel(level) {
if (typeof level === 'string') {
const lowerLevel = level.toLowerCase();
Logger.minLevel = SEVERITY_ORDER[lowerLevel] ?? LogLevel.DEBUG;
}
else {
Logger.minLevel = level;
}
static setLevel(level) {
if (typeof level === 'string') {
const lowerLevel = level.toLowerCase();
Logger.minLevel = SEVERITY_ORDER[lowerLevel] ?? LogLevel.DEBUG;
} else {
Logger.minLevel = level;
}
static debug(message, category = 'general') {
if (LogLevel.DEBUG >= Logger.minLevel) {
console.debug(`[${category}] DEBUG: ${message}`);
}
}
static debug(message, category = 'general') {
if (LogLevel.DEBUG >= Logger.minLevel) {
console.debug(`[${category}] DEBUG: ${message}`);
}
static info(message, category = 'general') {
if (LogLevel.INFO >= Logger.minLevel) {
console.info(`[${category}] INFO: ${message}`);
}
}
static info(message, category = 'general') {
if (LogLevel.INFO >= Logger.minLevel) {
console.info(`[${category}] INFO: ${message}`);
}
static warn(message, category = 'general') {
if (LogLevel.WARN >= Logger.minLevel) {
console.warn(`[${category}] WARN: ${message}`);
}
}
static warn(message, category = 'general') {
if (LogLevel.WARN >= Logger.minLevel) {
console.warn(`[${category}] WARN: ${message}`);
}
static error(message, category = 'general') {
if (LogLevel.ERROR >= Logger.minLevel) {
console.error(`[${category}] ERROR: ${message}`);
}
}
static error(message, category = 'general') {
if (LogLevel.ERROR >= Logger.minLevel) {
console.error(`[${category}] ERROR: ${message}`);
}
}
}
exports.Logger = Logger;
Logger.minLevel = LogLevel.DEBUG;
// ==================== URL & Model Validation ====================
function validateOllamaUrl(url) {
if (typeof url !== 'string' || !url.trim()) {
return { valid: false, error: 'URL cannot be empty' };
}
const trimmedUrl = url.trim();
if (trimmedUrl.endsWith('/')) {
return { valid: false, error: 'URL should not end with a slash' };
}
try {
const parsed = new URL(trimmedUrl);
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
return { valid: false, error: 'Must be a valid HTTP or HTTPS URL' };
}
return { valid: true };
}
catch {
return { valid: false, error: 'Must be a valid HTTP or HTTPS URL' };
}
}
function validateModelName(model) {
if (typeof model !== 'string') {
return { valid: false, error: 'Model name must be a string' };
}
const trimmedModel = model.trim();
// Explicit check for empty string after trimming
if (!trimmedModel || trimmedModel.length === 0) {
return { valid: false, error: 'Model name cannot be empty' };
}
if (trimmedModel.length < 2) {
return { valid: false, error: 'Model name must be at least 2 characters long' };
}
if (trimmedModel.length > 100) {
return { valid: false, error: 'Model name must be less than 100 characters long' };
}
if (!/^[a-zA-Z0-9._:-]+$/.test(trimmedModel)) {
return {
valid: false,
error: 'Model name can only contain letters, numbers, dots, dashes, underscores, and colons',
};
if (typeof url !== 'string' || !url.trim()) {
return { valid: false, error: 'URL cannot be empty' };
}
const trimmedUrl = url.trim();
if (trimmedUrl.endsWith('/')) {
return { valid: false, error: 'URL should not end with a slash' };
}
try {
const parsed = new URL(trimmedUrl);
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
return { valid: false, error: 'Must be a valid HTTP or HTTPS URL' };
}
return { valid: true };
} catch {
return { valid: false, error: 'Must be a valid HTTP or HTTPS URL' };
}
}
function validateModelName(model) {
if (typeof model !== 'string') {
return { valid: false, error: 'Model name must be a string' };
}
const trimmedModel = model.trim();
// Explicit check for empty string after trimming
if (!trimmedModel || trimmedModel.length === 0) {
return { valid: false, error: 'Model name cannot be empty' };
}
if (trimmedModel.length < 2) {
return { valid: false, error: 'Model name must be at least 2 characters long' };
}
if (trimmedModel.length > 100) {
return { valid: false, error: 'Model name must be less than 100 characters long' };
}
if (!/^[a-zA-Z0-9._:-]+$/.test(trimmedModel)) {
return {
valid: false,
error: 'Model name can only contain letters, numbers, dots, dashes, underscores, and colons',
};
}
return { valid: true };
}
function validatePluginSettings(settings) {
const errors = [];
const urlValidation = validateOllamaUrl(settings.ollamaUrl);
if (!urlValidation.valid) {
errors.push(`Invalid Ollama URL: ${urlValidation.error}`);
}
const modelValidation = validateModelName(settings.model);
if (!modelValidation.valid) {
errors.push(`Invalid Model Name: ${modelValidation.error}`);
}
return errors;
const errors = [];
const urlValidation = validateOllamaUrl(settings.ollamaUrl);
if (!urlValidation.valid) {
errors.push(`Invalid Ollama URL: ${urlValidation.error}`);
}
const modelValidation = validateModelName(settings.model);
if (!modelValidation.valid) {
errors.push(`Invalid Model Name: ${modelValidation.error}`);
}
return errors;
}
// ==================== Safe JSON Parsing ====================
const MAX_JSON_SIZE = 1000000;
const MAX_JSON_NESTING = 24;
function countNestingDepth(value, depth = 0) {
if (depth > MAX_JSON_NESTING) {
return depth;
}
if (Array.isArray(value)) {
return Math.max(...value.map((item) => countNestingDepth(item, depth + 1)), depth);
}
if (value !== null && typeof value === 'object') {
const entries = Object.values(value);
if (entries.length === 0)
return depth;
return Math.max(...entries.map((item) => countNestingDepth(item, depth + 1)), depth);
}
if (depth > MAX_JSON_NESTING) {
return depth;
}
if (Array.isArray(value)) {
return Math.max(...value.map((item) => countNestingDepth(item, depth + 1)), depth);
}
if (value !== null && typeof value === 'object') {
const entries = Object.values(value);
if (entries.length === 0) return depth;
return Math.max(...entries.map((item) => countNestingDepth(item, depth + 1)), depth);
}
return depth;
}
function safeParseJson(jsonString) {
if (typeof jsonString !== 'string') {
throw new Error('Input must be a string');
}
if (jsonString.length > MAX_JSON_SIZE) {
throw new Error('JSON input too large');
}
let parsed;
try {
parsed = JSON.parse(jsonString);
}
catch {
throw new Error('Invalid JSON');
}
// Check for dangerous prototype pollution patterns
const reStringified = JSON.stringify(parsed);
if (reStringified.includes('constructor') ||
reStringified.includes('prototype') ||
reStringified.includes('__proto__') ||
reStringified.includes('function')) {
throw new Error('dangerous code pattern detected');
}
// Check nesting depth
if (countNestingDepth(parsed) > MAX_JSON_NESTING) {
throw new Error('JSON nesting too deep');
}
return parsed;
if (typeof jsonString !== 'string') {
throw new Error('Input must be a string');
}
if (jsonString.length > MAX_JSON_SIZE) {
throw new Error('JSON input too large');
}
let parsed;
try {
parsed = JSON.parse(jsonString);
} catch {
throw new Error('Invalid JSON');
}
// Check for dangerous prototype pollution patterns
const reStringified = JSON.stringify(parsed);
if (
reStringified.includes('constructor') ||
reStringified.includes('prototype') ||
reStringified.includes('__proto__') ||
reStringified.includes('function')
) {
throw new Error('dangerous code pattern detected');
}
// Check nesting depth
if (countNestingDepth(parsed) > MAX_JSON_NESTING) {
throw new Error('JSON nesting too deep');
}
return parsed;
}
// ==================== Path & File Utilities ====================
function sanitizeFilePath(path) {
if (path.includes('..')) {
throw new Error('Invalid path - cannot contain .. segments');
}
return path;
}
// ==================== HTTP Helpers ====================
function isValidHttpUrl(url) {
try {
const parsed = new URL(url);
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
}
catch {
return false;
}
if (path.includes('..')) {
throw new Error('Invalid path - cannot contain .. segments');
}
return path;
}
// ==================== Markdown Utilities ====================
-13
View File
@@ -188,17 +188,4 @@ export function safeParseJson(jsonString: string): unknown {
return parsed;
}
// ==================== Path & File Utilities ====================
// ==================== HTTP Helpers ====================
export function isValidHttpUrl(url: string): boolean {
try {
const parsed = new URL(url);
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
} catch {
return false;
}
}
// ==================== Markdown Utilities ====================
-18
View File
@@ -7,7 +7,6 @@ import {
validateModelName,
validatePluginSettings,
safeParseJson,
isValidHttpUrl,
} from '../src/utils';
describe('Validation Functions', () => {
@@ -185,23 +184,6 @@ describe('Validation Functions', () => {
expect(() => safeParseJson(123 as any)).toThrow('Input must be a string');
});
});
describe('isValidHttpUrl', () => {
it('should accept valid HTTP URLs', () => {
expect(isValidHttpUrl('http://localhost:11434')).toBe(true);
expect(isValidHttpUrl('https://example.com')).toBe(true);
});
it('should reject non-HTTP URLs', () => {
expect(isValidHttpUrl('ftp://example.com')).toBe(false);
expect(isValidHttpUrl('file:///path')).toBe(false);
});
it('should reject invalid URLs', () => {
expect(isValidHttpUrl('not-a-url')).toBe(false);
expect(isValidHttpUrl('')).toBe(false);
});
});
});
describe('Logger', () => {