feat: the closed-source ladder, and why self-attestation is a declaration

Christian: 'The goal is not to hamstring closed source developers. It's just to
validate clean code.'

Right. So the cap is a rung, not a wall, and it now says so.

  L0 unattested   -> 3    nothing but your word
  L1 declared     -> 5    SBOM + signed hash + published destination allowlist
  L2 observed     -> 7    someone other than you watched this exact binary
  L3 reproducible -> 9    anyone can rebuild it and get the same bytes
  L4 contested    -> 10   someone tried to break it and published the attempt

A court filing lifts the ceiling entirely, because it needs no vendor cooperation.

db/attest/validate.mjs computes the level from which artifacts exist. A vendor
cannot claim one. The load-bearing rule is the attestor column:

  a vendor running our harness produces a DECLARATION, not an OBSERVATION.

Verified against three cases. A vendor claiming L2 with their own capture report
is downgraded to L1 and fails validation. The same artifacts with us as attestor
grant L2. A broken rebuild claim never reaches L3.

Levels 3 and 4 survive a self-run on purpose: a public rebuild recipe and a
published adversarial review can be re-executed or re-read by anyone, so who
pressed the button stops mattering.

Honest limit, written into the module: this cannot prove the absence of a
backdoor. Rice's theorem makes that undecidable, and a backdoor triggered by
remote config or message content is invisible to static inspection. An
attestation raises the cost and shortens the shelf life. It is not a proof.
This commit is contained in:
CLU 2026-10-29 16:07:55 -05:00
parent 83cca68965
commit 0979f032dc
7 changed files with 264 additions and 59 deletions

View file

