Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ COPY --from=builder /opt/venv /opt/venv
COPY app.py fetch_and_build.py generate_og_card.py specs.py db.py cache.py storage.py schema.sql ./
COPY signing.py mcp_trust.py open_lookup.py verify_log.py mcp_server.py auth.py usage.py ./
# Generator inputs: curated seed, scorecard cache, and templates/assets
COPY agents.json template.html ./
COPY agents.json template.html safety_facts.json ./
COPY --from=builder /build/scorecard-cache.json ./scorecard-cache.json
COPY --from=builder /build/mcp-registry-snapshot.json ./mcp-registry-snapshot.json
COPY templates/ templates/
Expand Down
65 changes: 65 additions & 0 deletions fetch_and_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -2882,6 +2882,69 @@ def build_adopters(rows: list[dict]) -> list[dict]:
}


# Q4 plan E2/E3: reviewed, sourced safety facts for selected profiles: who runs
# the product, what it can reach, where data goes and its security practice.
# They answer what "is X safe?" searchers ask and repo signals can't
# (docs/research/is-x-safe-question-audit-2026-10-06.md). Facts are shown,
# never scored, and a profile without an entry renders exactly as before.
SAFETY_FACTS_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "safety_facts.json")
SAFETY_FACT_TOPICS = (("operator", "Who runs it"), ("access", "What it can reach"),
("data", "Where your data goes"), ("security", "Security practice"))
# Evidence class -> the chip shown beside the fact.
SAFETY_FACT_CLASSES = {"declared": "PUBLISHER", "observed": "CHECKED", "source": "IN CODE"}


def safety_fact_problems(doc) -> list[str]:
"""Why a safety_facts.json document is malformed (an empty list means valid)."""
if not isinstance(doc, dict) or not isinstance(doc.get("profiles"), dict):
return ["no profiles object"]
topics = {t for t, _ in SAFETY_FACT_TOPICS}
problems = []
for slug, entry in doc["profiles"].items():
if not re.fullmatch(r"\d{4}-\d{2}-\d{2}", str((entry or {}).get("checked", ""))):
problems.append(f"{slug}: checked is not a YYYY-MM-DD date")
facts = (entry or {}).get("facts")
if not isinstance(facts, list) or not facts:
problems.append(f"{slug}: no facts")
continue
for i, fact in enumerate(facts):
where = f"{slug}[{i}]"
if fact.get("topic") not in topics:
problems.append(f"{where}: unknown topic {fact.get('topic')!r}")
if fact.get("class") not in SAFETY_FACT_CLASSES:
problems.append(f"{where}: unknown class {fact.get('class')!r}")
if not str(fact.get("text") or "").strip():
problems.append(f"{where}: empty text")
if not str(fact.get("source") or "").startswith("https://"):
problems.append(f"{where}: source must be an https URL")
return problems


def load_safety_facts(path: str = SAFETY_FACTS_PATH) -> dict[str, dict]:
"""slug -> {"checked", "groups": [{"key", "heading", "facts": [...]}]}, topics in
a fixed order. A missing or malformed file yields {} so a bad edit can never
break a render; tests/test_safety_facts.py rejects one before it ships."""
try:
with open(path, encoding="utf-8") as f:
doc = json.load(f)
except (OSError, json.JSONDecodeError):
return {}
if safety_fact_problems(doc):
return {}
out = {}
for slug, entry in doc["profiles"].items():
groups = []
for key, heading in SAFETY_FACT_TOPICS:
facts = [{**f, "label": SAFETY_FACT_CLASSES[f["class"]],
"source_host": (urlsplit(f["source"]).hostname or "").removeprefix("www.")}
for f in entry["facts"] if f["topic"] == key]
if facts:
groups.append({"key": key, "heading": heading, "facts": facts})
out[slug] = {"checked": entry["checked"], "groups": groups,
"has_source": any(f["class"] == "source" for f in entry["facts"])}
return out


