diff --git a/.gitignore b/.gitignore index 879add9b..9b9e01da 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,11 @@ dist/hacking/ # faces under public/cards/security/ are the committed record. # node scripts/security-pros.mjs all dist/security/ + +# Reference photographs the portraits are painted from. NOT committed: several are +# ordinary all-rights-reserved bio photos used with permission as references, and +# redistributing them in the repo is a different act from painting from them. +# data/security-references.json records where each came from, and +# node scripts/security-refs.mjs fetch +# re-downloads the lot. +assets/references/ diff --git a/README.md b/README.md index f04580ea..8b3254f3 100644 --- a/README.md +++ b/README.md @@ -87,8 +87,30 @@ pnpm security all 1 5 12 # ...only these card numbers pnpm security render # re-render faces from the template, no API calls ``` -Same stages and the same resumability as Series Two, with one deliberate -difference: **`all` does not run `enhance`.** On Series Two that image-to-image +Portraits are painted **from a real photograph of the person**: + +```bash +node scripts/security-refs.mjs resolve # find + verify a reference photo per card +node scripts/security-refs.mjs fetch # download them (gitignored) +node scripts/security-refs.mjs report # who still has no face +``` + +The first art pass generated faces from text prompts alone and produced an +invented stranger for every name. That art was withdrawn. Generation is now +image-to-image off `assets/references/security-pros/`, and **a card with no +verified reference photo gets no face** — the front renders a plate saying so. +40 of 50 currently have one; `data/security-references.json` records the source, +photographer and licence for each, and the card page prints the credit. + +Identity is verified, not assumed. A name search returns humans, not the right +human: "Michael Howard" resolves to the British politician, "Robert M. Lee" to a +Confederate general, "Mark Dowd" to a Liverpool councillor. The resolver requires +a surname match, rejects pre-1950 photographs, requires a Commons filename to +*open* with the person's full name, and carries an explicit reject list with +reasons. Everything it accepts was still checked by eye. + +The same stages and resumability as Series Two, with one deliberate difference: +**`all` does not run `enhance`.** On Series Two that image-to-image finish pass rewrote text on the card faces despite the prompt forbidding it — it fabricated quotes attributed to living people, invented stat panels that were not on the card, and misspelled a name. Nothing in the pipeline catches that, because diff --git a/assets/portraits/security-pros/card_001.png b/assets/portraits/security-pros/card_001.png index a984533f..d904bce0 100644 Binary files a/assets/portraits/security-pros/card_001.png and b/assets/portraits/security-pros/card_001.png differ diff --git a/assets/portraits/security-pros/card_002.png b/assets/portraits/security-pros/card_002.png index 33e3bf0e..c80a3625 100644 Binary files a/assets/portraits/security-pros/card_002.png and b/assets/portraits/security-pros/card_002.png differ diff --git a/assets/portraits/security-pros/card_003.png b/assets/portraits/security-pros/card_003.png index 99951f1a..08b95b2b 100644 Binary files a/assets/portraits/security-pros/card_003.png and b/assets/portraits/security-pros/card_003.png differ diff --git a/assets/portraits/security-pros/card_004.png b/assets/portraits/security-pros/card_004.png index 43e7367f..08cedcd7 100644 Binary files a/assets/portraits/security-pros/card_004.png and b/assets/portraits/security-pros/card_004.png differ diff --git a/assets/portraits/security-pros/card_005.png b/assets/portraits/security-pros/card_005.png index f4708450..5a876b38 100644 Binary files a/assets/portraits/security-pros/card_005.png and b/assets/portraits/security-pros/card_005.png differ diff --git a/assets/portraits/security-pros/card_006.png b/assets/portraits/security-pros/card_006.png index 7ddc4c60..36847797 100644 Binary files a/assets/portraits/security-pros/card_006.png and b/assets/portraits/security-pros/card_006.png differ diff --git a/assets/portraits/security-pros/card_007.png b/assets/portraits/security-pros/card_007.png index d790e1fc..8ed44fc6 100644 Binary files a/assets/portraits/security-pros/card_007.png and b/assets/portraits/security-pros/card_007.png differ diff --git a/assets/portraits/security-pros/card_008.png b/assets/portraits/security-pros/card_008.png index d9ab2566..b864b846 100644 Binary files a/assets/portraits/security-pros/card_008.png and b/assets/portraits/security-pros/card_008.png differ diff --git a/assets/portraits/security-pros/card_009.png b/assets/portraits/security-pros/card_009.png index debf88de..bdd32f19 100644 Binary files a/assets/portraits/security-pros/card_009.png and b/assets/portraits/security-pros/card_009.png differ diff --git a/assets/portraits/security-pros/card_010.png b/assets/portraits/security-pros/card_010.png index 795a6021..cb59e4e6 100644 Binary files a/assets/portraits/security-pros/card_010.png and b/assets/portraits/security-pros/card_010.png differ diff --git a/assets/portraits/security-pros/card_011.png b/assets/portraits/security-pros/card_011.png index e2787a28..17e688bf 100644 Binary files a/assets/portraits/security-pros/card_011.png and b/assets/portraits/security-pros/card_011.png differ diff --git a/assets/portraits/security-pros/card_012.png b/assets/portraits/security-pros/card_012.png index 22054c3a..6867f819 100644 Binary files a/assets/portraits/security-pros/card_012.png and b/assets/portraits/security-pros/card_012.png differ diff --git a/assets/portraits/security-pros/card_013.png b/assets/portraits/security-pros/card_013.png index 76ce6dc9..a917e03d 100644 Binary files a/assets/portraits/security-pros/card_013.png and b/assets/portraits/security-pros/card_013.png differ diff --git a/assets/portraits/security-pros/card_014.png b/assets/portraits/security-pros/card_014.png index 2770667e..99796cef 100644 Binary files a/assets/portraits/security-pros/card_014.png and b/assets/portraits/security-pros/card_014.png differ diff --git a/assets/portraits/security-pros/card_015.png b/assets/portraits/security-pros/card_015.png index 584188f7..d07daa8f 100644 Binary files a/assets/portraits/security-pros/card_015.png and b/assets/portraits/security-pros/card_015.png differ diff --git a/assets/portraits/security-pros/card_016.png b/assets/portraits/security-pros/card_016.png index bca299f2..780cd938 100644 Binary files a/assets/portraits/security-pros/card_016.png and b/assets/portraits/security-pros/card_016.png differ diff --git a/assets/portraits/security-pros/card_017.png b/assets/portraits/security-pros/card_017.png index 5d282fd8..9043a340 100644 Binary files a/assets/portraits/security-pros/card_017.png and b/assets/portraits/security-pros/card_017.png differ diff --git a/assets/portraits/security-pros/card_018.png b/assets/portraits/security-pros/card_018.png index dbc2054a..0bb698ae 100644 Binary files a/assets/portraits/security-pros/card_018.png and b/assets/portraits/security-pros/card_018.png differ diff --git a/assets/portraits/security-pros/card_019.png b/assets/portraits/security-pros/card_019.png index 4a150188..e789c3cd 100644 Binary files a/assets/portraits/security-pros/card_019.png and b/assets/portraits/security-pros/card_019.png differ diff --git a/assets/portraits/security-pros/card_020.png b/assets/portraits/security-pros/card_020.png index 8c946dec..1af7f25e 100644 Binary files a/assets/portraits/security-pros/card_020.png and b/assets/portraits/security-pros/card_020.png differ diff --git a/assets/portraits/security-pros/card_021.png b/assets/portraits/security-pros/card_021.png deleted file mode 100644 index 27355487..00000000 Binary files a/assets/portraits/security-pros/card_021.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_022.png b/assets/portraits/security-pros/card_022.png index 7b5ffa00..c49e22ca 100644 Binary files a/assets/portraits/security-pros/card_022.png and b/assets/portraits/security-pros/card_022.png differ diff --git a/assets/portraits/security-pros/card_023.png b/assets/portraits/security-pros/card_023.png index d8867a98..920b10d7 100644 Binary files a/assets/portraits/security-pros/card_023.png and b/assets/portraits/security-pros/card_023.png differ diff --git a/assets/portraits/security-pros/card_024.png b/assets/portraits/security-pros/card_024.png index c2edea63..8753f02b 100644 Binary files a/assets/portraits/security-pros/card_024.png and b/assets/portraits/security-pros/card_024.png differ diff --git a/assets/portraits/security-pros/card_025.png b/assets/portraits/security-pros/card_025.png index 5d858e15..bb3a9eec 100644 Binary files a/assets/portraits/security-pros/card_025.png and b/assets/portraits/security-pros/card_025.png differ diff --git a/assets/portraits/security-pros/card_026.png b/assets/portraits/security-pros/card_026.png deleted file mode 100644 index 02d38472..00000000 Binary files a/assets/portraits/security-pros/card_026.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_027.png b/assets/portraits/security-pros/card_027.png index 1ced3919..1d5a67f8 100644 Binary files a/assets/portraits/security-pros/card_027.png and b/assets/portraits/security-pros/card_027.png differ diff --git a/assets/portraits/security-pros/card_028.png b/assets/portraits/security-pros/card_028.png deleted file mode 100644 index 73c6a6fa..00000000 Binary files a/assets/portraits/security-pros/card_028.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_029.png b/assets/portraits/security-pros/card_029.png deleted file mode 100644 index 63011ecc..00000000 Binary files a/assets/portraits/security-pros/card_029.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_030.png b/assets/portraits/security-pros/card_030.png deleted file mode 100644 index e076d030..00000000 Binary files a/assets/portraits/security-pros/card_030.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_031.png b/assets/portraits/security-pros/card_031.png index b7989310..4864e7c9 100644 Binary files a/assets/portraits/security-pros/card_031.png and b/assets/portraits/security-pros/card_031.png differ diff --git a/assets/portraits/security-pros/card_032.png b/assets/portraits/security-pros/card_032.png deleted file mode 100644 index ed0648a5..00000000 Binary files a/assets/portraits/security-pros/card_032.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_033.png b/assets/portraits/security-pros/card_033.png deleted file mode 100644 index c28a487a..00000000 Binary files a/assets/portraits/security-pros/card_033.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_034.png b/assets/portraits/security-pros/card_034.png deleted file mode 100644 index 2a92e9c4..00000000 Binary files a/assets/portraits/security-pros/card_034.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_035.png b/assets/portraits/security-pros/card_035.png index eaf43ec8..09c15e90 100644 Binary files a/assets/portraits/security-pros/card_035.png and b/assets/portraits/security-pros/card_035.png differ diff --git a/assets/portraits/security-pros/card_036.png b/assets/portraits/security-pros/card_036.png index d0b980ef..73f4af32 100644 Binary files a/assets/portraits/security-pros/card_036.png and b/assets/portraits/security-pros/card_036.png differ diff --git a/assets/portraits/security-pros/card_037.png b/assets/portraits/security-pros/card_037.png index d93595f1..258ef483 100644 Binary files a/assets/portraits/security-pros/card_037.png and b/assets/portraits/security-pros/card_037.png differ diff --git a/assets/portraits/security-pros/card_038.png b/assets/portraits/security-pros/card_038.png index 32d741c7..8d966bec 100644 Binary files a/assets/portraits/security-pros/card_038.png and b/assets/portraits/security-pros/card_038.png differ diff --git a/assets/portraits/security-pros/card_039.png b/assets/portraits/security-pros/card_039.png index 99709be9..4dd76ef4 100644 Binary files a/assets/portraits/security-pros/card_039.png and b/assets/portraits/security-pros/card_039.png differ diff --git a/assets/portraits/security-pros/card_040.png b/assets/portraits/security-pros/card_040.png index 8395c298..b886319c 100644 Binary files a/assets/portraits/security-pros/card_040.png and b/assets/portraits/security-pros/card_040.png differ diff --git a/assets/portraits/security-pros/card_041.png b/assets/portraits/security-pros/card_041.png index f19528ad..60ae166e 100644 Binary files a/assets/portraits/security-pros/card_041.png and b/assets/portraits/security-pros/card_041.png differ diff --git a/assets/portraits/security-pros/card_042.png b/assets/portraits/security-pros/card_042.png deleted file mode 100644 index 47a8fa22..00000000 Binary files a/assets/portraits/security-pros/card_042.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_043.png b/assets/portraits/security-pros/card_043.png index b4ddc87e..667f7808 100644 Binary files a/assets/portraits/security-pros/card_043.png and b/assets/portraits/security-pros/card_043.png differ diff --git a/assets/portraits/security-pros/card_044.png b/assets/portraits/security-pros/card_044.png index 4586780b..d0f7ecca 100644 Binary files a/assets/portraits/security-pros/card_044.png and b/assets/portraits/security-pros/card_044.png differ diff --git a/assets/portraits/security-pros/card_045.png b/assets/portraits/security-pros/card_045.png index 54611abd..5bc5ddb8 100644 Binary files a/assets/portraits/security-pros/card_045.png and b/assets/portraits/security-pros/card_045.png differ diff --git a/assets/portraits/security-pros/card_046.png b/assets/portraits/security-pros/card_046.png index b778eb1c..e81e6d10 100644 Binary files a/assets/portraits/security-pros/card_046.png and b/assets/portraits/security-pros/card_046.png differ diff --git a/assets/portraits/security-pros/card_047.png b/assets/portraits/security-pros/card_047.png index 093ccf88..a2b7df9e 100644 Binary files a/assets/portraits/security-pros/card_047.png and b/assets/portraits/security-pros/card_047.png differ diff --git a/assets/portraits/security-pros/card_048.png b/assets/portraits/security-pros/card_048.png index d8045249..63cfaef6 100644 Binary files a/assets/portraits/security-pros/card_048.png and b/assets/portraits/security-pros/card_048.png differ diff --git a/assets/portraits/security-pros/card_049.png b/assets/portraits/security-pros/card_049.png index bb4eb644..cfe561ad 100644 Binary files a/assets/portraits/security-pros/card_049.png and b/assets/portraits/security-pros/card_049.png differ diff --git a/assets/portraits/security-pros/card_050.png b/assets/portraits/security-pros/card_050.png deleted file mode 100644 index d8728032..00000000 Binary files a/assets/portraits/security-pros/card_050.png and /dev/null differ diff --git a/data/security-references.json b/data/security-references.json new file mode 100644 index 00000000..91007960 --- /dev/null +++ b/data/security-references.json @@ -0,0 +1,747 @@ +[ + { + "number": 1, + "slug": "whitfield-diffie", + "name": "Whitfield Diffie", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/0/0c/Whitfield_Diffie_Royal_Society.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "Duncan.Hull", + "license": "CC BY-SA 4.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/4.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Whitfield_Diffie_Royal_Society.jpg", + "file": "Whitfield Diffie Royal Society.jpg", + "qid": "Q462089", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American cryptographer (born 1944)" + }, + { + "number": 2, + "slug": "martin-hellman", + "name": "Martin Hellman", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/d/d4/Martin-Hellman.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "User .:Ajvol:. on en.wikipedia", + "license": "CC BY-SA 3.0", + "licenseUrl": "http://creativecommons.org/licenses/by-sa/3.0/", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Martin-Hellman.jpg", + "file": "Martin-Hellman.jpg", + "qid": "Q476466", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American cryptologist (born 1945)" + }, + { + "number": 3, + "slug": "ron-rivest", + "name": "Ron Rivest", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/0/0b/Rivest_cropped.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "Sherman,_Rivest,_and_Chaum.jpg: carback1\nderivative work: Bonnifac (talk)", + "license": "CC BY-SA 3.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/3.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Rivest_cropped.jpg", + "file": "Rivest cropped.jpg", + "commonsFile": "Rivest cropped.jpg", + "via": "override" + }, + "candidates": [] + }, + { + "number": 4, + "slug": "adi-shamir", + "name": "Adi Shamir", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/1/1e/Adi_Shamir_at_TU_Darmstadt_%282013%29.jpg/1280px-Adi_Shamir_at_TU_Darmstadt_%282013%29.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "Erik Tews", + "license": "CC BY-SA 3.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/3.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Adi_Shamir_at_TU_Darmstadt_(2013).jpg", + "file": "Adi Shamir at TU Darmstadt (2013).jpg", + "qid": "Q320624", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "Israeli cryptographer (born 1952)" + }, + { + "number": 5, + "slug": "leonard-adleman", + "name": "Leonard Adleman", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/a/af/Len-mankin-pic.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "len adlmen", + "license": "CC BY-SA 3.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/3.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Len-mankin-pic.jpg", + "file": "Len-mankin-pic.jpg", + "qid": "Q918650", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American computer scientist" + }, + { + "number": 6, + "slug": "phil-zimmermann", + "name": "Phil Zimmermann", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/8/85/Phil_zimmermann.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "Phil Zimmermann, uploaded by Matt Crypto", + "license": "CC BY-SA 3.0", + "licenseUrl": "http://creativecommons.org/licenses/by-sa/3.0/", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Phil_zimmermann.jpg", + "file": "Phil zimmermann.jpg", + "qid": null, + "via": "commons-search" + }, + "candidates": [ + "Phil zimmermann.jpg" + ] + }, + { + "number": 7, + "slug": "bruce-schneier", + "name": "Bruce Schneier", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/4/45/Bruce_Schneier_1.jpg/1280px-Bruce_Schneier_1.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "sfllaw", + "license": "CC BY-SA 2.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/2.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Bruce_Schneier_1.jpg", + "file": "Bruce Schneier 1.jpg", + "qid": "Q368328", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American computer scientist (born 1963)" + }, + { + "number": 8, + "slug": "paul-kocher", + "name": "Paul Kocher", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/a/ac/Paul_kocher_deepcrack.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "https://www.oocities.org", + "license": "CC0", + "licenseUrl": "http://creativecommons.org/publicdomain/zero/1.0/deed.en", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Paul_kocher_deepcrack.jpg", + "file": "Paul kocher deepcrack.jpg", + "qid": "Q3371582", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American cryptographer" + }, + { + "number": 9, + "slug": "dorothy-denning", + "name": "Dorothy E. Denning", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/a/a9/Dorothy-Denning-Feb2013-head.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "Unknown photographer, US Navy", + "license": "Public domain", + "licenseUrl": "", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Dorothy-Denning-Feb2013-head.jpg", + "file": "Dorothy-Denning-Feb2013-head.jpg", + "qid": "Q11542", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American information security researcher" + }, + { + "number": 10, + "slug": "gene-spafford", + "name": "Gene Spafford", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/9/9d/Gene-spafford.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "Chlor at English Wikipedia", + "license": "CC BY-SA 3.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/3.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Gene-spafford.jpg", + "file": "Gene-spafford.jpg", + "qid": "Q5531458", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American computer scientist" + }, + { + "number": 11, + "slug": "ross-anderson", + "name": "Ross Anderson", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/b/b3/Ross_Anderson_%28security_researcher%29.jpg/1280px-Ross_Anderson_%28security_researcher%29.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "Markus Kuhn", + "license": "CC BY 3.0", + "licenseUrl": "https://creativecommons.org/licenses/by/3.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Ross_Anderson_(security_researcher).jpg", + "file": "Ross Anderson (security researcher).jpg", + "qid": "Q110901283", + "via": "commons-search" + }, + "candidates": [ + "Ross Anderson (security researcher).jpg", + "Ross Anderson (7862797770).jpg", + "Ross Anderson (7862798682).jpg", + "Ross Anderson 15 June 2018 IMG 4317.jpg", + "Ross Anderson (Men of affairs, 1914).png" + ] + }, + { + "number": 12, + "slug": "matt-blaze", + "name": "Matt Blaze", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/c/c6/Matt_Blaze_DEF_CON_20.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "DEFCONConference", + "license": "CC BY 3.0", + "licenseUrl": "https://creativecommons.org/licenses/by/3.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Matt_Blaze_DEF_CON_20.jpg", + "file": "Matt Blaze DEF CON 20.jpg", + "qid": null, + "via": "commons-search" + }, + "candidates": [ + "Matt Blaze DEF CON 20.jpg" + ] + }, + { + "number": 13, + "slug": "radia-perlman", + "name": "Radia Perlman", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/a/af/Radia_Perlman_2009.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "Scientist-100 at English Wikipedia", + "license": "Public domain", + "licenseUrl": "", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Radia_Perlman_2009.jpg", + "file": "Radia Perlman 2009.jpg", + "qid": "Q7558", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American software designer and network engineer (born 1951)" + }, + { + "number": 14, + "slug": "steven-bellovin", + "name": "Steven M. Bellovin", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/9/95/Steven_M_Bellovin_2016.jpg/1280px-Steven_M_Bellovin_2016.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "New America", + "license": "CC BY 2.0", + "licenseUrl": "https://creativecommons.org/licenses/by/2.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Steven_M_Bellovin_2016.jpg", + "file": "Steven M Bellovin 2016.jpg", + "qid": "Q1485905", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American computer scientist" + }, + { + "number": 15, + "slug": "william-cheswick", + "name": "William Cheswick", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/f/fc/William_Cheswick_2024.jpg/1280px-William_Cheswick_2024.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "Kevin Payravi", + "license": "CC BY-SA 4.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/4.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:William_Cheswick_2024.jpg", + "file": "William Cheswick 2024.jpg", + "qid": "Q8006762", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American engineer" + }, + { + "number": 16, + "slug": "marcus-ranum", + "name": "Marcus J. Ranum", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/1/12/Mjr-portrait-picture-mid.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "Marcus Ranum (per discussion on ticket)", + "license": "CC BY 3.0", + "licenseUrl": "https://creativecommons.org/licenses/by/3.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Mjr-portrait-picture-mid.jpg", + "file": "Mjr-portrait-picture-mid.jpg", + "qid": "Q3846688", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "researcher" + }, + { + "number": 17, + "slug": "wietse-venema", + "name": "Wietse Venema", + "ref": { + "url": "https://live.staticflickr.com/104/276434076_581ed1215d_b.jpg", + "credit": "Alexandre Dulaunoy", + "license": "CC BY-SA 2.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/2.0/", + "sourceUrl": "https://www.flickr.com/photos/31797858@N00/276434076", + "note": "hack.lu 2006. Supersedes the Wikidata P18 file, which shows a different man.", + "via": "override" + }, + "candidates": [] + }, + { + "number": 18, + "slug": "dan-farmer", + "name": "Dan Farmer", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/9/99/Dan_Farmer_in_2007.JPG?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "Ian Manka", + "license": "CC BY-SA 3.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/3.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Dan_Farmer_in_2007.JPG", + "file": "Dan Farmer in 2007.JPG", + "qid": "Q5213467", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American computer programmer, computer security researcher and businessman" + }, + { + "number": 19, + "slug": "gordon-lyon", + "name": "Gordon Lyon", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/f/fc/Fyodor_Hope6_Appelbaum_1100x1100.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "Jacob Appelbaum", + "license": "CC BY-SA 2.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/2.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Fyodor_Hope6_Appelbaum_1100x1100.jpg", + "file": "Fyodor Hope6 Appelbaum 1100x1100.jpg", + "qid": "Q3091346", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American network security expert, open source programmer, writer, and a hacker" + }, + { + "number": 20, + "slug": "martin-roesch", + "name": "Martin Roesch", + "ref": { + "url": "https://media.kasperskycontenthub.com/wp-content/uploads/sites/103/2013/07/07043722/sourcefire.jpg", + "credit": "Sourcefire press photo", + "license": "supplied", + "sourceUrl": "https://threatpost.com/", + "note": "Sourcefire-era press photograph. Supersedes the Commons files, which are a different Martin.", + "via": "override" + }, + "candidates": [] + }, + { + "number": 21, + "slug": "gerald-combs", + "name": "Gerald Combs", + "ref": null, + "candidates": [] + }, + { + "number": 22, + "slug": "renaud-deraison", + "name": "Renaud Deraison", + "ref": { + "url": "https://live.staticflickr.com/122/276307114_f29f0a38ec_b.jpg", + "credit": "Alexandre Dulaunoy", + "license": "CC BY-SA 2.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/2.0/", + "sourceUrl": "https://www.flickr.com/photos/31797858@N00/276307114", + "note": "hack.lu 2006 opening speech.", + "via": "override" + }, + "candidates": [] + }, + { + "number": 23, + "slug": "paul-vixie", + "name": "Paul Vixie", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/d/d6/Paul_Vixie_-_2014.jpg/1280px-Paul_Vixie_-_2014.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "ImaginingtheInternet", + "license": "CC BY 3.0", + "licenseUrl": "https://creativecommons.org/licenses/by/3.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Paul_Vixie_-_2014.jpg", + "file": "Paul Vixie - 2014.jpg", + "qid": "Q92777", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American internet pioneer" + }, + { + "number": 24, + "slug": "niels-provos", + "name": "Niels Provos", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/d/dc/Niels_provos.jpg/1280px-Niels_provos.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "NonAlphabetic", + "license": "CC BY-SA 4.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/4.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Niels_provos.jpg", + "file": "Niels provos.jpg", + "qid": "Q7031568", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "cryptographer" + }, + { + "number": 25, + "slug": "jeremiah-grossman", + "name": "Jeremiah Grossman", + "ref": { + "url": "https://cdn.prod.website-files.com/62deb8a5505ca121b1665bbe/63cf05d85cad9419bfa83096_Photo%20Oct%2003%202022%2C%206%2054%2000%20AM-p-800.jpg", + "credit": "Jeremiah Grossman", + "license": "supplied", + "sourceUrl": "https://www.jeremiahgrossman.com/", + "note": "Publicity photo from his own site, supplied by the site owner as reference.", + "via": "override" + }, + "candidates": [] + }, + { + "number": 26, + "slug": "robert-hansen", + "name": "Robert Hansen", + "ref": null, + "candidates": [ + "Robert Hansen ex libris.jpg", + "Robert Hansen Jr 180425.jpg" + ] + }, + { + "number": 27, + "slug": "mark-curphey", + "name": "Mark Curphey", + "ref": { + "url": "https://user-images.githubusercontent.com/143776/193322138-009524de-bae1-4190-989d-2d729ad0beb8.png", + "credit": "Mark Curphey", + "license": "supplied", + "sourceUrl": "https://www.crash-override.com/", + "note": "Portrait published by his own company.", + "via": "override" + }, + "candidates": [] + }, + { + "number": 28, + "slug": "jeff-williams", + "name": "Jeff Williams", + "ref": null, + "candidates": [], + "rejected": "\"Jeff Williams photo.jpg\" is one of several unrelated Jeff Williamses. Cannot confirm it is the Contrast Security founder." + }, + { + "number": 29, + "slug": "gary-mcgraw", + "name": "Gary McGraw", + "ref": null, + "candidates": [], + "rejected": "Only candidate is a group briefing shot with audience heads across the frame; the other Commons hits are unrelated US Navy photographs." + }, + { + "number": 30, + "slug": "michael-howard", + "name": "Michael Howard", + "ref": null, + "candidates": [], + "rejected": "Every Commons hit is a different Michael Howard — the British politician, or a young man with no connection to the Microsoft SDL author." + }, + { + "number": 31, + "slug": "adam-shostack", + "name": "Adam Shostack", + "ref": { + "url": "https://shostack.org/img/site/Adam-Shostack-headshot.png", + "credit": "Adam Shostack", + "license": "supplied", + "sourceUrl": "https://shostack.org/about/adam", + "note": "Headshot published on his own site.", + "via": "override" + }, + "candidates": [] + }, + { + "number": 32, + "slug": "ivan-ristic", + "name": "Ivan Ristić", + "ref": null, + "candidates": [] + }, + { + "number": 33, + "slug": "mark-dowd", + "name": "Mark Dowd", + "ref": null, + "candidates": [] + }, + { + "number": 34, + "slug": "halvar-flake", + "name": "Thomas Dullien", + "ref": null, + "candidates": [] + }, + { + "number": 35, + "slug": "alex-sotirov", + "name": "Alex Sotirov", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/c/c5/Alex_Sotirov.jpg/1280px-Alex_Sotirov.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "Alexander Klink", + "license": "CC BY 3.0", + "licenseUrl": "https://creativecommons.org/licenses/by/3.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Alex_Sotirov.jpg", + "file": "Alex Sotirov.jpg", + "qid": null, + "via": "commons-search" + }, + "candidates": [ + "Alex Sotirov.jpg" + ] + }, + { + "number": 36, + "slug": "thomas-ptacek", + "name": "Thomas Ptacek", + "ref": { + "url": "https://live.staticflickr.com/2733/4302617844_52625091af_b.jpg", + "credit": "cfournie", + "license": "CC BY 2.0", + "licenseUrl": "https://creativecommons.org/licenses/by/2.0/", + "sourceUrl": "https://www.flickr.com/photos/37932109@N05/4302617844", + "via": "override" + }, + "candidates": [] + }, + { + "number": 37, + "slug": "matthew-green", + "name": "Matthew D. Green", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/d/da/Matthew_Green.jpeg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "Calhbca", + "license": "CC BY-SA 4.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/4.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Matthew_Green.jpeg", + "file": "Matthew Green.jpeg", + "qid": "Q16729426", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American cryptographer and researcher" + }, + { + "number": 38, + "slug": "j-alex-halderman", + "name": "J. Alex Halderman", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/b/b2/J._Alex_Halderman_-_2018.jpg/1280px-J._Alex_Halderman_-_2018.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "media.ccc.de", + "license": "CC BY 3.0", + "licenseUrl": "https://creativecommons.org/licenses/by/3.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:J._Alex_Halderman_-_2018.jpg", + "file": "J. Alex Halderman - 2018.jpg", + "qid": "Q130974774", + "via": "commons-search" + }, + "candidates": [ + "J. Alex Halderman - 2018.jpg" + ] + }, + { + "number": 39, + "slug": "nadia-heninger", + "name": "Nadia Heninger", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/5/5a/Nadia_Heninger_2013_at_Chaos_Communication_Congress_30C3_DJB_03.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "Ordercrazy", + "license": "CC0", + "licenseUrl": "http://creativecommons.org/publicdomain/zero/1.0/deed.en", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Nadia_Heninger_2013_at_Chaos_Communication_Congress_30C3_DJB_03.jpg", + "file": "Nadia Heninger 2013 at Chaos Communication Congress 30C3 DJB 03.jpg", + "qid": "Q56678255", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American cryptographer, computer security expert" + }, + { + "number": 40, + "slug": "troy-hunt", + "name": "Troy Hunt", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/1/11/Troy_Hunt.jpg/1280px-Troy_Hunt.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "Troy Hunt", + "license": "CC BY-SA 4.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/4.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Troy_Hunt.jpg", + "file": "Troy Hunt.jpg", + "qid": "Q23308064", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "Australian web security expert" + }, + { + "number": 41, + "slug": "window-snyder", + "name": "Window Snyder", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/4/42/Window_Snyder_Web_Summit_2017_cropped.jpg/1280px-Window_Snyder_Web_Summit_2017_cropped.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "Web Summit", + "license": "CC BY 2.0", + "licenseUrl": "https://creativecommons.org/licenses/by/2.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Window_Snyder_Web_Summit_2017_cropped.jpg", + "file": "Window Snyder Web Summit 2017 cropped.jpg", + "qid": "Q8024355", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "Computer security expert" + }, + { + "number": 42, + "slug": "heather-adkins", + "name": "Heather Adkins", + "ref": null, + "candidates": [] + }, + { + "number": 43, + "slug": "parisa-tabriz", + "name": "Parisa Tabriz", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/6/67/Parisa_Tabriz_Blackhat%2717_profile.jpg/1280px-Parisa_Tabriz_Blackhat%2717_profile.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "mrisher", + "license": "CC BY-SA 4.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/4.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Parisa_Tabriz_Blackhat%2717_profile.jpg", + "file": "Parisa Tabriz Blackhat'17 profile.jpg", + "qid": "Q17496835", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American/Iranian computer security expert" + }, + { + "number": 44, + "slug": "alex-stamos", + "name": "Alex Stamos", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/6/6a/Alex_Stamos_at_Web_Summit_2015_-_Dublin%2C_Ireland.jpg/1280px-Alex_Stamos_at_Web_Summit_2015_-_Dublin%2C_Ireland.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "Web Summit", + "license": "CC BY 2.0", + "licenseUrl": "https://creativecommons.org/licenses/by/2.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Alex_Stamos_at_Web_Summit_2015_-_Dublin,_Ireland.jpg", + "file": "Alex Stamos at Web Summit 2015 - Dublin, Ireland.jpg", + "qid": "Q50822408", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American computer scientist and academic, former chief security officer at Facebook" + }, + { + "number": 45, + "slug": "dan-geer", + "name": "Dan Geer", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/4/4d/Dan_Geer_-_Freedom_to_Connect_2015_%2816109600123%29.jpg/1280px-Dan_Geer_-_Freedom_to_Connect_2015_%2816109600123%29.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "Doc Searls from Santa Barbara, USA", + "license": "CC BY 2.0", + "licenseUrl": "https://creativecommons.org/licenses/by/2.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Dan_Geer_-_Freedom_to_Connect_2015_(16109600123).jpg", + "file": "Dan Geer - Freedom to Connect 2015 (16109600123).jpg", + "qid": "Q5213551", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "American computer scientist" + }, + { + "number": 46, + "slug": "kevin-mandia", + "name": "Kevin Mandia", + "ref": { + "url": "https://ballisticventures.com/wp-content/uploads/2024/06/Kevin-Mandia.png", + "credit": "Ballistic Ventures", + "license": "supplied", + "sourceUrl": "https://www.ballisticventures.com/team/kevin-mandia", + "note": "Partner bio photo.", + "via": "override" + }, + "candidates": [] + }, + { + "number": 47, + "slug": "mikko-hypponen", + "name": "Mikko Hyppönen", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/c/ce/Mikko_Hypp%C3%B6nen_-_2021_%28cropped%29.jpg/1280px-Mikko_Hypp%C3%B6nen_-_2021_%28cropped%29.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "Ville Erkkilä", + "license": "CC BY-SA 4.0", + "licenseUrl": "https://creativecommons.org/licenses/by-sa/4.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Mikko_Hypp%C3%B6nen_-_2021_(cropped).jpg", + "file": "Mikko Hyppönen - 2021 (cropped).jpg", + "qid": "Q2489610", + "via": "wikidata-p18" + }, + "candidates": [], + "desc": "Finnish computer security expert" + }, + { + "number": 48, + "slug": "robert-m-lee", + "name": "Robert M. Lee", + "ref": { + "url": "https://upload.wikimedia.org/wikipedia/commons/9/92/Robert_M._Lee_at_World_Economic_Forum_Davos_2022.png?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail_unscaled", + "credit": "World Economic Forum", + "license": "CC BY 3.0", + "licenseUrl": "https://creativecommons.org/licenses/by/3.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Robert_M._Lee_at_World_Economic_Forum_Davos_2022.png", + "file": "Robert M. Lee at World Economic Forum Davos 2022.png", + "qid": null, + "via": "commons-search" + }, + "candidates": [ + "Robert M. Lee at World Economic Forum Davos 2022.png" + ] + }, + { + "number": 49, + "slug": "lesley-carhart", + "name": "Lesley Carhart", + "ref": { + "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/7/70/Lesley_Carhart_-_What_Can_We_Learn_About_Cybersecurity_Trashfires.jpg/1280px-Lesley_Carhart_-_What_Can_We_Learn_About_Cybersecurity_Trashfires.jpg?utm_source=commons.wikimedia.org&utm_campaign=imageinfo&utm_content=thumbnail", + "credit": "TechCrunch", + "license": "CC BY 2.0", + "licenseUrl": "https://creativecommons.org/licenses/by/2.0", + "sourceUrl": "https://commons.wikimedia.org/wiki/File:Lesley_Carhart_-_What_Can_We_Learn_About_Cybersecurity_Trashfires.jpg", + "file": "Lesley Carhart - What Can We Learn About Cybersecurity Trashfires.jpg", + "qid": null, + "via": "commons-search" + }, + "candidates": [ + "Lesley Carhart - What Can We Learn About Cybersecurity Trashfires.jpg", + "Lesley Carhart - What Can We Learn About Cybersecurity Trashfires (cropped).jpg" + ] + }, + { + "number": 50, + "slug": "katie-nickels", + "name": "Katie Nickels", + "ref": null, + "candidates": [] + } +] diff --git a/public/cards/security/001-whitfield-diffie-back.png b/public/cards/security/001-whitfield-diffie-back.png index 915e9f9c..22db10ae 100644 Binary files a/public/cards/security/001-whitfield-diffie-back.png and b/public/cards/security/001-whitfield-diffie-back.png differ diff --git a/public/cards/security/001-whitfield-diffie-front.png b/public/cards/security/001-whitfield-diffie-front.png index a94cd2a6..456a3902 100644 Binary files a/public/cards/security/001-whitfield-diffie-front.png and b/public/cards/security/001-whitfield-diffie-front.png differ diff --git a/public/cards/security/002-martin-hellman-back.png b/public/cards/security/002-martin-hellman-back.png index 13e8958f..35c28d32 100644 Binary files a/public/cards/security/002-martin-hellman-back.png and b/public/cards/security/002-martin-hellman-back.png differ diff --git a/public/cards/security/002-martin-hellman-front.png b/public/cards/security/002-martin-hellman-front.png index 4c44c51e..94fecf84 100644 Binary files a/public/cards/security/002-martin-hellman-front.png and b/public/cards/security/002-martin-hellman-front.png differ diff --git a/public/cards/security/003-ron-rivest-back.png b/public/cards/security/003-ron-rivest-back.png index 065bb240..8d8dc62e 100644 Binary files a/public/cards/security/003-ron-rivest-back.png and b/public/cards/security/003-ron-rivest-back.png differ diff --git a/public/cards/security/003-ron-rivest-front.png b/public/cards/security/003-ron-rivest-front.png index 9e84ffdd..d366c2c9 100644 Binary files a/public/cards/security/003-ron-rivest-front.png and b/public/cards/security/003-ron-rivest-front.png differ diff --git a/public/cards/security/004-adi-shamir-back.png b/public/cards/security/004-adi-shamir-back.png index 5a55fd03..c316c6d1 100644 Binary files a/public/cards/security/004-adi-shamir-back.png and b/public/cards/security/004-adi-shamir-back.png differ diff --git a/public/cards/security/004-adi-shamir-front.png b/public/cards/security/004-adi-shamir-front.png index 19d8ce36..6fa0c767 100644 Binary files a/public/cards/security/004-adi-shamir-front.png and b/public/cards/security/004-adi-shamir-front.png differ diff --git a/public/cards/security/005-leonard-adleman-back.png b/public/cards/security/005-leonard-adleman-back.png index ee6ecded..2aa1e27e 100644 Binary files a/public/cards/security/005-leonard-adleman-back.png and b/public/cards/security/005-leonard-adleman-back.png differ diff --git a/public/cards/security/005-leonard-adleman-front.png b/public/cards/security/005-leonard-adleman-front.png index 1e72d2f8..5667914f 100644 Binary files a/public/cards/security/005-leonard-adleman-front.png and b/public/cards/security/005-leonard-adleman-front.png differ diff --git a/public/cards/security/006-phil-zimmermann-back.png b/public/cards/security/006-phil-zimmermann-back.png index 013dcb22..32cad92d 100644 Binary files a/public/cards/security/006-phil-zimmermann-back.png and b/public/cards/security/006-phil-zimmermann-back.png differ diff --git a/public/cards/security/006-phil-zimmermann-front.png b/public/cards/security/006-phil-zimmermann-front.png index 88e40f72..b30f44d0 100644 Binary files a/public/cards/security/006-phil-zimmermann-front.png and b/public/cards/security/006-phil-zimmermann-front.png differ diff --git a/public/cards/security/007-bruce-schneier-back.png b/public/cards/security/007-bruce-schneier-back.png index 0aa46261..665c466d 100644 Binary files a/public/cards/security/007-bruce-schneier-back.png and b/public/cards/security/007-bruce-schneier-back.png differ diff --git a/public/cards/security/007-bruce-schneier-front.png b/public/cards/security/007-bruce-schneier-front.png index 09ee26dd..8192b1d3 100644 Binary files a/public/cards/security/007-bruce-schneier-front.png and b/public/cards/security/007-bruce-schneier-front.png differ diff --git a/public/cards/security/008-paul-kocher-back.png b/public/cards/security/008-paul-kocher-back.png index f59ff81b..beee2443 100644 Binary files a/public/cards/security/008-paul-kocher-back.png and b/public/cards/security/008-paul-kocher-back.png differ diff --git a/public/cards/security/008-paul-kocher-front.png b/public/cards/security/008-paul-kocher-front.png index 1ad6909f..c2148af6 100644 Binary files a/public/cards/security/008-paul-kocher-front.png and b/public/cards/security/008-paul-kocher-front.png differ diff --git a/public/cards/security/009-dorothy-denning-back.png b/public/cards/security/009-dorothy-denning-back.png index 316ee333..a6aed799 100644 Binary files a/public/cards/security/009-dorothy-denning-back.png and b/public/cards/security/009-dorothy-denning-back.png differ diff --git a/public/cards/security/009-dorothy-denning-front.png b/public/cards/security/009-dorothy-denning-front.png index 7cc90d98..4528550d 100644 Binary files a/public/cards/security/009-dorothy-denning-front.png and b/public/cards/security/009-dorothy-denning-front.png differ diff --git a/public/cards/security/010-gene-spafford-back.png b/public/cards/security/010-gene-spafford-back.png index 0e7499e5..2ccf8289 100644 Binary files a/public/cards/security/010-gene-spafford-back.png and b/public/cards/security/010-gene-spafford-back.png differ diff --git a/public/cards/security/010-gene-spafford-front.png b/public/cards/security/010-gene-spafford-front.png index 53deabb0..6aa9e79a 100644 Binary files a/public/cards/security/010-gene-spafford-front.png and b/public/cards/security/010-gene-spafford-front.png differ diff --git a/public/cards/security/011-ross-anderson-back.png b/public/cards/security/011-ross-anderson-back.png index 5b828bdb..90a8bbe3 100644 Binary files a/public/cards/security/011-ross-anderson-back.png and b/public/cards/security/011-ross-anderson-back.png differ diff --git a/public/cards/security/011-ross-anderson-front.png b/public/cards/security/011-ross-anderson-front.png index 3e2d83ec..69d034aa 100644 Binary files a/public/cards/security/011-ross-anderson-front.png and b/public/cards/security/011-ross-anderson-front.png differ diff --git a/public/cards/security/012-matt-blaze-back.png b/public/cards/security/012-matt-blaze-back.png index f62ff8a7..8d2e733e 100644 Binary files a/public/cards/security/012-matt-blaze-back.png and b/public/cards/security/012-matt-blaze-back.png differ diff --git a/public/cards/security/012-matt-blaze-front.png b/public/cards/security/012-matt-blaze-front.png index 7a04bb56..513dbf40 100644 Binary files a/public/cards/security/012-matt-blaze-front.png and b/public/cards/security/012-matt-blaze-front.png differ diff --git a/public/cards/security/013-radia-perlman-back.png b/public/cards/security/013-radia-perlman-back.png index 1ec4b05c..e382a9af 100644 Binary files a/public/cards/security/013-radia-perlman-back.png and b/public/cards/security/013-radia-perlman-back.png differ diff --git a/public/cards/security/013-radia-perlman-front.png b/public/cards/security/013-radia-perlman-front.png index 52e33b49..7b7052c3 100644 Binary files a/public/cards/security/013-radia-perlman-front.png and b/public/cards/security/013-radia-perlman-front.png differ diff --git a/public/cards/security/014-steven-bellovin-back.png b/public/cards/security/014-steven-bellovin-back.png index 1b0a1bf6..76121f39 100644 Binary files a/public/cards/security/014-steven-bellovin-back.png and b/public/cards/security/014-steven-bellovin-back.png differ diff --git a/public/cards/security/014-steven-bellovin-front.png b/public/cards/security/014-steven-bellovin-front.png index a6c48d4d..ce3f9d89 100644 Binary files a/public/cards/security/014-steven-bellovin-front.png and b/public/cards/security/014-steven-bellovin-front.png differ diff --git a/public/cards/security/015-william-cheswick-back.png b/public/cards/security/015-william-cheswick-back.png index 1b2d5b61..c7851443 100644 Binary files a/public/cards/security/015-william-cheswick-back.png and b/public/cards/security/015-william-cheswick-back.png differ diff --git a/public/cards/security/015-william-cheswick-front.png b/public/cards/security/015-william-cheswick-front.png index f96a23e5..2880d76c 100644 Binary files a/public/cards/security/015-william-cheswick-front.png and b/public/cards/security/015-william-cheswick-front.png differ diff --git a/public/cards/security/016-marcus-ranum-back.png b/public/cards/security/016-marcus-ranum-back.png index ce73574e..3fc69234 100644 Binary files a/public/cards/security/016-marcus-ranum-back.png and b/public/cards/security/016-marcus-ranum-back.png differ diff --git a/public/cards/security/016-marcus-ranum-front.png b/public/cards/security/016-marcus-ranum-front.png index c8777327..6bb4d114 100644 Binary files a/public/cards/security/016-marcus-ranum-front.png and b/public/cards/security/016-marcus-ranum-front.png differ diff --git a/public/cards/security/017-wietse-venema-back.png b/public/cards/security/017-wietse-venema-back.png index e667238a..4eaabb47 100644 Binary files a/public/cards/security/017-wietse-venema-back.png and b/public/cards/security/017-wietse-venema-back.png differ diff --git a/public/cards/security/017-wietse-venema-front.png b/public/cards/security/017-wietse-venema-front.png index b40d4804..52efeafb 100644 Binary files a/public/cards/security/017-wietse-venema-front.png and b/public/cards/security/017-wietse-venema-front.png differ diff --git a/public/cards/security/018-dan-farmer-back.png b/public/cards/security/018-dan-farmer-back.png index 5cb3d653..a5bbdae3 100644 Binary files a/public/cards/security/018-dan-farmer-back.png and b/public/cards/security/018-dan-farmer-back.png differ diff --git a/public/cards/security/018-dan-farmer-front.png b/public/cards/security/018-dan-farmer-front.png index 473abf7a..bb4bbbb9 100644 Binary files a/public/cards/security/018-dan-farmer-front.png and b/public/cards/security/018-dan-farmer-front.png differ diff --git a/public/cards/security/019-gordon-lyon-back.png b/public/cards/security/019-gordon-lyon-back.png index cd2b3f9c..6624303b 100644 Binary files a/public/cards/security/019-gordon-lyon-back.png and b/public/cards/security/019-gordon-lyon-back.png differ diff --git a/public/cards/security/019-gordon-lyon-front.png b/public/cards/security/019-gordon-lyon-front.png index 9ec0efb0..84437d4c 100644 Binary files a/public/cards/security/019-gordon-lyon-front.png and b/public/cards/security/019-gordon-lyon-front.png differ diff --git a/public/cards/security/020-martin-roesch-back.png b/public/cards/security/020-martin-roesch-back.png index 02d4a65a..3b38d354 100644 Binary files a/public/cards/security/020-martin-roesch-back.png and b/public/cards/security/020-martin-roesch-back.png differ diff --git a/public/cards/security/020-martin-roesch-front.png b/public/cards/security/020-martin-roesch-front.png index 8c832f07..d1ec09bb 100644 Binary files a/public/cards/security/020-martin-roesch-front.png and b/public/cards/security/020-martin-roesch-front.png differ diff --git a/public/cards/security/021-gerald-combs-front.png b/public/cards/security/021-gerald-combs-front.png index e1a455ea..65f8bd8f 100644 Binary files a/public/cards/security/021-gerald-combs-front.png and b/public/cards/security/021-gerald-combs-front.png differ diff --git a/public/cards/security/022-renaud-deraison-back.png b/public/cards/security/022-renaud-deraison-back.png index 0217894f..bb38908d 100644 Binary files a/public/cards/security/022-renaud-deraison-back.png and b/public/cards/security/022-renaud-deraison-back.png differ diff --git a/public/cards/security/022-renaud-deraison-front.png b/public/cards/security/022-renaud-deraison-front.png index ce87913c..15424ec8 100644 Binary files a/public/cards/security/022-renaud-deraison-front.png and b/public/cards/security/022-renaud-deraison-front.png differ diff --git a/public/cards/security/023-paul-vixie-back.png b/public/cards/security/023-paul-vixie-back.png index e6c64562..f850fa4b 100644 Binary files a/public/cards/security/023-paul-vixie-back.png and b/public/cards/security/023-paul-vixie-back.png differ diff --git a/public/cards/security/023-paul-vixie-front.png b/public/cards/security/023-paul-vixie-front.png index 821a620f..17852305 100644 Binary files a/public/cards/security/023-paul-vixie-front.png and b/public/cards/security/023-paul-vixie-front.png differ diff --git a/public/cards/security/024-niels-provos-back.png b/public/cards/security/024-niels-provos-back.png index f89601bd..593d7460 100644 Binary files a/public/cards/security/024-niels-provos-back.png and b/public/cards/security/024-niels-provos-back.png differ diff --git a/public/cards/security/024-niels-provos-front.png b/public/cards/security/024-niels-provos-front.png index cf9a1aae..d713bc22 100644 Binary files a/public/cards/security/024-niels-provos-front.png and b/public/cards/security/024-niels-provos-front.png differ diff --git a/public/cards/security/025-jeremiah-grossman-back.png b/public/cards/security/025-jeremiah-grossman-back.png index d047bb6e..667dd89f 100644 Binary files a/public/cards/security/025-jeremiah-grossman-back.png and b/public/cards/security/025-jeremiah-grossman-back.png differ diff --git a/public/cards/security/025-jeremiah-grossman-front.png b/public/cards/security/025-jeremiah-grossman-front.png index ad6925ad..a80bca16 100644 Binary files a/public/cards/security/025-jeremiah-grossman-front.png and b/public/cards/security/025-jeremiah-grossman-front.png differ diff --git a/public/cards/security/026-robert-hansen-front.png b/public/cards/security/026-robert-hansen-front.png index cb5823cb..247ea7f7 100644 Binary files a/public/cards/security/026-robert-hansen-front.png and b/public/cards/security/026-robert-hansen-front.png differ diff --git a/public/cards/security/027-mark-curphey-back.png b/public/cards/security/027-mark-curphey-back.png index bb236cb3..21484504 100644 Binary files a/public/cards/security/027-mark-curphey-back.png and b/public/cards/security/027-mark-curphey-back.png differ diff --git a/public/cards/security/027-mark-curphey-front.png b/public/cards/security/027-mark-curphey-front.png index 11ec5d12..16ca9dc1 100644 Binary files a/public/cards/security/027-mark-curphey-front.png and b/public/cards/security/027-mark-curphey-front.png differ diff --git a/public/cards/security/028-jeff-williams-front.png b/public/cards/security/028-jeff-williams-front.png index d5c7f860..9cf3ff45 100644 Binary files a/public/cards/security/028-jeff-williams-front.png and b/public/cards/security/028-jeff-williams-front.png differ diff --git a/public/cards/security/029-gary-mcgraw-front.png b/public/cards/security/029-gary-mcgraw-front.png index 0605914d..e272c3ab 100644 Binary files a/public/cards/security/029-gary-mcgraw-front.png and b/public/cards/security/029-gary-mcgraw-front.png differ diff --git a/public/cards/security/030-michael-howard-front.png b/public/cards/security/030-michael-howard-front.png index 8ecbb8d5..b29227ef 100644 Binary files a/public/cards/security/030-michael-howard-front.png and b/public/cards/security/030-michael-howard-front.png differ diff --git a/public/cards/security/031-adam-shostack-back.png b/public/cards/security/031-adam-shostack-back.png index 2fba1004..a68f21a8 100644 Binary files a/public/cards/security/031-adam-shostack-back.png and b/public/cards/security/031-adam-shostack-back.png differ diff --git a/public/cards/security/031-adam-shostack-front.png b/public/cards/security/031-adam-shostack-front.png index 2120988d..c30a6740 100644 Binary files a/public/cards/security/031-adam-shostack-front.png and b/public/cards/security/031-adam-shostack-front.png differ diff --git a/public/cards/security/032-ivan-ristic-front.png b/public/cards/security/032-ivan-ristic-front.png index 8b9f45d4..42ebd24a 100644 Binary files a/public/cards/security/032-ivan-ristic-front.png and b/public/cards/security/032-ivan-ristic-front.png differ diff --git a/public/cards/security/033-mark-dowd-front.png b/public/cards/security/033-mark-dowd-front.png index 87002206..d57f13c3 100644 Binary files a/public/cards/security/033-mark-dowd-front.png and b/public/cards/security/033-mark-dowd-front.png differ diff --git a/public/cards/security/034-halvar-flake-front.png b/public/cards/security/034-halvar-flake-front.png index 86064ce3..8caa1fef 100644 Binary files a/public/cards/security/034-halvar-flake-front.png and b/public/cards/security/034-halvar-flake-front.png differ diff --git a/public/cards/security/035-alex-sotirov-back.png b/public/cards/security/035-alex-sotirov-back.png index fd1cb3f5..e6ca68a9 100644 Binary files a/public/cards/security/035-alex-sotirov-back.png and b/public/cards/security/035-alex-sotirov-back.png differ diff --git a/public/cards/security/035-alex-sotirov-front.png b/public/cards/security/035-alex-sotirov-front.png index 94dba9a3..b3300063 100644 Binary files a/public/cards/security/035-alex-sotirov-front.png and b/public/cards/security/035-alex-sotirov-front.png differ diff --git a/public/cards/security/036-thomas-ptacek-back.png b/public/cards/security/036-thomas-ptacek-back.png index 8112421e..887c6a63 100644 Binary files a/public/cards/security/036-thomas-ptacek-back.png and b/public/cards/security/036-thomas-ptacek-back.png differ diff --git a/public/cards/security/036-thomas-ptacek-front.png b/public/cards/security/036-thomas-ptacek-front.png index d1bed8f7..80d67b85 100644 Binary files a/public/cards/security/036-thomas-ptacek-front.png and b/public/cards/security/036-thomas-ptacek-front.png differ diff --git a/public/cards/security/037-matthew-green-back.png b/public/cards/security/037-matthew-green-back.png index 605ad9a5..6dd1e292 100644 Binary files a/public/cards/security/037-matthew-green-back.png and b/public/cards/security/037-matthew-green-back.png differ diff --git a/public/cards/security/037-matthew-green-front.png b/public/cards/security/037-matthew-green-front.png index a7a100c4..270c2f0c 100644 Binary files a/public/cards/security/037-matthew-green-front.png and b/public/cards/security/037-matthew-green-front.png differ diff --git a/public/cards/security/038-j-alex-halderman-back.png b/public/cards/security/038-j-alex-halderman-back.png index 68721d79..806661fd 100644 Binary files a/public/cards/security/038-j-alex-halderman-back.png and b/public/cards/security/038-j-alex-halderman-back.png differ diff --git a/public/cards/security/038-j-alex-halderman-front.png b/public/cards/security/038-j-alex-halderman-front.png index 9074d706..47f6f07d 100644 Binary files a/public/cards/security/038-j-alex-halderman-front.png and b/public/cards/security/038-j-alex-halderman-front.png differ diff --git a/public/cards/security/039-nadia-heninger-back.png b/public/cards/security/039-nadia-heninger-back.png index fb9eaa07..4cfd6c5a 100644 Binary files a/public/cards/security/039-nadia-heninger-back.png and b/public/cards/security/039-nadia-heninger-back.png differ diff --git a/public/cards/security/039-nadia-heninger-front.png b/public/cards/security/039-nadia-heninger-front.png index 8a47094a..7a9a407b 100644 Binary files a/public/cards/security/039-nadia-heninger-front.png and b/public/cards/security/039-nadia-heninger-front.png differ diff --git a/public/cards/security/040-troy-hunt-back.png b/public/cards/security/040-troy-hunt-back.png index d24a1914..d4b6cb54 100644 Binary files a/public/cards/security/040-troy-hunt-back.png and b/public/cards/security/040-troy-hunt-back.png differ diff --git a/public/cards/security/040-troy-hunt-front.png b/public/cards/security/040-troy-hunt-front.png index 5572fad0..fc1558c6 100644 Binary files a/public/cards/security/040-troy-hunt-front.png and b/public/cards/security/040-troy-hunt-front.png differ diff --git a/public/cards/security/041-window-snyder-back.png b/public/cards/security/041-window-snyder-back.png index 984b62d8..93cf0f41 100644 Binary files a/public/cards/security/041-window-snyder-back.png and b/public/cards/security/041-window-snyder-back.png differ diff --git a/public/cards/security/041-window-snyder-front.png b/public/cards/security/041-window-snyder-front.png index 9d4d275a..d0678bbf 100644 Binary files a/public/cards/security/041-window-snyder-front.png and b/public/cards/security/041-window-snyder-front.png differ diff --git a/public/cards/security/042-heather-adkins-front.png b/public/cards/security/042-heather-adkins-front.png index 1a6b5540..c5c04b27 100644 Binary files a/public/cards/security/042-heather-adkins-front.png and b/public/cards/security/042-heather-adkins-front.png differ diff --git a/public/cards/security/043-parisa-tabriz-back.png b/public/cards/security/043-parisa-tabriz-back.png index 1f8a070d..fb941b38 100644 Binary files a/public/cards/security/043-parisa-tabriz-back.png and b/public/cards/security/043-parisa-tabriz-back.png differ diff --git a/public/cards/security/043-parisa-tabriz-front.png b/public/cards/security/043-parisa-tabriz-front.png index 950dff9d..a139857a 100644 Binary files a/public/cards/security/043-parisa-tabriz-front.png and b/public/cards/security/043-parisa-tabriz-front.png differ diff --git a/public/cards/security/044-alex-stamos-back.png b/public/cards/security/044-alex-stamos-back.png index 7357be20..a53d3892 100644 Binary files a/public/cards/security/044-alex-stamos-back.png and b/public/cards/security/044-alex-stamos-back.png differ diff --git a/public/cards/security/044-alex-stamos-front.png b/public/cards/security/044-alex-stamos-front.png index 2b3c68e3..e2a00a4c 100644 Binary files a/public/cards/security/044-alex-stamos-front.png and b/public/cards/security/044-alex-stamos-front.png differ diff --git a/public/cards/security/045-dan-geer-back.png b/public/cards/security/045-dan-geer-back.png index 9afe5c4d..f691a647 100644 Binary files a/public/cards/security/045-dan-geer-back.png and b/public/cards/security/045-dan-geer-back.png differ diff --git a/public/cards/security/045-dan-geer-front.png b/public/cards/security/045-dan-geer-front.png index 98f7dd90..21499e6d 100644 Binary files a/public/cards/security/045-dan-geer-front.png and b/public/cards/security/045-dan-geer-front.png differ diff --git a/public/cards/security/046-kevin-mandia-back.png b/public/cards/security/046-kevin-mandia-back.png index 0d7eadf7..5c958546 100644 Binary files a/public/cards/security/046-kevin-mandia-back.png and b/public/cards/security/046-kevin-mandia-back.png differ diff --git a/public/cards/security/046-kevin-mandia-front.png b/public/cards/security/046-kevin-mandia-front.png index e4e68aeb..1fabb795 100644 Binary files a/public/cards/security/046-kevin-mandia-front.png and b/public/cards/security/046-kevin-mandia-front.png differ diff --git a/public/cards/security/047-mikko-hypponen-back.png b/public/cards/security/047-mikko-hypponen-back.png index 49aefc24..c3a6b915 100644 Binary files a/public/cards/security/047-mikko-hypponen-back.png and b/public/cards/security/047-mikko-hypponen-back.png differ diff --git a/public/cards/security/047-mikko-hypponen-front.png b/public/cards/security/047-mikko-hypponen-front.png index d2aa740c..4ef33547 100644 Binary files a/public/cards/security/047-mikko-hypponen-front.png and b/public/cards/security/047-mikko-hypponen-front.png differ diff --git a/public/cards/security/048-robert-m-lee-back.png b/public/cards/security/048-robert-m-lee-back.png index cb39e91f..5dc332b5 100644 Binary files a/public/cards/security/048-robert-m-lee-back.png and b/public/cards/security/048-robert-m-lee-back.png differ diff --git a/public/cards/security/048-robert-m-lee-front.png b/public/cards/security/048-robert-m-lee-front.png index 71b9c743..a02063cc 100644 Binary files a/public/cards/security/048-robert-m-lee-front.png and b/public/cards/security/048-robert-m-lee-front.png differ diff --git a/public/cards/security/049-lesley-carhart-back.png b/public/cards/security/049-lesley-carhart-back.png index 39ae3330..ec156eb1 100644 Binary files a/public/cards/security/049-lesley-carhart-back.png and b/public/cards/security/049-lesley-carhart-back.png differ diff --git a/public/cards/security/049-lesley-carhart-front.png b/public/cards/security/049-lesley-carhart-front.png index 62a29c60..6d91a98f 100644 Binary files a/public/cards/security/049-lesley-carhart-front.png and b/public/cards/security/049-lesley-carhart-front.png differ diff --git a/public/cards/security/050-katie-nickels-front.png b/public/cards/security/050-katie-nickels-front.png index 118e8db8..7e9b953b 100644 Binary files a/public/cards/security/050-katie-nickels-front.png and b/public/cards/security/050-katie-nickels-front.png differ diff --git a/scripts/security-pros.mjs b/scripts/security-pros.mjs index 50bdfde6..59d2972f 100644 --- a/scripts/security-pros.mjs +++ b/scripts/security-pros.mjs @@ -25,6 +25,17 @@ therefore opt-in only and never part of `all`. Running it means accepting that every affected face must be checked by eye before publish. + PORTRAITS ARE CONDITIONED ON A REAL PHOTOGRAPH + ---------------------------------------------- + The first pass generated faces from text prompts alone, so every portrait was an + invented stranger. Generation now takes a verified reference photo as input (see + scripts/security-refs.mjs), and a card with no verified photo gets NO FACE — the + front renders a plate saying so. We do not invent a likeness of a real person. + + The reference photo's photographer is credited: most are CC BY / CC BY-SA, and a + conditioned portrait is a derivative work. data/security-references.json carries + the provenance and the card page prints it. + The roster is src/data/security.ts, imported directly (Node strips the types), so there is no second copy of the data to keep in sync. @@ -72,6 +83,9 @@ process.emitWarning = (warning, ...rest) => { return emitWarning(warning, ...rest); }; +const REFS_DIR = path.join(ROOT, 'assets', 'references', 'security-pros'); +const REF_MANIFEST = path.join(ROOT, 'data', 'security-references.json'); + const DIRS = { approvedArt: path.join(ROOT, 'assets', 'portraits', 'security-pros'), workingArt: path.join(ROOT, 'assets', 'portraits-art', 'security-pros'), @@ -207,6 +221,40 @@ async function editImage(pngBuffer, prompt, { size = '1024x1536' } = {}) { // -------------------------------------------------------------- prompts ---- +function referenceFor(h) { + const p = path.join(REFS_DIR, `${cardId(h.number)}.jpg`); + return fssync.existsSync(p) ? p : null; +} + +let refManifest = null; +function refInfo(slug) { + if (!refManifest) { + refManifest = fssync.existsSync(REF_MANIFEST) + ? JSON.parse(fssync.readFileSync(REF_MANIFEST, 'utf8')) + : []; + } + return refManifest.find((r) => r.slug === slug)?.ref || null; +} + +/** + * Image-to-image instruction. The reference photograph is the subject; the model's + * job is to restyle it, not to reimagine who it is. Says so several ways, because + * the failure we are guarding against is precisely a plausible different face. + */ +function likenessPrompt(h) { + const motif = (h.domains || []).slice(0, 2).join(' and ') || 'computer security'; + return [ + 'Repaint this photograph as a realistic semi-painterly portrait for a premium collectible trading card.', + 'CRITICAL: keep the SAME PERSON. Preserve the exact facial structure, likeness, age, hairstyle, facial hair,', + 'glasses, skin tone and expression of the person in the photograph. Do not beautify, do not de-age,', + 'do not substitute a different face. The result must be recognisable as the same individual.', + 'Head and shoulders, facing the viewer, warm cinematic studio lighting with a cool rim light,', + 'detailed painted realism, dignified and serious.', + `Replace the background with a dark background carrying subtle faint motifs of ${motif}.`, + 'No text, no letters, no logos, no border, no card frame, no watermark.', + ].join(' '); +} + function portraitPrompt(h) { const motif = (h.domains || []).slice(0, 2).join(' and ') || 'computer security'; const era = h.era ? ` Period-accurate to ${h.era}.` : ''; @@ -331,9 +379,20 @@ async function pool(items, label, task) { async function portraits(roster) { await ensureDirs(); console.log(`portraits: ${provider()} / ${provider() === 'openai' ? OPENAI_MODEL() : GEMINI_MODEL()}`); - return pool(roster, 'portraits', async (h) => { + const withRef = roster.filter((h) => referenceFor(h)); + const without = roster.filter((h) => !referenceFor(h)); + if (without.length) { + console.log( + `portraits: ${without.length} have no verified reference photo and will get no face ` + + `(${without.map((h) => h.number).join(', ')}). Run scripts/security-refs.mjs to look again.` + ); + } + return pool(withRef, 'portraits', async (h) => { if (portraitFor(h)) return 'skip'; - const buf = await withRetry(cardId(h.number), () => generateImage(portraitPrompt(h))); + // Image-to-image off the real photograph. Never generateImage() — a text-only + // prompt is what produced the invented faces this pipeline exists to prevent. + const ref = await fs.readFile(referenceFor(h)); + const buf = await withRetry(cardId(h.number), () => editImage(ref, likenessPrompt(h), { size: '1024x1024' })); await fs.writeFile(path.join(DIRS.workingArt, `${cardId(h.number)}.png`), buf); }); } @@ -367,10 +426,11 @@ async function render(roster) { const id = cardId(h.number); try { const art = portraitFor(h); - if (!art) throw new Error('no portrait art — run `portraits` first'); - const uri = `data:image/png;base64,${(await fs.readFile(art)).toString('base64')}`; - await shoot(buildFront(h, uri), id, 'front'); - await shoot(buildBack(h), id, 'back'); + // No art is a legitimate outcome: nobody with no verified reference photo + // gets an invented face. buildFront renders a "no likeness" plate instead. + const uri = art ? `data:image/png;base64,${(await fs.readFile(art)).toString('base64')}` : null; + await shoot(buildFront(h, uri, refInfo(h.slug)), id, 'front'); + await shoot(buildBack(h, refInfo(h.slug)), id, 'back'); ok++; console.log(`✓ ${String(h.number).padStart(2, '0')} ${h.name} (${h.rarity})`); } catch (err) { diff --git a/scripts/security-refs.mjs b/scripts/security-refs.mjs new file mode 100644 index 00000000..63aa105f --- /dev/null +++ b/scripts/security-refs.mjs @@ -0,0 +1,388 @@ +#!/usr/bin/env node +/* + Security Professionals (Series Three) — reference photo resolution. + + WHY THIS EXISTS + --------------- + The first art pass generated portraits from text prompts alone, so every face + was an invented stranger rather than the real person. Portraits are now + conditioned on an actual photograph, and this stage is what finds one. + + IDENTITY IS NOT ASSUMED. A Wikidata search for a name returns humans, not the + right human: "Michael Howard" resolves to the British Conservative leader, + "Jeff Williams" to a Canadian rugby player, "Ivan Ristic" to an art historian. + Shipping those would put a stranger's face on the card, which is worse than the + generic one it replaces. So a candidate is only accepted when it clears + `looksRight()` below, and everything else is reported for a human to judge. + + Resolution order, highest authority first: + 1. OVERRIDES hand-verified, including photos supplied directly + 2. Wikidata P18 free file already wired to a verified item + 3. Commons search free file that exists but is not wired to the item + 4. nothing the card gets no face; it does NOT get an invented one + + Provenance for every accepted photo is written to data/security-references.json + and is what the card page credits. A CC BY / CC BY-SA photo obliges us to name + the photographer, and a derivative portrait inherits that obligation. + + Run: + node scripts/security-refs.mjs resolve # find candidates, write the manifest + node scripts/security-refs.mjs fetch # download accepted references + node scripts/security-refs.mjs report # who is still missing a face +*/ +import fs from 'node:fs/promises'; +import fssync from 'node:fs'; +import path from 'node:path'; +import process from 'node:process'; +import { fileURLToPath } from 'node:url'; + +const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const UA = 'opensourcelegends.com card pipeline (anthony@profullstack.com)'; + +const emitWarning = process.emitWarning.bind(process); +process.emitWarning = (warning, ...rest) => { + const code = + rest.find((r) => typeof r === 'string' && r.startsWith('MODULE_')) ?? + rest.find((r) => r && typeof r === 'object')?.code; + if (code === 'MODULE_TYPELESS_PACKAGE_JSON') return; + return emitWarning(warning, ...rest); +}; + +const REFS_DIR = path.join(ROOT, 'assets', 'references', 'security-pros'); +const MANIFEST = path.join(ROOT, 'data', 'security-references.json'); +const cardId = (n) => `card_${String(n).padStart(3, '0')}`; + +/** + * Hand-verified references. An entry here overrides every lookup. + * + * `license` must describe the actual terms. "supplied" means the site owner + * provided the file directly and is asserting the right to use it: that is not + * a free licence, so the card credits the source rather than claiming CC terms + * the deck cannot back. + */ +const OVERRIDES = { + // Wikidata's P18 for Rivest is a two-person conference photo; a portrait pass + // conditioned on it could easily lock onto the wrong face. This is the solo crop. + 'ron-rivest': { commonsFile: 'Rivest cropped.jpg' }, + + // CC-licensed conference photography, found via Openverse where Commons had + // nothing. Alexandre Dulaunoy shot hack.lu 2006 and released the set CC BY-SA. + 'wietse-venema': { + url: 'https://live.staticflickr.com/104/276434076_581ed1215d_b.jpg', + credit: 'Alexandre Dulaunoy', + license: 'CC BY-SA 2.0', + licenseUrl: 'https://creativecommons.org/licenses/by-sa/2.0/', + sourceUrl: 'https://www.flickr.com/photos/31797858@N00/276434076', + note: 'hack.lu 2006. Supersedes the Wikidata P18 file, which shows a different man.', + }, + 'renaud-deraison': { + url: 'https://live.staticflickr.com/122/276307114_f29f0a38ec_b.jpg', + credit: 'Alexandre Dulaunoy', + license: 'CC BY-SA 2.0', + licenseUrl: 'https://creativecommons.org/licenses/by-sa/2.0/', + sourceUrl: 'https://www.flickr.com/photos/31797858@N00/276307114', + note: 'hack.lu 2006 opening speech.', + }, + 'thomas-ptacek': { + url: 'https://live.staticflickr.com/2733/4302617844_52625091af_b.jpg', + credit: 'cfournie', + license: 'CC BY 2.0', + licenseUrl: 'https://creativecommons.org/licenses/by/2.0/', + sourceUrl: 'https://www.flickr.com/photos/37932109@N05/4302617844', + }, + + // Official bio / press photographs, used as portrait references on the site + // owner's authorisation. These are not freely licensed, so the manifest records + // where each came from rather than asserting CC terms the deck cannot back. + 'adam-shostack': { + url: 'https://shostack.org/img/site/Adam-Shostack-headshot.png', + credit: 'Adam Shostack', + license: 'supplied', + sourceUrl: 'https://shostack.org/about/adam', + note: 'Headshot published on his own site.', + }, + 'kevin-mandia': { + url: 'https://ballisticventures.com/wp-content/uploads/2024/06/Kevin-Mandia.png', + credit: 'Ballistic Ventures', + license: 'supplied', + sourceUrl: 'https://www.ballisticventures.com/team/kevin-mandia', + note: 'Partner bio photo.', + }, + 'mark-curphey': { + url: 'https://user-images.githubusercontent.com/143776/193322138-009524de-bae1-4190-989d-2d729ad0beb8.png', + credit: 'Mark Curphey', + license: 'supplied', + sourceUrl: 'https://www.crash-override.com/', + note: 'Portrait published by his own company.', + }, + 'martin-roesch': { + url: 'https://media.kasperskycontenthub.com/wp-content/uploads/sites/103/2013/07/07043722/sourcefire.jpg', + credit: 'Sourcefire press photo', + license: 'supplied', + sourceUrl: 'https://threatpost.com/', + note: 'Sourcefire-era press photograph. Supersedes the Commons files, which are a different Martin.', + }, + + 'jeremiah-grossman': { + url: 'https://cdn.prod.website-files.com/62deb8a5505ca121b1665bbe/63cf05d85cad9419bfa83096_Photo%20Oct%2003%202022%2C%206%2054%2000%20AM-p-800.jpg', + credit: 'Jeremiah Grossman', + license: 'supplied', + sourceUrl: 'https://www.jeremiahgrossman.com/', + note: 'Publicity photo from his own site, supplied by the site owner as reference.', + }, +}; + +/** Wikidata items confirmed to be the wrong human — never accept these. */ +const REJECT_QIDS = new Set([ + 'Q325381', // Michael Howard, British politician + 'Q38051181', // Robert Hansen, Danish crime novelist 1883-1957 +]); + +/** + * Auto-resolution found something, a human looked at it, and it did not hold up. + * These get no face. Recorded with the reason so nobody re-adds them by rerunning + * the resolver and trusting the output. + */ +const REJECTED = { + 'jeff-williams': + '"Jeff Williams photo.jpg" is one of several unrelated Jeff Williamses. Cannot confirm it is the Contrast Security founder.', + 'gary-mcgraw': + 'Only candidate is a group briefing shot with audience heads across the frame; the other Commons hits are unrelated US Navy photographs.', + 'michael-howard': + 'Every Commons hit is a different Michael Howard — the British politician, or a young man with no connection to the Microsoft SDL author.', +}; + +const j = async (url) => { + const r = await fetch(url, { headers: { 'User-Agent': UA } }); + if (!r.ok) throw new Error(`${r.status} for ${url}`); + return r.json(); +}; + +async function loadRoster() { + // src/data/security.ts statically imports the manifest, so it has to exist + // before the roster can be loaded — including on the very first resolve, and + // after someone deletes it to force a clean run. + if (!fssync.existsSync(MANIFEST)) { + await fs.mkdir(path.dirname(MANIFEST), { recursive: true }); + await fs.writeFile(MANIFEST, '[]\n'); + } + const mod = await import(path.join(ROOT, 'src', 'data', 'security.ts')); + return mod.pros; +} + +/** + * Does this Wikidata item plausibly describe the person on the card? + * Deliberately conservative: a miss costs a manual lookup, a false accept ships + * a stranger's face. + */ +const FIELD = + /secur|crypt|comput|software|engineer|scientist|hacker|program|internet|research|professor|technolog|inform|network|business|writer/i; + +function looksRight(entity, pro) { + if (!entity) return false; + const claims = entity.claims || {}; + if (!(claims.P31 || []).some((c) => c.mainsnak?.datavalue?.value?.id === 'Q5')) return false; + + const label = entity.labels?.en?.value || ''; + const desc = entity.descriptions?.en?.value || ''; + const aliases = (entity.aliases?.en || []).map((a) => a.value); + + // Surname must actually appear — "Mark Dowd" matching "Mark Dowdall" is a miss. + const surname = pro.name.split(/\s+/).pop().toLowerCase(); + const names = [label, ...aliases].map((s) => s.toLowerCase()); + if (!names.some((n) => n.split(/\s+/).includes(surname))) return false; + + // A dead-before-the-internet birth date rules the person out. + const born = claims.P569?.[0]?.mainsnak?.datavalue?.value?.time; + if (born) { + const year = Number(String(born).slice(1, 5)); + if (year && year < 1920) return false; + } + + return FIELD.test(desc) || FIELD.test(label); +} + + +const norm = (s) => + String(s) + .toLowerCase() + .replace(/[._-]+/g, ' ') + .replace(/[^a-z0-9 ]+/g, '') + .replace(/\s+/g, ' ') + .trim(); + +/** + * True only when the filename *begins* with the person's full name, so + * "Alex Sotirov.jpg" and "J. Alex Halderman - 2018.jpg" pass while + * "Martin Wagenleiter (Roesch) 861x1200.jpg" and "HT-Jeffrey-Williams.jpg" do not. + */ +function filenameOpensWithName(filename, name) { + const f = norm(filename.replace(/\.[a-z0-9]+$/i, '')); + return f.startsWith(norm(name)); +} + +/** Commons file -> direct URL plus the licence and author we are obliged to credit. */ +async function commonsMeta(filename) { + const d = await j( + `https://commons.wikimedia.org/w/api.php?action=query&format=json&prop=imageinfo&iiprop=url|extmetadata&iiurlwidth=1024&titles=${encodeURIComponent('File:' + filename)}` + ); + const page = Object.values(d.query?.pages || {})[0]; + const info = page?.imageinfo?.[0]; + if (!info) return null; + const meta = info.extmetadata || {}; + const strip = (v) => String(v?.value ?? '').replace(/<[^>]*>/g, '').trim(); + // A 19th-century photographer credit means the search found a historical + // namesake, not the person on the card. + const dateRaw = strip(meta.DateTimeOriginal) || strip(meta.DateTime); + const year = Number((dateRaw.match(/\b(1[6-9]\d{2}|20\d{2})\b/) || [])[1]); + if (year && year < 1950) return null; + + return { + url: info.thumburl || info.url, + credit: strip(meta.Artist) || 'Unknown', + license: strip(meta.LicenseShortName) || 'unknown', + licenseUrl: strip(meta.LicenseUrl) || '', + sourceUrl: info.descriptionurl, + file: filename, + }; +} + +async function resolve() { + const roster = await loadRoster(); + const manifest = []; + + for (const pro of roster) { + const row = { number: pro.number, slug: pro.slug, name: pro.name, ref: null, candidates: [] }; + + if (REJECTED[pro.slug]) { + row.rejected = REJECTED[pro.slug]; + manifest.push(row); + console.log(`${String(pro.number).padStart(2)} ${pro.name.padEnd(22)} REJECTED ${row.rejected.slice(0, 58)}`); + continue; + } + + if (OVERRIDES[pro.slug]) { + const o = OVERRIDES[pro.slug]; + row.ref = o.commonsFile + ? { ...(await commonsMeta(o.commonsFile)), ...o, via: 'override' } + : { ...o, via: 'override' }; + manifest.push(row); + console.log(`${String(pro.number).padStart(2)} ${pro.name.padEnd(22)} OVERRIDE ${row.ref.license}`); + continue; + } + + let entity = null; + let qid = null; + try { + const s = await j( + `https://www.wikidata.org/w/api.php?action=wbsearchentities&search=${encodeURIComponent(pro.name)}&language=en&format=json&limit=6&type=item` + ); + for (const hit of s.search || []) { + if (REJECT_QIDS.has(hit.id)) continue; + const e = await j(`https://www.wikidata.org/wiki/Special:EntityData/${hit.id}.json`); + const ent = e.entities?.[hit.id]; + if (looksRight(ent, pro)) { + entity = ent; + qid = hit.id; + break; + } + } + } catch (err) { + row.error = String(err.message).slice(0, 80); + } + + const p18 = entity?.claims?.P18?.[0]?.mainsnak?.datavalue?.value; + if (p18) { + const meta = await commonsMeta(p18); + if (meta) { + row.ref = { ...meta, qid, via: 'wikidata-p18' }; + row.desc = entity.descriptions?.en?.value || ''; + } + } + + // No P18: a free file may still exist on Commons, unlinked. Containing the + // name somewhere is far too weak a test — "Martin Wagenleiter (Roesch)" and + // "Merseyside PTE 40th anniversary" both matched that way, and a search for + // "Robert M. Lee" happily returns a daguerreotype of the Confederate general. + // So the filename must OPEN with the person's full name. + if (!row.ref) { + try { + const s = await j( + `https://commons.wikimedia.org/w/api.php?action=query&format=json&generator=search&gsrnamespace=6&gsrlimit=10&gsrsearch=${encodeURIComponent(pro.name)}` + ); + for (const p of Object.values(s.query?.pages || {})) { + const f = p.title.replace(/^File:/, ''); + if (!/\.(jpe?g|png)$/i.test(f)) continue; + if (!filenameOpensWithName(f, pro.name)) continue; + row.candidates.push(f); + } + if (row.candidates.length) { + const meta = await commonsMeta(row.candidates[0]); + if (meta) row.ref = { ...meta, qid, via: 'commons-search' }; + } + } catch {} + } + + manifest.push(row); + const tag = row.ref ? row.ref.via.toUpperCase().padEnd(10) : 'NONE '; + console.log( + `${String(pro.number).padStart(2)} ${pro.name.padEnd(22)} ${tag} ${row.ref ? `${row.ref.license} | ${row.ref.credit.slice(0, 34)}` : (row.desc || '')}` + ); + } + + await fs.mkdir(path.dirname(MANIFEST), { recursive: true }); + await fs.writeFile(MANIFEST, JSON.stringify(manifest, null, 2) + '\n'); + const got = manifest.filter((m) => m.ref).length; + console.log(`\nresolve: ${got}/${manifest.length} have a reference photo -> ${path.relative(ROOT, MANIFEST)}`); + const none = manifest.filter((m) => !m.ref); + console.log(`no photo (${none.length}) — these get no face, never an invented one:`); + for (const m of none) console.log(` ${String(m.number).padStart(2)} ${m.name}${m.rejected ? ' — ' + m.rejected.slice(0, 70) : ''}`); +} + +async function fetchRefs() { + const manifest = JSON.parse(await fs.readFile(MANIFEST, 'utf8')); + await fs.mkdir(REFS_DIR, { recursive: true }); + let ok = 0; + const failed = []; + for (const row of manifest.filter((m) => m.ref)) { + const dest = path.join(REFS_DIR, `${cardId(row.number)}.jpg`); + if (fssync.existsSync(dest)) { + ok++; + continue; + } + try { + const r = await fetch(row.ref.url, { headers: { 'User-Agent': UA } }); + if (!r.ok) throw new Error(String(r.status)); + await fs.writeFile(dest, Buffer.from(await r.arrayBuffer())); + ok++; + console.log(`✓ ${String(row.number).padStart(2)} ${row.name}`); + } catch (err) { + failed.push(`${row.number} ${row.name}: ${err.message}`); + } + } + console.log(`fetch: ${ok} references in ${path.relative(ROOT, REFS_DIR)}, ${failed.length} failed`); + for (const f of failed) console.log(' ✗ ' + f); +} + +async function report() { + const manifest = JSON.parse(await fs.readFile(MANIFEST, 'utf8')); + const byLicense = {}; + for (const m of manifest.filter((x) => x.ref)) { + byLicense[m.ref.license] = (byLicense[m.ref.license] || 0) + 1; + } + console.log('references by licence:', byLicense); + const missing = manifest.filter((m) => !m.ref); + console.log(`\n${missing.length} with no reference photo — these get no face, not an invented one:`); + for (const m of missing) console.log(` ${String(m.number).padStart(2)} ${m.name}`); +} + +const cmd = process.argv[2] || 'resolve'; +const fns = { resolve, fetch: fetchRefs, report }; +if (!fns[cmd]) { + console.error(`unknown command: ${cmd}\n\n resolve | fetch | report`); + process.exit(1); +} +fns[cmd]().catch((err) => { + console.error('ERROR:', err.message); + process.exit(1); +}); diff --git a/scripts/security-template.mjs b/scripts/security-template.mjs index 1ebc489c..df8ef757 100644 --- a/scripts/security-template.mjs +++ b/scripts/security-template.mjs @@ -85,7 +85,7 @@ const shell = (accent) => `${FONTS} .crest { display:flex; align-items:center; gap:7px; } .crest .t { font-family:'Oswald'; color:#fff; font-weight:700; font-size:11px; letter-spacing:.6px; line-height:1.05; }`; -export function buildFront(pro, portraitDataUri) { +export function buildFront(pro, portraitDataUri, ref) { const accent = accentOf(pro); const kw = traits(pro).join(' · '); const alias = pro.handle @@ -98,6 +98,13 @@ export function buildFront(pro, portraitDataUri) { invented text. The caption band is now sized to what it actually holds. */ .portrait { position:absolute; left:14px; right:14px; top:14px; height:72%; border-radius:16px; overflow:hidden; box-shadow:inset 0 0 0 2px ${accent}55; } .portrait img { width:100%; height:100%; object-fit:cover; object-position:center 18%; } + /* A card with no verified reference photo says so, rather than carrying an + invented face. This is a deliberate, visible gap. */ + .nolikeness { width:100%; height:100%; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:16px; + background:radial-gradient(90% 70% at 50% 35%, #171d26 0%, #0c1016 70%); } + .nolikeness .wm { opacity:.12; position:absolute; } + .nolikeness .initials { font-family:'Oswald'; font-weight:700; font-size:96px; letter-spacing:6px; color:${accent}; opacity:.85; z-index:1; } + .nolikeness .nlnote { font-family:'JetBrains Mono',monospace; font-size:15px; line-height:1.5; color:#8b93a5; text-align:center; letter-spacing:.6px; z-index:1; } .portrait::after { content:''; position:absolute; left:0; right:0; bottom:0; height:34%; background:linear-gradient(180deg,transparent,rgba(11,15,17,.94)); } .num { position:absolute; top:18px; left:20px; z-index:4; font-family:'Oswald'; font-weight:700; font-size:40px; line-height:1; padding:2px 14px 4px; background:${accent}; color:#0a0810; border-radius:0 0 12px 0; clip-path:polygon(0 0,100% 0,84% 100%,0 100%); } .crestbox { position:absolute; top:20px; right:20px; z-index:4; } @@ -110,7 +117,13 @@ export function buildFront(pro, portraitDataUri) { .kw { font-family:'Oswald'; flex:1; text-align:center; color:#cfd2dc; font-weight:600; font-size:15px; letter-spacing:1.2px; }