@ -0,0 +1,147 @@
#!/usr/bin/env node
/**
* Attestation validator.
*
* A vendor may NOT claim a level. This computes the level from which artifacts
* actually exist, and downgrades self-run observation to a declaration.
*
* The core rule:
* A vendor running our harness produces a DECLARATION, not an OBSERVATION.
* Declarations cap at level 1. Levels 3 and 4 survive a self-run because a
* public rebuild recipe and a published adversarial review can be re-checked
* by anyone who wants to.
*
* What this can and cannot do, stated plainly:
* CAN bound a specific binary, enumerate what it talks to, and prove that a
* rebuild reproduces it bit for bit.
* CANNOT prove the absence of a backdoor. Rice's theorem makes that undecidable
* for arbitrary code, and a backdoor delivered by remote configuration or
* triggered by message content is not visible to a static scan.
* So the honest claim is: an attestation RAISES THE COST of a backdoor and
* SHORTENS ITS SHELF LIFE. It is not a proof of purity.
*
* Usage:
* node validate.mjs <attestation.json> compute the level for one file
* node validate.mjs --example print a filled example
*/
import { readFileSync } from 'node:fs';
/** Highest level whose required artifacts are all present. */
export function artifactsLevel(a) {
let level = 0;
const has = (v) => v !== null && v !== undefined && v !== '';
if (has(a.sbom_url) && has(a.signed_hash_url) && has(a.destination_allowlist_url)) {
level = 1;
}
if (level >= 1 && has(a.capture_report)) {
level = 2;
}
if (level >= 2 && has(a.rebuild_recipe_url) && a.rebuild_matches === 1) {
level = 3;
}
if (level >= 3 && has(a.adversarial_report_url) && a.differential_clean === 1) {
level = 4;
}
return level;
}
/** Apply the trust rule: who ran it decides what the result is worth. */
export function effectiveLevel(a) {
let level = artifactsLevel(a);
// A self-observed capture is a claim about oneself. Downgrade to declaration.
if (a.attestor === 'vendor' && level === 2) level = 1;
// Levels 3 and 4 are not weakened by self-running: the recipe and the review
// are public artifacts that a third party can re-execute or re-read.
return level;
}
/** Maximum score a dimension may reach at a given effective level. */
export const DIMENSION_CAP = {
'verifiability': { 0: 3, 1: 4, 2: 5, 3: 9, 4: 10 },
'telemetry_egress': { 0: 3, 1: 5, 2: 7, 3: 9, 4: 10 },
'default': { 0: 3, 1: 5, 2: 7, 3: 9, 4: 10 },
};
export function capFor(dimensionId, level) {
const table = DIMENSION_CAP[dimensionId] ?? DIMENSION_CAP.default;
return table[level] ?? 3;
}
/** Full validation report for one attestation. */
export function validate(a, { now = new Date() } = {}) {
const problems = [];
const level = effectiveLevel(a);
const raw = artifactsLevel(a);
if (raw > level) {
problems.push(
`claim of level ${raw} reduced to ${level}: attestor is '${a.attestor}', ` +
`and a self-run observation is a declaration, not an observation`,
);
}
if (!a.artifact_sha256 || !/^[a-f0-9]{64}$/.test(a.artifact_sha256)) {
problems.push('artifact_sha256 missing or not a 64-char lowercase hex digest');
}
if (!a.version) problems.push('no version: an attestation must name the release it covers');
if (a.expires_on && new Date(a.expires_on) <= now) {
problems.push(`expired on ${a.expires_on}: every new build invalidates the certificate`);
}
if (level >= 3 && a.rebuild_matches !== 1) {
problems.push('level 3 requires rebuild_matches = 1');
}
if (level >= 4 && !a.adversarial_report_url) {
problems.push('level 4 requires a published adversarial review');
}
return {
level,
claimed_artifacts_level: raw,
valid: problems.length === 0,
problems,
caps: {
verifiability: capFor('verifiability', level),
other_dimensions: capFor('default', level),
},
};
}
// ------------------------------------------------------------------ cli
if (process.argv[1] && process.argv[1].endsWith('validate.mjs')) {
const arg = process.argv[2];
if (!arg || arg === '--example') {
console.log(JSON.stringify({
product_id: 'slack',
version: '4.52.182',
artifact_sha256: '0'.repeat(64),
sbom_url: 'https://example.com/slack-4.52.182.cdx.json',
signed_hash_url: 'https://example.com/slack-4.52.182.sig',
destination_allowlist_url: 'https://example.com/slack-destinations.txt',
capture_report: null,
rebuild_recipe_url: null,
rebuild_matches: null,
adversarial_report_url: null,
differential_clean: null,
issued_on: '2026-10-29',
expires_on: '2026-11-29',
auditor: 'CLU',
attestor: 'vendor',
}, null, 2));
process.exit(0);
}
const a = JSON.parse(readFileSync(arg, 'utf8'));
const r = validate(a);
console.log(`effective level : ${r.level}`);
console.log(`artifacts level : ${r.claimed_artifacts_level}`);
console.log(`valid : ${r.valid}`);
console.log(`caps : verifiability <= ${r.caps.verifiability}, others <= ${r.caps.other_dimensions}`);
if (r.problems.length) {
console.log('problems:');
for (const p of r.problems) console.log(` - ${p}`);
}
process.exit(r.valid ? 0 : 1);
}

View file

