Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>Cipheron — Secure Code Review Assistant</title> | |
| <style> | |
| :root { color-scheme: light dark; } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; | |
| max-width: 820px; | |
| margin: 0 auto; | |
| padding: 24px 16px 60px; | |
| line-height: 1.5; | |
| } | |
| h1 { font-size: 1.5rem; margin-bottom: 4px; } | |
| .sub { opacity: 0.7; margin-top: 0; font-size: 0.95rem; } | |
| .note { | |
| background: rgba(127,127,127,0.12); | |
| border-radius: 8px; | |
| padding: 10px 14px; | |
| font-size: 0.85rem; | |
| margin: 16px 0; | |
| } | |
| #status { | |
| font-size: 0.9rem; | |
| margin: 12px 0; | |
| padding: 10px 14px; | |
| border-radius: 8px; | |
| background: rgba(127,127,127,0.12); | |
| } | |
| progress { width: 100%; height: 10px; margin-top: 6px; } | |
| #chat { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| margin: 20px 0; | |
| min-height: 100px; | |
| } | |
| .msg { padding: 10px 14px; border-radius: 10px; white-space: pre-wrap; font-size: 0.95rem; } | |
| .msg.user { background: rgba(59,130,246,0.15); align-self: flex-end; max-width: 85%; } | |
| .msg.assistant { background: rgba(127,127,127,0.12); align-self: flex-start; max-width: 90%; } | |
| .msg.assistant code, .msg.assistant pre { font-family: ui-monospace, Consolas, monospace; } | |
| form { display: flex; gap: 8px; margin-top: 12px; } | |
| textarea { | |
| flex: 1; | |
| resize: vertical; | |
| min-height: 70px; | |
| padding: 10px; | |
| border-radius: 8px; | |
| border: 1px solid rgba(127,127,127,0.4); | |
| font-family: inherit; | |
| font-size: 0.95rem; | |
| } | |
| button { | |
| padding: 10px 18px; | |
| border-radius: 8px; | |
| border: none; | |
| background: #3b82f6; | |
| color: white; | |
| font-size: 0.95rem; | |
| cursor: pointer; | |
| } | |
| button:disabled { opacity: 0.5; cursor: not-allowed; } | |
| a { color: #3b82f6; } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>🔐 Cipheron</h1> | |
| <p class="sub">Secure code review assistant — 0.5B params, runs entirely in your browser (nothing sent to a server).</p> | |
| <div class="note"> | |
| Reliably good at: <b>SQL injection, command injection</b> fixes.<br /> | |
| Weaker at: path traversal, hardcoded secrets, weak hashing, insecure deserialization, XSS — | |
| the training data had very few examples of these. | |
| See the <a href="https://huggingface.co/bencodez/Cipheron" target="_blank">model card</a> for details. | |
| </div> | |
| <div id="status">Click "Load model" to download Cipheron (~500MB, cached by your browser after the first time).</div> | |
| <progress id="progress" value="0" max="1" style="display:none"></progress> | |
| <button id="loadBtn">Load model</button> | |
| <div id="chat"></div> | |
| <form id="form" style="display:none"> | |
| <textarea id="input" placeholder="Paste code and ask for a security review...">Review this code for security issues and fix it: | |
| def get_user(username): | |
| query = "SELECT * FROM users WHERE username = '" + username + "'" | |
| return db.execute(query)</textarea> | |
| <button id="sendBtn" type="submit">Send</button> | |
| </form> | |
| <script type="module"> | |
| import { Wllama } from "https://cdn.jsdelivr.net/npm/@wllama/wllama@2/esm/index.js"; | |
| const CONFIG_PATHS = { | |
| "single-thread/wllama.wasm": "https://cdn.jsdelivr.net/npm/@wllama/wllama@2/src/single-thread/wllama.wasm", | |
| "multi-thread/wllama.wasm": "https://cdn.jsdelivr.net/npm/@wllama/wllama@2/src/multi-thread/wllama.wasm", | |
| }; | |
| const MODEL_URL = "https://huggingface.co/bencodez/Cipheron/resolve/main/Cipheron-Q8_0.gguf"; | |
| const SYSTEM_PROMPT = "You are a secure coding assistant. Review code for security vulnerabilities and provide fixed, secure versions."; | |
| const statusEl = document.getElementById("status"); | |
| const progressEl = document.getElementById("progress"); | |
| const loadBtn = document.getElementById("loadBtn"); | |
| const form = document.getElementById("form"); | |
| const input = document.getElementById("input"); | |
| const sendBtn = document.getElementById("sendBtn"); | |
| const chatEl = document.getElementById("chat"); | |
| let wllama = null; | |
| let history = []; | |
| function addMessage(role, content) { | |
| const div = document.createElement("div"); | |
| div.className = "msg " + role; | |
| div.textContent = content; | |
| chatEl.appendChild(div); | |
| div.scrollIntoView({ behavior: "smooth", block: "end" }); | |
| return div; | |
| } | |
| function buildPrompt(messages) { | |
| let s = ""; | |
| for (const m of messages) { | |
| s += `<|im_start|>${m.role}\n${m.content}<|im_end|>\n`; | |
| } | |
| s += "<|im_start|>assistant\n"; | |
| return s; | |
| } | |
| loadBtn.addEventListener("click", async () => { | |
| loadBtn.disabled = true; | |
| progressEl.style.display = "block"; | |
| statusEl.textContent = "Downloading model..."; | |
| try { | |
| wllama = new Wllama(CONFIG_PATHS); | |
| await wllama.loadModelFromUrl(MODEL_URL, { | |
| n_ctx: 1024, | |
| progressCallback: ({ loaded, total }) => { | |
| if (total) { | |
| progressEl.value = loaded / total; | |
| statusEl.textContent = `Downloading model... ${(loaded / 1e6).toFixed(0)}MB / ${(total / 1e6).toFixed(0)}MB`; | |
| } | |
| }, | |
| }); | |
| statusEl.textContent = "Model loaded. Ask a question below."; | |
| progressEl.style.display = "none"; | |
| loadBtn.style.display = "none"; | |
| form.style.display = "flex"; | |
| } catch (err) { | |
| statusEl.textContent = "Failed to load model: " + err.message; | |
| loadBtn.disabled = false; | |
| } | |
| }); | |
| form.addEventListener("submit", async (e) => { | |
| e.preventDefault(); | |
| const userText = input.value.trim(); | |
| if (!userText || !wllama) return; | |
| addMessage("user", userText); | |
| history.push({ role: "user", content: userText }); | |
| input.value = ""; | |
| sendBtn.disabled = true; | |
| const assistantDiv = addMessage("assistant", ""); | |
| const messages = [{ role: "system", content: SYSTEM_PROMPT }, ...history]; | |
| const prompt = buildPrompt(messages); | |
| let full = ""; | |
| try { | |
| await wllama.createCompletion(prompt, { | |
| nPredict: 400, | |
| sampling: { temp: 0.5 }, | |
| onNewToken: (token, piece, currentText) => { | |
| full = currentText; | |
| assistantDiv.textContent = full; | |
| assistantDiv.scrollIntoView({ behavior: "smooth", block: "end" }); | |
| }, | |
| }); | |
| } catch (err) { | |
| full = "Error: " + err.message; | |
| assistantDiv.textContent = full; | |
| } | |
| history.push({ role: "assistant", content: full }); | |
| sendBtn.disabled = false; | |
| }); | |
| </script> | |
| </body> | |
| </html> | |