Arx / library /build_knowledge.py
umer-wasim's picture
Rename to Arx (display name) with a new fortress icon; knowledge base: Who is Arx
f42ab02 verified
Raw History Blame Contribute Delete
12.4 kB
#!/usr/bin/env python3
"""Build library/knowledge.json: the passages the chat assistant answers from (and cites).
Generated from the catalog and library so the chat never drifts from the audit:
fix how to fix / why it matters, per check and OS (check_guidance.json + catalog rule + references)
control ISO 27001 / GDPR / EU AI Act control summaries (controls.json)
tool AI tool profiles (ai_tools.json) and EU AI Act risk categories
phone where a setting is on iPhone / Android (catalog/mobile_questions.json)
status what each audit status means
faq the auditor itself: what it checks, privacy, install, phone check
Usage: python3 library/build_knowledge.py
"""
import json
import os
import re
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
OS_LABEL = {"windows": "Windows", "linux": "Linux", "macos": "macOS", "ios": "iPhone", "android": "Android"}
def load(*parts):
with open(os.path.join(ROOT, *parts), encoding="utf-8") as handle:
return json.load(handle)
def read_conf(path):
conf = {}
for line in open(os.path.join(ROOT, path), encoding="utf-8"):
if "=" in line and not line.lstrip().startswith("#"):
key, value = line.split("=", 1)
conf[key.strip()] = value.strip().strip('"')
return conf
def fill(text, baseline):
"""Same placeholder rules as the report builder (training/build_dataset.py), including derived values."""
sys.path.insert(0, os.path.join(ROOT, "training"))
from build_dataset import fill as report_fill
return report_fill(text, baseline)
FAQ = [
("What the Devseis Endpoint Auditor is",
"The Devseis Endpoint Auditor is a free, open-source, read-only check of a computer or phone against ISO 27001, GDPR "
"technical controls and the EU AI Act. It reads security settings, compares them with a baseline, matches installed "
"software against known vulnerabilities, and an AI auditor that runs on the device writes a report with findings, "
"risk levels and fix steps. It is not a certification and not legal advice."),
("Does the audit change anything?",
"No. The audit only reads settings such as password rules, firewall, disk encryption and installed software. It never "
"changes them. Administrator rights are requested because some settings can only be read by an administrator."),
("Privacy: where do audit results go?",
"Nowhere. Evidence and reports stay on the device. The app downloads only two things: the AI auditor model and a signed "
"list of known vulnerabilities. Nothing about the device is uploaded. Chat history is stored only in this browser."),
("How to run an audit of a computer",
"A web page cannot read a computer's settings, so computers are audited with the free Devseis desktop app for Mac, "
"Windows (beta) and Linux. Download it from the Download page, install it, click Run audit and enter the computer "
"password once. The first run downloads the AI auditor; the audit then takes about 5 to 15 minutes and ends with a PDF report."),
("How to install on a Mac",
"Open the downloaded .dmg file and drag Devseis Endpoint Auditor to Applications. The first time, macOS says it could not "
"verify the app because it is not yet notarised: click Done, then open System Settings, Privacy & Security, and click "
"Open Anyway. After that it opens normally. It needs macOS 13 or newer."),
("How to install on Windows",
"Run the downloaded setup .exe. Windows SmartScreen shows 'Windows protected your PC' because the app is not yet "
"code-signed: click More info, then Run anyway. Click Next, Install and Finish. When the audit starts, click Yes in the "
"User Account Control prompt. The Windows version is a beta."),
("How to install on Linux",
"On Ubuntu, Debian, Linux Mint or Pop!_OS, double-click the .deb file and click Install. On other distributions, make "
"the AppImage executable (Properties, Permissions, Allow executing file as program) and double-click it. The audit asks "
"for the password with the normal desktop prompt."),
("How the phone check works",
"Phones are checked in the browser: open the Devseis page on the iPhone or Android phone and start the phone check. The "
"browser detects what it can (system version, whether a screen lock is set up) and asks the rest with help on where to "
"look in Settings. Every result is labelled detected or self-reported. The report can be saved or shared as a PDF."),
("What self-reported and detected mean",
"Detected means the browser or the app read the value itself. Self-reported means the user answered a question; the "
"value was not verified on the device. Phone checks are mostly self-reported because browsers cannot read phone settings."),
("What is in the audit report",
"The report has a compliance score, the most important gaps, and for each problem: what was found, why it matters, the "
"risk level, step-by-step fixes for that operating system, how to verify the fix, and the related ISO 27001, GDPR and EU "
"AI Act references. It also lists AI tools found and their EU AI Act risk class."),
("Does the auditor need internet?",
"Only the first time, to download the AI auditor and the vulnerability list. After that audits and chat work offline. "
"Pending security updates can only be checked online because they ask the vendor."),
("Settings for organisations",
"Before running audits, an organisation can edit baseline.conf (approved administrator accounts, approved AI tools, "
"limits such as screen lock within 5 minutes) and organisational.conf (answers to 8 organisational questions such as "
"record of processing, DPIA and breach procedure). The desktop app keeps them in its settings folder."),
("The Devseis AI auditor model",
"The assistant and the report writer are a small language model fine-tuned by Devseis from Qwen2.5 and run on the "
"device with WebLLM. Audit verdicts are decided by rules in code, not by the model; the model explains, prioritises and "
"writes, and every report answer is checked against the evidence before it is used."),
("Who is Arx?",
"Arx is the name of the Devseis security assistant and endpoint auditor (from the Latin for fortress). Arx answers "
"questions about securing computers and phones, ISO 27001, GDPR and the EU AI Act, and audits devices: computers with the "
"Devseis Endpoint Auditor desktop app, phones with the phone check in the browser. Arx runs on the device; nothing is uploaded."),
]
def main():
catalog = load("catalog", "checks.json")
guidance = load("library", "check_guidance.json")
controls = load("library", "controls.json")
tools = load("library", "ai_tools.json")
phone = load("catalog", "mobile_questions.json")
baseline = read_conf("baseline.conf")
refs = catalog["references"]
ctl = {(c["framework"], c["id"]): c for c in controls["controls"]}
chunks = []
def add(cid, kind, title, text, os_family=None, check=None):
chunks.append({"id": cid, "kind": kind, "title": title, "text": re.sub(r"\s+", " ", text).strip(),
**({"os": os_family} if os_family else {}), **({"check": check} if check else {})})
def ref_text(check):
out = []
for key, label in (("iso27001", "ISO 27001:2022"), ("gdpr", "GDPR"), ("ai_act", "EU AI Act")):
for r in check["controls"][key]:
title = (ctl.get((label, r)) or {}).get("title") or refs[key].get(r, "")
out.append(f"{label} {r}" + (f" ({title})" if title else ""))
return "; ".join(out)
for check in catalog["checks"]:
if check["section"] == "org":
g = guidance["checks"].get(guidance["aliases"].get(check["id"], check["id"]))
add(f"fix:{check['id']}", "fix", check["name"],
f"{check['name']} is an organisational control, answered once by the organisation, not read from a device. "
f"{g['why'] if g else ''} Rule: {check['rule']} Related: {ref_text(check)}.", check=check["id"])
continue
g = guidance["checks"].get(guidance["aliases"].get(check["id"], check["id"]))
if not g:
continue
for os_family in check["os"]:
name = (check.get("names") or {}).get(os_family, check["name"])
steps = g["fix"].get("all", []) + g["fix"].get(os_family, [])
text = (f"{name} on {OS_LABEL[os_family]}. Why it matters: {g['why']} Risk if missing: {g['risk']['level']} - "
f"{g['risk']['reason']} Requirement: {fill(check['rule'], baseline)} How to fix on {OS_LABEL[os_family]}: "
+ " ".join(f"({i}) {fill(s, baseline)}" for i, s in enumerate(steps, 1))
+ f" How to verify: {fill(g['verify'], baseline)} Related controls: {ref_text(check)}.")
add(f"fix:{check['id']}:{os_family}", "fix", f"{name} on {OS_LABEL[os_family]}", text, os_family, check["id"])
for c in controls["controls"]:
add(f"control:{c['framework']}:{c['id']}", "control", f"{c['framework']} {c['id']} {c['title']}",
f"{c['framework']} {c['id']} ({c['title']}): {c['summary']} An auditor looks for: {'; '.join(c.get('auditor_looks_for', []))}. "
f"On endpoints: {c.get('endpoint_relevance', '')}")
for name, t in tools["tools"].items():
add(f"tool:{name}", "tool", f"{name} (AI tool)",
f"{name}: {t['kind']}, provider {t['provider']}, data location: {t['data_location']}. Default EU AI Act risk class "
f"for general office use: {t['default_risk']}. GDPR: {t['gdpr_notes']} Whether it is allowed at work depends on the "
f"organisation's approved AI tools list and on the purpose: some purposes make any AI tool high-risk or prohibited.")
rules = tools["purpose_rules"]
for key, title in (("prohibited", "EU AI Act prohibited AI practices"), ("high_risk_examples", "EU AI Act high-risk AI uses"),
("limited_risk", "EU AI Act limited-risk AI (transparency duties)"), ("minimal_risk", "EU AI Act minimal-risk AI")):
items = rules[key] if isinstance(rules[key], list) else [rules[key]]
add(f"aiact:{key}", "control", title, f"{title}: " + "; ".join(items) + ".")
for check_id, per_os in phone["checks"].items():
for os_family, spec in per_os.items():
name = next(((c.get("names") or {}).get(os_family, c["name"]) for c in catalog["checks"] if c["id"] == check_id), check_id)
add(f"phone:{check_id}:{os_family}", "phone", f"Where to find {name} on {OS_LABEL[os_family]}",
f"{name} on {OS_LABEL[os_family]}: {spec['question']} Where to look: {spec['where']}", os_family, check_id)
notes = guidance["status_notes"]
add("status:all", "status", "What the audit statuses mean",
"Compliant: the setting meets the baseline. Non-Compliant: it does not meet the baseline. "
f"Pending: {notes['Pending']} NotApplicable: {notes['NotApplicable']} Error: {notes['Error']}")
counts = {o: sum(o in c["os"] for c in catalog["checks"]) for o in OS_LABEL}
add("faq:checks", "faq", "How many checks the audit runs",
"The audit catalog has " + str(len(catalog["checks"])) + " checks: " + ", ".join(f"{OS_LABEL[o]} {n}" for o, n in counts.items())
+ ". They cover passwords and lockout, malware protection, firewall, screen lock, USB storage, administrator accounts, "
"disk encryption, updates and known vulnerabilities, GDPR technical controls (backups, logging, remote access, "
"telemetry and more), EU AI Act controls (AI tools found, shadow AI, risk classification) and 8 organisational controls.")
for i, (title, text) in enumerate(FAQ):
add(f"faq:{i}", "faq", title, text)
out = {"version": catalog["catalog_version"], "chunks": chunks}
with open(os.path.join(ROOT, "library", "knowledge.json"), "w", encoding="utf-8") as handle:
json.dump(out, handle, ensure_ascii=False, indent=0)
kinds = {}
for c in chunks:
kinds[c["kind"]] = kinds.get(c["kind"], 0) + 1
print(f"knowledge.json: {len(chunks)} passages {kinds}")
if __name__ == "__main__":
main()