@ -15,6 +15,7 @@
import { DatabaseSync } from 'node:sqlite'; import { DatabaseSync } from 'node:sqlite';
import { readFileSync, writeFileSync, readdirSync, mkdirSync, existsSync, unlinkSync } from 'node:fs'; import { readFileSync, writeFileSync, readdirSync, mkdirSync, existsSync, unlinkSync } from 'node:fs';
import { createHash } from 'node:crypto'; import { createHash } from 'node:crypto';
import { effectiveLevel, capFor } from './attest/validate.mjs';
import { join, dirname } from 'node:path'; import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
@ -190,17 +191,32 @@ function verify() {
if (a.reproducible_build === 0) ok(v <= 3, `${a.id}: no reproducible build but verifiability ${v} > 3`); if (a.reproducible_build === 0) ok(v <= 3, `${a.id}: no reproducible build but verifiability ${v} > 3`);
if (a.reproducible_build === 1) ok(v <= 6, `${a.id}: experimental reproducible build but verifiability ${v} > 6`); if (a.reproducible_build === 1) ok(v <= 6, `${a.id}: experimental reproducible build but verifiability ${v} > 6`);
// Closed source cannot exceed 5 on anything without independent evidence. // Closed source starts at level 0 and climbs a public ladder. The ceiling is
// Reputation is tier 3. Tier 3 is not a measurement. // not a punishment, it is the rung you are standing on.
//
// L0 unattested -> 3 (we have nothing but your word)
// L1 declared -> 5 (SBOM, signed hash, published destination allowlist)
// L2 observed -> 7 (someone other than you watched this exact binary)
// L3 reproducible -> 9 (anyone can rebuild it and get the same bytes)
// L4 contested -> 10 (someone tried to break it and published the attempt)
//
// A court filing is independent evidence and needs no vendor cooperation, so
// it lifts the ceiling entirely.
if (a.source_available === 0) { if (a.source_available === 0) {
const above = db.prepare('SELECT * FROM score WHERE assessment_id = ? AND value > 5').all(a.id); const att = db.prepare(`
for (const s of above) { SELECT * FROM attestation
const independent = db.prepare(` WHERE product_id = ? AND version = ?
ORDER BY issued_on DESC LIMIT 1`).get(a.product_id, a.version);
const level = att ? effectiveLevel(att) : 0;
const dims = db.prepare('SELECT * FROM score WHERE assessment_id = ?').all(a.id);
for (const s of dims) {
const filed = db.prepare(`
SELECT COUNT(*) AS n FROM evidence SELECT COUNT(*) AS n FROM evidence
WHERE assessment_id = ? AND dimension_id = ? WHERE assessment_id = ? AND dimension_id = ? AND tier <= 2 AND kind = 'filing'`)
AND tier <= 2 AND kind IN ('capture','filing')`).get(a.id, s.dimension_id).n; .get(a.id, s.dimension_id).n;
ok(independent > 0, const ceiling = filed > 0 ? 10 : capFor(s.dimension_id, level);
`${a.id}/${s.dimension_id}: closed source scored ${s.value} > 5 with no capture or court filing to back it`); ok(s.value <= ceiling,
`${a.id}/${s.dimension_id}: scored ${s.value} but the ceiling at attestation level ${level} is ${ceiling}`);
} }
} }
} }

View file

