Spaces:
Running
Running
File size: 6,317 Bytes
32ebdde be0a1de 32ebdde | 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 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | <!doctype html>
<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>
|