Platform
Security for developers
The marketing security page covers compliance and policy. This page covers the things you'll actually paste into code — HMAC verification, secret rotation, and what we do (and don't do) with your repo source.
Verifying webhook signatures
Every outbound webhook carries an X-QLens-Signature header of the form sha256=<hex>. The signature is HMAC-SHA-256 of the raw request body using your shared secret as the key.
Node.js (Express)
import crypto from "node:crypto";
import express from "express";
const app = express();
// IMPORTANT: keep the body raw — do not parse before verifying.
app.post(
"/qlens",
express.raw({ type: "application/json" }),
(req, res) => {
const sig = req.header("X-QLens-Signature") ?? "";
const expected =
"sha256=" +
crypto
.createHmac("sha256", process.env.QLENS_WEBHOOK_SECRET!)
.update(req.body) // Buffer, not parsed JSON
.digest("hex");
const ok =
sig.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
if (!ok) return res.status(401).send("bad signature");
const event = JSON.parse(req.body.toString("utf8"));
// ...handle event...
res.status(204).end();
},
);Python (Flask)
import hmac, hashlib, os
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = os.environ["QLENS_WEBHOOK_SECRET"].encode()
@app.post("/qlens")
def qlens_hook():
raw = request.get_data() # raw bytes
sig = request.headers.get("X-QLens-Signature", "")
expected = "sha256=" + hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(sig, expected):
abort(401)
event = request.get_json()
# ...handle event...
return "", 204Always use a constant-time compare (timingSafeEqual/compare_digest). A naive == leaks via timing channels.
Rotating secrets
Secrets you control:
- API keys (
qlens_…) — rotate at /dashboard/keys. Old keys stop working immediately on revoke. - Webhook signing secret — rotate at /dashboard/settings. Both old and new secrets are accepted for a 24-hour grace window to give your receiver time to roll.
- Slack webhook URL — managed in Slack, pasted into our settings. No grace window: if you change it, paste the new URL immediately.
What we read from your repo
For auto-fix proposals, the GitHub App requests these read scopes only on opted-in repos:
- Contents — to read failing test files and their direct imports.
- Pull requests — to open the fix PR and update its body.
- Checks — to attach the auto-fix status check.
We don't mirror your code. Source files are read transiently per proposal, passed to the LLM with a short retention window, and dropped after the patch is generated. The diff we keep is the patch itself, scoped to the lines you'd see in the PR.
Reporting a vulnerability
Please email security@iklab.dev rather than opening a public issue. We respond within one business day, and we'll confirm a fix timeline within five.