@ -136,6 +136,48 @@ CREATE TABLE IF NOT EXISTS discovery (
CREATE INDEX IF NOT EXISTS idx_discovery_product ON discovery(product_id); CREATE INDEX IF NOT EXISTS idx_discovery_product ON discovery(product_id);
-- ---------------------------------------------------------------- attestation
-- Closed source can earn its way up, but only by producing evidence that expires.
--
-- The level is NOT claimed by the vendor and NOT assigned by us. It is COMPUTED
-- from which required artifacts actually exist. A vendor can claim level 4 all day;
-- if the capture report is missing, the validator grants level 1.
--
-- Every attestation is bound to one artifact hash on one version. Any new build
-- invalidates it. There is no such thing as a standing certificate here.
CREATE TABLE IF NOT EXISTS attestation (
id INTEGER PRIMARY KEY AUTOINCREMENT,
product_id TEXT NOT NULL REFERENCES product(id),
version TEXT NOT NULL,
artifact_sha256 TEXT NOT NULL,
level INTEGER NOT NULL CHECK (level BETWEEN 0 AND 4),
-- L1 declared: the vendor tells the world what is in the box
sbom_url TEXT, -- CycloneDX or SPDX
signed_hash_url TEXT, -- signed release manifest
destination_allowlist_url TEXT, -- every host it may contact, and why
-- L2 observed: we watched this exact binary
capture_report TEXT, -- D1-D4 output, hash-bound
-- L3 reproducible: the binary is provably the build they claim
rebuild_recipe_url TEXT,
rebuild_matches INTEGER CHECK (rebuild_matches IN (0,1)),
-- L4 adversarial: someone tried to break it and published the attempt
adversarial_report_url TEXT,
differential_clean INTEGER CHECK (differential_clean IN (0,1)),
issued_on TEXT NOT NULL,
expires_on TEXT NOT NULL,
auditor TEXT NOT NULL,
-- Who ran the test. This is the load-bearing column.
-- A vendor running our harness produces a DECLARATION, not an observation.
-- Declarations are capped at level 1, because a self-run test is a claim.
-- Levels 3 and 4 survive a self-run, because a public rebuild recipe and a
-- published adversarial review can be re-checked by anyone.
attestor TEXT NOT NULL DEFAULT 'vendor'
CHECK (attestor IN ('vendor','binary-ranch','third-party','community')),
UNIQUE(product_id, version, artifact_sha256)
);
CREATE INDEX IF NOT EXISTS idx_attestation_product ON attestation(product_id, version);
-- ---------------------------------------------------------------- derived views -- ---------------------------------------------------------------- derived views
-- The single number the game and the report both use. -- The single number the game and the report both use.
CREATE VIEW IF NOT EXISTS v_product_score AS CREATE VIEW IF NOT EXISTS v_product_score AS

View file

@ -1,5 +1,5 @@
{ {
"generated_at": "2026-10-29T20:29:36.195Z", "generated_at": "2026-10-29T21:07:55.483Z",
"method_version": "btr-0.1", "method_version": "btr-0.1",
"dimensions": [ "dimensions": [
{ {
@ -2851,7 +2851,7 @@
], ],
"evidence": [ "evidence": [
{ {
"id": 22, "id": 33,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "local_data_exposure", "dimension_id": "local_data_exposure",
"claim": "Attachments are stored unencrypted on the filesystem; the SQLCipher key is held by the OS keyring.", "claim": "Attachments are stored unencrypted on the filesystem; the SQLCipher key is held by the OS keyring.",
@ -2862,7 +2862,7 @@
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 23, "id": 34,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "telemetry_egress", "dimension_id": "telemetry_egress",
"claim": "Electron media permission defaults to false and audio is denied unless the user has enabled mediaPermissions.", "claim": "Electron media permission defaults to false and audio is denied unless the user has enabled mediaPermissions.",
@ -2873,7 +2873,7 @@
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 24, "id": 35,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "telemetry_egress", "dimension_id": "telemetry_egress",
"claim": "Zero analytics, tracker or crash-upload dependencies across 224 packages.", "claim": "Zero analytics, tracker or crash-upload dependencies across 224 packages.",
@ -2884,7 +2884,7 @@
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 25, "id": 36,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "telemetry_egress", "dimension_id": "telemetry_egress",
"claim": "Only two audio capture call sites exist and both are reachable only from an explicit UI action.", "claim": "Only two audio capture call sites exist and both are reachable only from an explicit UI action.",
@ -2895,7 +2895,7 @@
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 26, "id": 37,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "verifiability", "dimension_id": "verifiability",
"claim": "Signal states reproducible builds for macOS and Windows are not available, and the Linux build is experimental.", "claim": "Signal states reproducible builds for macOS and Windows are not available, and the Linux build is experimental.",
@ -2906,7 +2906,7 @@
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 27, "id": 38,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "server_retention", "dimension_id": "server_retention",
"claim": "Grand jury subpoena response returned two timestamps and nothing else.", "claim": "Grand jury subpoena response returned two timestamps and nothing else.",
@ -2917,7 +2917,7 @@
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 28, "id": 39,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "ownership_jurisdiction", "dimension_id": "ownership_jurisdiction",
"claim": "Signal Technology Foundation is a 501(c)(3) with EIN 82-4506840.", "claim": "Signal Technology Foundation is a 501(c)(3) with EIN 82-4506840.",
@ -2928,7 +2928,7 @@
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 29, "id": 40,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "incident_record", "dimension_id": "incident_record",
"claim": "FBI and CISA PSA260626: Russian intelligence compromised individual Signal accounts via phishing and Backup Recovery Key theft, but NOT the encryption or the application itself.", "claim": "FBI and CISA PSA260626: Russian intelligence compromised individual Signal accounts via phishing and Backup Recovery Key theft, but NOT the encryption or the application itself.",
@ -2939,7 +2939,7 @@
"observed_at": "2026-10-29T15:26:00-05:00" "observed_at": "2026-10-29T15:26:00-05:00"
}, },
{ {
"id": 30, "id": 41,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "incident_record", "dimension_id": "incident_record",
"claim": "Signal statement, 28 April 2026: \"Signal was not hacked, in that our encryption, infrastructure, and the integrity of the app code was not compromised.\" Additional linkage confirmations shipped 13 May 2026.", "claim": "Signal statement, 28 April 2026: \"Signal was not hacked, in that our encryption, infrastructure, and the integrity of the app code was not compromised.\" Additional linkage confirmations shipped 13 May 2026.",
@ -2950,7 +2950,7 @@
"observed_at": "2026-10-29T15:26:00-05:00" "observed_at": "2026-10-29T15:26:00-05:00"
}, },
{ {
"id": 31, "id": 42,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "ownership_jurisdiction", "dimension_id": "ownership_jurisdiction",
"claim": "Open Technology Fund funded Open Whisper Systems $2,955,000 across 2013 to 2016, itemised on OTF own project page. This is the factual core under the \"US government funded Signal\" claim, and it is already reflected in the two points docked on this dimension.", "claim": "Open Technology Fund funded Open Whisper Systems $2,955,000 across 2013 to 2016, itemised on OTF own project page. This is the factual core under the \"US government funded Signal\" claim, and it is already reflected in the two points docked on this dimension.",
@ -2961,7 +2961,7 @@
"observed_at": "2026-10-29T15:26:00-05:00" "observed_at": "2026-10-29T15:26:00-05:00"
}, },
{ {
"id": 32, "id": 43,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "user_control_exit", "dimension_id": "user_control_exit",
"claim": "Signal publicly committed to leaving the UK (2023, Online Safety Act) and Sweden (Feb 2025) rather than weaken encryption. A vendor that threatens to lose a market is behaving incompatibly with a secret backdoor.", "claim": "Signal publicly committed to leaving the UK (2023, Online Safety Act) and Sweden (Feb 2025) rather than weaken encryption. A vendor that threatens to lose a market is behaving incompatibly with a secret backdoor.",
@ -2974,7 +2974,7 @@
], ],
"findings": [ "findings": [
{ {
"id": 13, "id": 17,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"severity": "high", "severity": "high",
"title": "Received attachments are stored unencrypted on disk", "title": "Received attachments are stored unencrypted on disk",
@ -2983,7 +2983,7 @@
"cve": "CVE-2023-24069" "cve": "CVE-2023-24069"
}, },
{ {
"id": 14, "id": 18,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"severity": "medium", "severity": "medium",
"title": "No verified reproducible build for the Linux package", "title": "No verified reproducible build for the Linux package",
@ -2992,7 +2992,7 @@
"cve": null "cve": null
}, },
{ {
"id": 15, "id": 19,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"severity": "medium", "severity": "medium",
"title": "Local database key is held in the OS keyring", "title": "Local database key is held in the OS keyring",
@ -3001,7 +3001,7 @@
"cve": null "cve": null
}, },
{ {
"id": 16, "id": 20,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"severity": "low", "severity": "low",
"title": "Registration requires a phone number", "title": "Registration requires a phone number",

View file

@ -39,7 +39,7 @@ export interface CatalogDimension {
} }
export const catalog = { export const catalog = {
"generated_at": "2026-10-29T20:29:36.195Z", "generated_at": "2026-10-29T21:07:55.483Z",
"method_version": "btr-0.1", "method_version": "btr-0.1",
"dimensions": [ "dimensions": [
{ {
@ -2891,7 +2891,7 @@ export const catalog = {
], ],
"evidence": [ "evidence": [
{ {
"id": 22, "id": 33,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "local_data_exposure", "dimension_id": "local_data_exposure",
"claim": "Attachments are stored unencrypted on the filesystem; the SQLCipher key is held by the OS keyring.", "claim": "Attachments are stored unencrypted on the filesystem; the SQLCipher key is held by the OS keyring.",
@ -2902,7 +2902,7 @@ export const catalog = {
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 23, "id": 34,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "telemetry_egress", "dimension_id": "telemetry_egress",
"claim": "Electron media permission defaults to false and audio is denied unless the user has enabled mediaPermissions.", "claim": "Electron media permission defaults to false and audio is denied unless the user has enabled mediaPermissions.",
@ -2913,7 +2913,7 @@ export const catalog = {
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 24, "id": 35,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "telemetry_egress", "dimension_id": "telemetry_egress",
"claim": "Zero analytics, tracker or crash-upload dependencies across 224 packages.", "claim": "Zero analytics, tracker or crash-upload dependencies across 224 packages.",
@ -2924,7 +2924,7 @@ export const catalog = {
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 25, "id": 36,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "telemetry_egress", "dimension_id": "telemetry_egress",
"claim": "Only two audio capture call sites exist and both are reachable only from an explicit UI action.", "claim": "Only two audio capture call sites exist and both are reachable only from an explicit UI action.",
@ -2935,7 +2935,7 @@ export const catalog = {
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 26, "id": 37,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "verifiability", "dimension_id": "verifiability",
"claim": "Signal states reproducible builds for macOS and Windows are not available, and the Linux build is experimental.", "claim": "Signal states reproducible builds for macOS and Windows are not available, and the Linux build is experimental.",
@ -2946,7 +2946,7 @@ export const catalog = {
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 27, "id": 38,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "server_retention", "dimension_id": "server_retention",
"claim": "Grand jury subpoena response returned two timestamps and nothing else.", "claim": "Grand jury subpoena response returned two timestamps and nothing else.",
@ -2957,7 +2957,7 @@ export const catalog = {
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 28, "id": 39,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "ownership_jurisdiction", "dimension_id": "ownership_jurisdiction",
"claim": "Signal Technology Foundation is a 501(c)(3) with EIN 82-4506840.", "claim": "Signal Technology Foundation is a 501(c)(3) with EIN 82-4506840.",
@ -2968,7 +2968,7 @@ export const catalog = {
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 29, "id": 40,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "incident_record", "dimension_id": "incident_record",
"claim": "FBI and CISA PSA260626: Russian intelligence compromised individual Signal accounts via phishing and Backup Recovery Key theft, but NOT the encryption or the application itself.", "claim": "FBI and CISA PSA260626: Russian intelligence compromised individual Signal accounts via phishing and Backup Recovery Key theft, but NOT the encryption or the application itself.",
@ -2979,7 +2979,7 @@ export const catalog = {
"observed_at": "2026-10-29T15:26:00-05:00" "observed_at": "2026-10-29T15:26:00-05:00"
}, },
{ {
"id": 30, "id": 41,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "incident_record", "dimension_id": "incident_record",
"claim": "Signal statement, 28 April 2026: \"Signal was not hacked, in that our encryption, infrastructure, and the integrity of the app code was not compromised.\" Additional linkage confirmations shipped 13 May 2026.", "claim": "Signal statement, 28 April 2026: \"Signal was not hacked, in that our encryption, infrastructure, and the integrity of the app code was not compromised.\" Additional linkage confirmations shipped 13 May 2026.",
@ -2990,7 +2990,7 @@ export const catalog = {
"observed_at": "2026-10-29T15:26:00-05:00" "observed_at": "2026-10-29T15:26:00-05:00"
}, },
{ {
"id": 31, "id": 42,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "ownership_jurisdiction", "dimension_id": "ownership_jurisdiction",
"claim": "Open Technology Fund funded Open Whisper Systems $2,955,000 across 2013 to 2016, itemised on OTF own project page. This is the factual core under the \"US government funded Signal\" claim, and it is already reflected in the two points docked on this dimension.", "claim": "Open Technology Fund funded Open Whisper Systems $2,955,000 across 2013 to 2016, itemised on OTF own project page. This is the factual core under the \"US government funded Signal\" claim, and it is already reflected in the two points docked on this dimension.",
@ -3001,7 +3001,7 @@ export const catalog = {
"observed_at": "2026-10-29T15:26:00-05:00" "observed_at": "2026-10-29T15:26:00-05:00"
}, },
{ {
"id": 32, "id": 43,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "user_control_exit", "dimension_id": "user_control_exit",
"claim": "Signal publicly committed to leaving the UK (2023, Online Safety Act) and Sweden (Feb 2025) rather than weaken encryption. A vendor that threatens to lose a market is behaving incompatibly with a secret backdoor.", "claim": "Signal publicly committed to leaving the UK (2023, Online Safety Act) and Sweden (Feb 2025) rather than weaken encryption. A vendor that threatens to lose a market is behaving incompatibly with a secret backdoor.",
@ -3014,7 +3014,7 @@ export const catalog = {
], ],
"findings": [ "findings": [
{ {
"id": 13, "id": 17,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"severity": "high", "severity": "high",
"title": "Received attachments are stored unencrypted on disk", "title": "Received attachments are stored unencrypted on disk",
@ -3023,7 +3023,7 @@ export const catalog = {
"cve": "CVE-2023-24069" "cve": "CVE-2023-24069"
}, },
{ {
"id": 14, "id": 18,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"severity": "medium", "severity": "medium",
"title": "No verified reproducible build for the Linux package", "title": "No verified reproducible build for the Linux package",
@ -3032,7 +3032,7 @@ export const catalog = {
"cve": null "cve": null
}, },
{ {
"id": 15, "id": 19,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"severity": "medium", "severity": "medium",
"title": "Local database key is held in the OS keyring", "title": "Local database key is held in the OS keyring",
@ -3041,7 +3041,7 @@ export const catalog = {
"cve": null "cve": null
}, },
{ {
"id": 16, "id": 20,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"severity": "low", "severity": "low",
"title": "Registration requires a phone number", "title": "Registration requires a phone number",

View file

@ -1,5 +1,5 @@
{ {
"generated_at": "2026-10-29T20:29:36.195Z", "generated_at": "2026-10-29T21:07:55.483Z",
"method_version": "btr-0.1", "method_version": "btr-0.1",
"dimensions": [ "dimensions": [
{ {
@ -2851,7 +2851,7 @@
], ],
"evidence": [ "evidence": [
{ {
"id": 22, "id": 33,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "local_data_exposure", "dimension_id": "local_data_exposure",
"claim": "Attachments are stored unencrypted on the filesystem; the SQLCipher key is held by the OS keyring.", "claim": "Attachments are stored unencrypted on the filesystem; the SQLCipher key is held by the OS keyring.",
@ -2862,7 +2862,7 @@
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 23, "id": 34,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "telemetry_egress", "dimension_id": "telemetry_egress",
"claim": "Electron media permission defaults to false and audio is denied unless the user has enabled mediaPermissions.", "claim": "Electron media permission defaults to false and audio is denied unless the user has enabled mediaPermissions.",
@ -2873,7 +2873,7 @@
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 24, "id": 35,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "telemetry_egress", "dimension_id": "telemetry_egress",
"claim": "Zero analytics, tracker or crash-upload dependencies across 224 packages.", "claim": "Zero analytics, tracker or crash-upload dependencies across 224 packages.",
@ -2884,7 +2884,7 @@
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 25, "id": 36,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "telemetry_egress", "dimension_id": "telemetry_egress",
"claim": "Only two audio capture call sites exist and both are reachable only from an explicit UI action.", "claim": "Only two audio capture call sites exist and both are reachable only from an explicit UI action.",
@ -2895,7 +2895,7 @@
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 26, "id": 37,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "verifiability", "dimension_id": "verifiability",
"claim": "Signal states reproducible builds for macOS and Windows are not available, and the Linux build is experimental.", "claim": "Signal states reproducible builds for macOS and Windows are not available, and the Linux build is experimental.",
@ -2906,7 +2906,7 @@
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 27, "id": 38,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "server_retention", "dimension_id": "server_retention",
"claim": "Grand jury subpoena response returned two timestamps and nothing else.", "claim": "Grand jury subpoena response returned two timestamps and nothing else.",
@ -2917,7 +2917,7 @@
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 28, "id": 39,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "ownership_jurisdiction", "dimension_id": "ownership_jurisdiction",
"claim": "Signal Technology Foundation is a 501(c)(3) with EIN 82-4506840.", "claim": "Signal Technology Foundation is a 501(c)(3) with EIN 82-4506840.",
@ -2928,7 +2928,7 @@
"observed_at": "2026-10-29T13:13:56-05:00" "observed_at": "2026-10-29T13:13:56-05:00"
}, },
{ {
"id": 29, "id": 40,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "incident_record", "dimension_id": "incident_record",
"claim": "FBI and CISA PSA260626: Russian intelligence compromised individual Signal accounts via phishing and Backup Recovery Key theft, but NOT the encryption or the application itself.", "claim": "FBI and CISA PSA260626: Russian intelligence compromised individual Signal accounts via phishing and Backup Recovery Key theft, but NOT the encryption or the application itself.",
@ -2939,7 +2939,7 @@
"observed_at": "2026-10-29T15:26:00-05:00" "observed_at": "2026-10-29T15:26:00-05:00"
}, },
{ {
"id": 30, "id": 41,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "incident_record", "dimension_id": "incident_record",
"claim": "Signal statement, 28 April 2026: \"Signal was not hacked, in that our encryption, infrastructure, and the integrity of the app code was not compromised.\" Additional linkage confirmations shipped 13 May 2026.", "claim": "Signal statement, 28 April 2026: \"Signal was not hacked, in that our encryption, infrastructure, and the integrity of the app code was not compromised.\" Additional linkage confirmations shipped 13 May 2026.",
@ -2950,7 +2950,7 @@
"observed_at": "2026-10-29T15:26:00-05:00" "observed_at": "2026-10-29T15:26:00-05:00"
}, },
{ {
"id": 31, "id": 42,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "ownership_jurisdiction", "dimension_id": "ownership_jurisdiction",
"claim": "Open Technology Fund funded Open Whisper Systems $2,955,000 across 2013 to 2016, itemised on OTF own project page. This is the factual core under the \"US government funded Signal\" claim, and it is already reflected in the two points docked on this dimension.", "claim": "Open Technology Fund funded Open Whisper Systems $2,955,000 across 2013 to 2016, itemised on OTF own project page. This is the factual core under the \"US government funded Signal\" claim, and it is already reflected in the two points docked on this dimension.",
@ -2961,7 +2961,7 @@
"observed_at": "2026-10-29T15:26:00-05:00" "observed_at": "2026-10-29T15:26:00-05:00"
}, },
{ {
"id": 32, "id": 43,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"dimension_id": "user_control_exit", "dimension_id": "user_control_exit",
"claim": "Signal publicly committed to leaving the UK (2023, Online Safety Act) and Sweden (Feb 2025) rather than weaken encryption. A vendor that threatens to lose a market is behaving incompatibly with a secret backdoor.", "claim": "Signal publicly committed to leaving the UK (2023, Online Safety Act) and Sweden (Feb 2025) rather than weaken encryption. A vendor that threatens to lose a market is behaving incompatibly with a secret backdoor.",
@ -2974,7 +2974,7 @@
], ],
"findings": [ "findings": [
{ {
"id": 13, "id": 17,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"severity": "high", "severity": "high",
"title": "Received attachments are stored unencrypted on disk", "title": "Received attachments are stored unencrypted on disk",
@ -2983,7 +2983,7 @@
"cve": "CVE-2023-24069" "cve": "CVE-2023-24069"
}, },
{ {
"id": 14, "id": 18,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"severity": "medium", "severity": "medium",
"title": "No verified reproducible build for the Linux package", "title": "No verified reproducible build for the Linux package",
@ -2992,7 +2992,7 @@
"cve": null "cve": null
}, },
{ {
"id": 15, "id": 19,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"severity": "medium", "severity": "medium",
"title": "Local database key is held in the OS keyring", "title": "Local database key is held in the OS keyring",
@ -3001,7 +3001,7 @@
"cve": null "cve": null
}, },
{ {
"id": 16, "id": 20,
"assessment_id": "A-2026-10-29-SIGNAL-DESKTOP", "assessment_id": "A-2026-10-29-SIGNAL-DESKTOP",
"severity": "low", "severity": "low",
"title": "Registration requires a phone number", "title": "Registration requires a phone number",

View file

@ -1,9 +1,9 @@
{ {
"digest": "ac0af0fe41910baecd8394bc837a623abdeae38ad851118ae37eb8862961248b", "digest": "0dc5a4129eef262900a70dbdc743d99aa7fb50bbd778cf7ab58ebbbe4ac2308a",
"algorithm": "sha256", "algorithm": "sha256",
"file": "catalog.json", "file": "catalog.json",
"byte_length": 82952, "byte_length": 82952,
"rebuilt_at": "2026-10-29T20:29:36.197Z", "rebuilt_at": "2026-10-29T21:07:55.485Z",
"method_version": "btr-0.1", "method_version": "btr-0.1",
"products": 123, "products": 123,
"assessed": 1 "assessed": 1