def agent_review_insights(row: dict) -> dict:
"""Summarize the trust score in plain language for agent profile pages."""
score = row.get("trust_score") or 0
Expand Down Expand Up @@ -8529,7 +8592,9 @@ def _fetch_hn(row: dict) -> None:
for _lst in cat_sorted_rows.values():
_lst.sort(key=lambda x: x.get("category_rank") or 9999)
# Add category_slug so agent pages can link to category pages
safety_facts = load_safety_facts()
for row in rows + legacy_rows:
row["safety_facts"] = safety_facts.get(row.get("slug"))
row["category_slug"] = slugify(row.get("category", "")) if row.get("category") else ""
owner = row["repo"].split("/")[0].lower()
row["org_slug_or_none"] = owner if owner in org_slug_set else None
Expand Down
107 changes: 107 additions & 0 deletions safety_facts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
{
"_about": "Reviewed safety facts shown on profile pages (Q4 plan E2/E3; gaps in docs/research/is-x-safe-question-audit-2026-10-06.md). Every fact names its public source and its evidence class: 'observed' (we checked it ourselves on the date given), 'declared' (the publisher's own statement, not verified by us) or 'source' (read from the project's code or repository). Facts never change scores or ranks. Add a fact only with a source; remove it when the source changes or disappears. Topics: operator, access, data, security.",
"profiles": {
"composio": {
"checked": "2026-10-06",
"facts": [
{"topic": "operator", "class": "declared", "text": "Run by Sampark Inc, doing business as Composio. Its terms are governed by Delaware law.", "source": "https://composio.dev/terms"},
{"topic": "access", "class": "declared", "text": "An agent using Composio can act in every app you connect, within the scopes you granted. Composio's own advice is to treat those scopes like production credentials.", "source": "https://docs.composio.dev/docs/security/overview"},
{"topic": "data", "class": "declared", "text": "Composio's cloud stores the OAuth access and refresh tokens and API keys of the apps you connect, and uses them to call those apps on your behalf. Using your own OAuth app doesn't change that.", "source": "https://docs.composio.dev/docs/security/token-custody"},
{"topic": "data", "class": "declared", "text": "Those credentials are encrypted at rest (AES-256-GCM) with keys Composio holds by default. Customer-managed keys, a private VPC or self-hosting are enterprise arrangements.", "source": "https://docs.composio.dev/docs/security/token-custody"},
{"topic": "data", "class": "declared", "text": "Connected-account tokens are redacted from API responses by default, and a Zero Data Retention option stops call payloads from being stored.", "source": "https://docs.composio.dev/docs/security/overview"},
{"topic": "security", "class": "declared", "text": "States SOC 2 Type II compliance. The report and the sub-processor list are in its trust center, trust.composio.dev. Vulnerability reports go to security@composio.dev.", "source": "https://docs.composio.dev/docs/security/overview"},
{"topic": "security", "class": "observed", "text": "GitHub private vulnerability reporting is enabled on the repository, which has a SECURITY.md. composio.dev serves no security.txt.", "source": "https://github.com/composiohq/composio/security"}
]
},
"cline": {
"checked": "2026-10-06",
"facts": [
{"topic": "operator", "class": "declared", "text": "Made by Cline Bot Inc.", "source": "https://cline.bot/tos"},
{"topic": "access", "class": "declared", "text": "Runs in your editor or terminal and can read and edit files, run terminal commands, use a browser and call MCP servers. Each kind of action has its own auto-approve setting.", "source": "https://docs.cline.bot/features/auto-approve"},
{"topic": "access", "class": "source", "text": "In the VS Code extension, reading and editing files, using the browser and calling MCP servers are auto-approved by default; terminal commands ask first.", "source": "https://github.com/cline/cline/blob/main/apps/vscode/src/shared/AutoApprovalSettings.ts"},
{"topic": "data", "class": "declared", "text": "Works with the model provider you choose (Anthropic, OpenAI, OpenRouter and others), or with a local model through Ollama or LM Studio.", "source": "https://github.com/cline/cline#readme"},
{"topic": "data", "class": "declared", "text": "Cline's own telemetry excludes code, file contents and paths, command arguments, conversation content and API keys, and can be turned off in settings.", "source": "https://docs.cline.bot/more-info/telemetry"},
{"topic": "security", "class": "observed", "text": "cline.bot publishes a security.txt (security@cline.bot), the repository has a SECURITY.md, and GitHub private vulnerability reporting is enabled.", "source": "https://cline.bot/.well-known/security.txt"},
{"topic": "security", "class": "observed", "text": "Its maintainers have published 3 security advisories (1 critical, 1 high, 1 low), all in the last 12 months.", "source": "https://github.com/cline/cline/security/advisories"}
]
},
"firecrawl": {
"checked": "2026-10-06",
"facts": [
{"topic": "operator", "class": "declared", "text": "Run by SideGuide Technologies, Inc., doing business as Firecrawl, a Delaware corporation.", "source": "https://www.firecrawl.dev/terms-of-service"},
{"topic": "data", "class": "declared", "text": "You can run Firecrawl on your own servers instead of using its hosted API.", "source": "https://github.com/firecrawl/firecrawl/blob/main/SELF_HOST.md"},
{"topic": "security", "class": "declared", "text": "States SOC 2 Type 2. Vulnerability reports go to a Bugcrowd program with safe harbor.", "source": "https://www.firecrawl.dev/security"},
{"topic": "security", "class": "observed", "text": "firecrawl.dev publishes a security.txt. The repository has no SECURITY.md and GitHub private vulnerability reporting is off.", "source": "https://firecrawl.dev/.well-known/security.txt"},
{"topic": "security", "class": "observed", "text": "Its maintainers have published 4 security advisories (1 critical, 3 high), 2 of them in the last 12 months.", "source": "https://github.com/firecrawl/firecrawl/security/advisories"}
]
},
"dify": {
"checked": "2026-10-06",
"facts": [
{"topic": "operator", "class": "declared", "text": "Run by LangGenius, Inc., a Delaware corporation.", "source": "https://dify.ai/terms"},
{"topic": "security", "class": "declared", "text": "States SOC 2 Type II (audited by Sensiba), ISO 27001:2022 (Johanson) and GDPR compliance, as of March 2026.", "source": "https://dify.ai/blog/dify-achieves-soc-2-iso-27001-gdpr-compliance-for-the-second-year-running"},
{"topic": "security", "class": "observed", "text": "Its maintainers have published 21 security advisories (8 high, 10 medium, 3 low), 14 of them in the last 12 months.", "source": "https://github.com/langgenius/dify/security/advisories"},
{"topic": "security", "class": "observed", "text": "The repository has a SECURITY.md and GitHub private vulnerability reporting is enabled. dify.ai serves no security.txt.", "source": "https://github.com/langgenius/dify/security"}
]
},
"e2b": {
"checked": "2026-10-06",
"facts": [
{"topic": "access", "class": "declared", "text": "Each sandbox runs in its own Firecracker microVM with its own kernel, so code is isolated at the hypervisor boundary rather than in a container.", "source": "https://e2b.dev/security"},
{"topic": "data", "class": "declared", "text": "Sandboxes are destroyed on timeout or shutdown. A paused sandbox keeps its filesystem and memory until it is resumed.", "source": "https://e2b.dev/security"},
{"topic": "security", "class": "declared", "text": "Holds a SOC 2 Type II report, available under NDA from trust.e2b.dev, and signs HIPAA BAAs on Enterprise plans. Vulnerability reports go to security@e2b.dev; testing against E2B needs prior written consent.", "source": "https://e2b.dev/security"},
{"topic": "security", "class": "observed", "text": "GitHub private vulnerability reporting is enabled on the SDK repository, which has no SECURITY.md. e2b.dev serves no security.txt.", "source": "https://github.com/e2b-dev/e2b/security"}
]
},
"claude-code": {
"checked": "2026-10-06",
"facts": [
{"topic": "operator", "class": "observed", "text": "Made by Anthropic. The repository's licence reserves all rights and puts use under Anthropic's Commercial Terms, so it isn't open source.", "source": "https://github.com/anthropics/claude-code"},
{"topic": "access", "class": "declared", "text": "Interactive sessions start in Auto mode, where a classifier model approves or blocks actions. In Manual mode it starts read-only and asks before editing files or running commands.", "source": "https://code.claude.com/docs/en/security"},
{"topic": "access", "class": "declared", "text": "Its file tools ask before reading or writing outside the folder it started in, but a shell command you approve can write anywhere your user account can unless you turn on its sandbox.", "source": "https://code.claude.com/docs/en/security"},
{"topic": "data", "class": "declared", "text": "Prompts and model output go to Anthropic, or to Bedrock, Google Cloud or Microsoft Foundry if you configure one. Commercial accounts: kept 30 days and not used for training. Consumer accounts: used for training when that setting is on (kept 5 years), otherwise kept 30 days.", "source": "https://code.claude.com/docs/en/data-usage"},
{"topic": "data", "class": "declared", "text": "Session transcripts are stored on your machine in plain text under ~/.claude/projects/ for 30 days by default. Usage metrics exclude code, prompts and file paths and can be turned off with DISABLE_TELEMETRY=1.", "source": "https://code.claude.com/docs/en/data-usage"},
{"topic": "security", "class": "declared", "text": "Anthropic publishes its SOC 2 Type 2 report and ISO 27001 certificate in its trust center, trust.anthropic.com. Vulnerabilities go to a HackerOne program.", "source": "https://code.claude.com/docs/en/security"},
{"topic": "security", "class": "observed", "text": "Its maintainers have published 33 security advisories (26 high, 4 medium, 3 low), 23 of them in the last 12 months. The repository has a SECURITY.md.", "source": "https://github.com/anthropics/claude-code/security/advisories"}
]
},
"codex": {
"checked": "2026-10-06",
"facts": [
{"topic": "operator", "class": "observed", "text": "Made by OpenAI. The CLI is open source under Apache-2.0.", "source": "https://github.com/openai/codex"},
{"topic": "access", "class": "declared", "text": "In a version-controlled folder it recommends its Auto preset, which reads, edits and runs commands in the working directory without asking. Writes stay in that workspace, and network access is off unless you enable it.", "source": "https://learn.chatgpt.com/docs/agent-approvals-security"},
{"topic": "access", "class": "declared", "text": "Those limits are enforced by the operating system: Seatbelt on macOS, bwrap with seccomp on Linux, and a WSL2 or native sandbox on Windows.", "source": "https://learn.chatgpt.com/docs/agent-approvals-security"},
{"topic": "security", "class": "observed", "text": "openai.com's security.txt points to a Bugcrowd program, and the repository has a SECURITY.md.", "source": "https://openai.com/.well-known/security.txt"},
{"topic": "security", "class": "observed", "text": "Its maintainers have published 1 security advisory (high), in September 2025.", "source": "https://github.com/openai/codex/security/advisories"}
]
},
"livekit-agents": {
"checked": "2026-10-06",
"facts": [
{"topic": "operator", "class": "declared", "text": "The Agents framework is open source under Apache-2.0. Agents connect to LiveKit Cloud, the company's hosted service, or to a self-hosted LiveKit server.", "source": "https://github.com/livekit/agents#readme"},
{"topic": "data", "class": "declared", "text": "LiveKit Cloud doesn't keep media or transcripts by default: recordings are written to your own S3, GCS or Azure bucket, and prompts, audio and completions for AI inference aren't logged.", "source": "https://livekit.com/security"},
{"topic": "security", "class": "declared", "text": "States SOC 2 Type II, GDPR and CCPA compliance and EU–US Data Privacy Framework certification, and signs HIPAA BAAs on Scale and Enterprise plans. Reports are in its trust center, trust.livekit.io.", "source": "https://livekit.com/security"},
{"topic": "security", "class": "observed", "text": "livekit.io publishes a security.txt (security@livekit.io), and GitHub private vulnerability reporting is enabled on the repository. Its maintainers have published 1 security advisory (medium), in the last 12 months.", "source": "https://livekit.io/.well-known/security.txt"}
]
},
"freebuff": {
"checked": "2026-10-06",
"facts": [
{"topic": "operator", "class": "declared", "text": "Run by Freebuff, Inc., which also operates Codebuff.", "source": "https://freebuff.com/privacy-policy"},
{"topic": "data", "class": "declared", "text": "It collects prompts, code, files and repository data, which are processed by its own systems and by AI model providers.", "source": "https://freebuff.com/privacy-policy"},
{"topic": "data", "class": "declared", "text": "It shows ads: prompts and messages may be analysed to select and measure them, including by deriving general interest categories.", "source": "https://freebuff.com/privacy-policy"},
{"topic": "data", "class": "declared", "text": "It doesn't train models on your prompts or project data unless it says so before you use that model or feature.", "source": "https://freebuff.com/privacy-policy"},
{"topic": "security", "class": "observed", "text": "freebuff.com's security.txt lists support@codebuff.com. The repository has a SECURITY.md; GitHub private vulnerability reporting is off.", "source": "https://freebuff.com/.well-known/security.txt"}
]
},
"kilo-code": {
"checked": "2026-10-06",
"facts": [
{"topic": "operator", "class": "declared", "text": "Run by Anaconda, Inc. with its affiliate Kilo Code Inc.", "source": "https://kilo.ai/terms"},
{"topic": "data", "class": "declared", "text": "Paid plans can switch off retention of AI prompts and outputs, with controls over which model providers requests are routed to.", "source": "https://kilo.ai/security-and-compliance"},
{"topic": "security", "class": "declared", "text": "SOC 2 documentation is available in its trust center, trust.kilo.ai. The page doesn't say which report type.", "source": "https://kilo.ai/security-and-compliance"},
{"topic": "security", "class": "observed", "text": "kilo.ai publishes a security.txt (security@kilo.ai), and the repository has a SECURITY.md.", "source": "https://kilo.ai/.well-known/security.txt"}
]
}
}
}
Loading
Loading