Spaces:
Running
Running
File size: 6,226 Bytes
3d50167 798bcc6 3d50167 798bcc6 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
// ===== KIMI SECURITY & VALIDATION CONFIGURATION =====
window.KIMI_SECURITY_CONFIG = {
// Input validation limits
MAX_MESSAGE_LENGTH: 5000,
MAX_API_KEY_LENGTH: 200,
MIN_API_KEY_LENGTH: 10,
// Security settings
API_KEY_PATTERNS: [
/^sk-or-v1-[a-zA-Z0-9]{16,}$/, // OpenRouter pattern (relaxed length)
/^sk-[a-zA-Z0-9_\-]{16,}$/, // OpenAI and similar (relaxed)
/^[a-zA-Z0-9_\-]{16,}$/ // Generic API key fallback
],
// Cache settings
CACHE_MAX_AGE: 300000, // 5 minutes
CACHE_MAX_SIZE: 100,
// Performance settings
DEBOUNCE_DELAY: 300,
BATCH_DELAY: 800,
THROTTLE_LIMIT: 1000,
// Error messages
ERRORS: {
INVALID_INPUT: "Invalid input provided",
MESSAGE_TOO_LONG: "Message too long. Please keep it under {max} characters.",
INVALID_API_KEY: "Invalid API key format",
NETWORK_ERROR: "Network error. Please check your connection.",
SYSTEM_ERROR: "System error occurred. Please try again."
}
};
// Validation utilities using the configuration
window.KIMI_VALIDATORS = {
validateMessage: message => {
if (!message || typeof message !== "string") return { valid: false, error: "INVALID_INPUT" };
if (message.length > window.KIMI_SECURITY_CONFIG.MAX_MESSAGE_LENGTH) {
return {
valid: false,
error: "MESSAGE_TOO_LONG",
params: { max: window.KIMI_SECURITY_CONFIG.MAX_MESSAGE_LENGTH }
};
}
return { valid: true };
},
validateApiKey: key => {
if (!key || typeof key !== "string") return false;
if (key.length < window.KIMI_SECURITY_CONFIG.MIN_API_KEY_LENGTH) return false;
if (key.length > window.KIMI_SECURITY_CONFIG.MAX_API_KEY_LENGTH) return false;
return window.KIMI_SECURITY_CONFIG.API_KEY_PATTERNS.some(pattern => pattern.test(key));
},
validateSliderValue: (value, type) => {
// Use centralized config from KIMI_CONFIG
if (window.KIMI_CONFIG && window.KIMI_CONFIG.validate) {
return window.KIMI_CONFIG.validate(value, type);
}
// Fallback if config not available
const num = parseFloat(value);
if (isNaN(num)) return { valid: false, value: 0 };
return { valid: true, value: num };
}
};
window.KIMI_SECURITY_INITIALIZED = true;
// ===== Global Input Hardening (anti-autofill and password manager suppression) =====
(function setupGlobalInputHardening() {
try {
const ATTRS = {
autocomplete: "off",
autocapitalize: "none",
autocorrect: "off",
spellcheck: "false",
inputmode: "text",
"aria-autocomplete": "none",
"data-lpignore": "true",
"data-1p-ignore": "true",
"data-bwignore": "true",
"data-form-type": "other"
};
const API_INPUT_ID = "openrouter-api-key";
function hardenElement(el) {
if (!el || !(el instanceof HTMLElement)) return;
const tag = el.tagName;
if (tag !== "INPUT" && tag !== "TEXTAREA") return;
// Do not convert other inputs to password; only enforce anti-autofill attributes
for (const [k, v] of Object.entries(ATTRS)) {
try {
if (el.getAttribute(k) !== v) el.setAttribute(k, v);
} catch {}
}
// Special handling for the API key field: ensure it's treated as non-credential by managers
if (el.id === API_INPUT_ID) {
try {
// Keep password type by default for masking; JS toggler can switch to text on demand
if (!el.hasAttribute("type")) el.setAttribute("type", "password");
// Explicitly set a non-credential-ish name/value context
if (el.getAttribute("name") !== "openrouter_api_key") el.setAttribute("name", "openrouter_api_key");
if (el.getAttribute("autocomplete") !== "new-password") el.setAttribute("autocomplete", "new-password");
} catch {}
} else {
// For non-API inputs, if browser set type=password by heuristics, revert to text
try {
if (el.getAttribute("type") === "password" && el.id !== API_INPUT_ID) {
el.setAttribute("type", "text");
}
} catch {}
}
}
function hardenAll(scope = document) {
const nodes = scope.querySelectorAll("input, textarea");
nodes.forEach(hardenElement);
}
// Initial pass
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => hardenAll());
} else {
hardenAll();
}
// Observe dynamic DOM changes
const mo = new MutationObserver(mutations => {
for (const m of mutations) {
if (m.type === "childList") {
m.addedNodes.forEach(node => {
if (node.nodeType === 1) {
if (node.matches && (node.matches("input") || node.matches("textarea"))) {
hardenElement(node);
}
const descendants = node.querySelectorAll ? node.querySelectorAll("input, textarea") : [];
descendants.forEach(hardenElement);
}
});
}
}
});
try {
mo.observe(document.documentElement || document.body, {
subtree: true,
childList: true
});
} catch {}
// Expose for debugging if needed
window._kimiInputHardener = { hardenAll };
} catch (e) {
// Fail-safe: never block the app
console.warn("Input hardening setup error:", e);
}
})();
|