diff --git a/.gitignore b/.gitignore index 879add9..9b9e01d 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 f04580e..8b3254f 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 new file mode 100644 index 0000000..d904bce Binary files /dev/null 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 new file mode 100644 index 0000000..c80a362 Binary files /dev/null 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 new file mode 100644 index 0000000..08b95b2 Binary files /dev/null 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 new file mode 100644 index 0000000..08cedcd Binary files /dev/null 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 new file mode 100644 index 0000000..5a876b3 Binary files /dev/null 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 new file mode 100644 index 0000000..3684779 Binary files /dev/null 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 new file mode 100644 index 0000000..8ed44fc Binary files /dev/null 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 new file mode 100644 index 0000000..b864b84 Binary files /dev/null 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 new file mode 100644 index 0000000..bdd32f1 Binary files /dev/null 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 new file mode 100644 index 0000000..cb59e4e Binary files /dev/null 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 new file mode 100644 index 0000000..17e688b Binary files /dev/null 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 new file mode 100644 index 0000000..6867f81 Binary files /dev/null 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 new file mode 100644 index 0000000..a917e03 Binary files /dev/null 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 new file mode 100644 index 0000000..99796ce Binary files /dev/null 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 new file mode 100644 index 0000000..d07daa8 Binary files /dev/null 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 new file mode 100644 index 0000000..780cd93 Binary files /dev/null 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 new file mode 100644 index 0000000..9043a34 Binary files /dev/null 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 new file mode 100644 index 0000000..0bb698a Binary files /dev/null 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 new file mode 100644 index 0000000..e789c3c Binary files /dev/null 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 new file mode 100644 index 0000000..1af7f25 Binary files /dev/null and b/assets/portraits/security-pros/card_020.png differ diff --git a/assets/portraits/security-pros/card_022.png b/assets/portraits/security-pros/card_022.png new file mode 100644 index 0000000..c49e22c Binary files /dev/null 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 new file mode 100644 index 0000000..920b10d Binary files /dev/null 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 new file mode 100644 index 0000000..8753f02 Binary files /dev/null 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 new file mode 100644 index 0000000..bb3a9ee Binary files /dev/null and b/assets/portraits/security-pros/card_025.png differ diff --git a/assets/portraits/security-pros/card_027.png b/assets/portraits/security-pros/card_027.png new file mode 100644 index 0000000..1d5a67f Binary files /dev/null and b/assets/portraits/security-pros/card_027.png differ diff --git a/assets/portraits/security-pros/card_031.png b/assets/portraits/security-pros/card_031.png new file mode 100644 index 0000000..4864e7c Binary files /dev/null and b/assets/portraits/security-pros/card_031.png differ diff --git a/assets/portraits/security-pros/card_035.png b/assets/portraits/security-pros/card_035.png new file mode 100644 index 0000000..09c15e9 Binary files /dev/null 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 new file mode 100644 index 0000000..73f4af3 Binary files /dev/null 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 new file mode 100644 index 0000000..258ef48 Binary files /dev/null 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 new file mode 100644 index 0000000..8d966be Binary files /dev/null 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 new file mode 100644 index 0000000..4dd76ef Binary files /dev/null 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 new file mode 100644 index 0000000..b886319 Binary files /dev/null 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 new file mode 100644 index 0000000..60ae166 Binary files /dev/null and b/assets/portraits/security-pros/card_041.png differ diff --git a/assets/portraits/security-pros/card_043.png b/assets/portraits/security-pros/card_043.png new file mode 100644 index 0000000..667f780 Binary files /dev/null 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 new file mode 100644 index 0000000..d0f7ecc Binary files /dev/null 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 new file mode 100644 index 0000000..5bc5ddb Binary files /dev/null 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 new file mode 100644 index 0000000..e81e6d1 Binary files /dev/null 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 new file mode 100644 index 0000000..a2b7df9 Binary files /dev/null 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 new file mode 100644 index 0000000..63cfaef Binary files /dev/null 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 new file mode 100644 index 0000000..cfe561a Binary files /dev/null and b/assets/portraits/security-pros/card_049.png differ diff --git a/data/security-references.json b/data/security-references.json new file mode 100644 index 0000000..9100796 --- /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 new file mode 100644 index 0000000..22db10a Binary files /dev/null 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 new file mode 100644 index 0000000..456a390 Binary files /dev/null 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 new file mode 100644 index 0000000..35c28d3 Binary files /dev/null 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 new file mode 100644 index 0000000..94fecf8 Binary files /dev/null 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 new file mode 100644 index 0000000..8d8dc62 Binary files /dev/null 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 new file mode 100644 index 0000000..d366c2c Binary files /dev/null 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 new file mode 100644 index 0000000..c316c6d Binary files /dev/null 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 new file mode 100644 index 0000000..6fa0c76 Binary files /dev/null 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 new file mode 100644 index 0000000..2aa1e27 Binary files /dev/null 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 new file mode 100644 index 0000000..5667914 Binary files /dev/null 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 new file mode 100644 index 0000000..32cad92 Binary files /dev/null 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 new file mode 100644 index 0000000..b30f44d Binary files /dev/null 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 new file mode 100644 index 0000000..665c466 Binary files /dev/null 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 new file mode 100644 index 0000000..8192b1d Binary files /dev/null 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 new file mode 100644 index 0000000..beee244 Binary files /dev/null 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 new file mode 100644 index 0000000..c2148af Binary files /dev/null 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 new file mode 100644 index 0000000..a6aed79 Binary files /dev/null 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 new file mode 100644 index 0000000..4528550 Binary files /dev/null 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 new file mode 100644 index 0000000..2ccf828 Binary files /dev/null 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 new file mode 100644 index 0000000..6aa9e79 Binary files /dev/null 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 new file mode 100644 index 0000000..90a8bbe Binary files /dev/null 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 new file mode 100644 index 0000000..69d034a Binary files /dev/null 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 new file mode 100644 index 0000000..8d2e733 Binary files /dev/null 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 new file mode 100644 index 0000000..513dbf4 Binary files /dev/null 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 new file mode 100644 index 0000000..e382a9a Binary files /dev/null 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 new file mode 100644 index 0000000..7b7052c Binary files /dev/null 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 new file mode 100644 index 0000000..76121f3 Binary files /dev/null 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 new file mode 100644 index 0000000..ce3f9d8 Binary files /dev/null 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 new file mode 100644 index 0000000..c785144 Binary files /dev/null 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 new file mode 100644 index 0000000..2880d76 Binary files /dev/null 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 new file mode 100644 index 0000000..3fc6923 Binary files /dev/null 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 new file mode 100644 index 0000000..6bb4d11 Binary files /dev/null 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 new file mode 100644 index 0000000..4eaabb4 Binary files /dev/null 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 new file mode 100644 index 0000000..52efeaf Binary files /dev/null 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 new file mode 100644 index 0000000..a5bbdae Binary files /dev/null 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 new file mode 100644 index 0000000..bb4bbbb Binary files /dev/null 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 new file mode 100644 index 0000000..6624303 Binary files /dev/null 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 new file mode 100644 index 0000000..84437d4 Binary files /dev/null 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 new file mode 100644 index 0000000..3b38d35 Binary files /dev/null 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 new file mode 100644 index 0000000..d1ec09b Binary files /dev/null and b/public/cards/security/020-martin-roesch-front.png differ diff --git a/public/cards/security/021-gerald-combs-back.png b/public/cards/security/021-gerald-combs-back.png new file mode 100644 index 0000000..b8cc12d Binary files /dev/null and b/public/cards/security/021-gerald-combs-back.png differ diff --git a/public/cards/security/021-gerald-combs-front.png b/public/cards/security/021-gerald-combs-front.png new file mode 100644 index 0000000..65f8bd8 Binary files /dev/null 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 new file mode 100644 index 0000000..bb38908 Binary files /dev/null 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 new file mode 100644 index 0000000..15424ec Binary files /dev/null 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 new file mode 100644 index 0000000..f850fa4 Binary files /dev/null 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 new file mode 100644 index 0000000..1785230 Binary files /dev/null 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 new file mode 100644 index 0000000..593d746 Binary files /dev/null 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 new file mode 100644 index 0000000..d713bc2 Binary files /dev/null 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 new file mode 100644 index 0000000..667dd89 Binary files /dev/null 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 new file mode 100644 index 0000000..a80bca1 Binary files /dev/null and b/public/cards/security/025-jeremiah-grossman-front.png differ diff --git a/public/cards/security/026-robert-hansen-back.png b/public/cards/security/026-robert-hansen-back.png new file mode 100644 index 0000000..0f23d79 Binary files /dev/null and b/public/cards/security/026-robert-hansen-back.png differ diff --git a/public/cards/security/026-robert-hansen-front.png b/public/cards/security/026-robert-hansen-front.png new file mode 100644 index 0000000..247ea7f Binary files /dev/null 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 new file mode 100644 index 0000000..2148450 Binary files /dev/null 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 new file mode 100644 index 0000000..16ca9dc Binary files /dev/null and b/public/cards/security/027-mark-curphey-front.png differ diff --git a/public/cards/security/028-jeff-williams-back.png b/public/cards/security/028-jeff-williams-back.png new file mode 100644 index 0000000..f432ca9 Binary files /dev/null and b/public/cards/security/028-jeff-williams-back.png differ diff --git a/public/cards/security/028-jeff-williams-front.png b/public/cards/security/028-jeff-williams-front.png new file mode 100644 index 0000000..9cf3ff4 Binary files /dev/null and b/public/cards/security/028-jeff-williams-front.png differ diff --git a/public/cards/security/029-gary-mcgraw-back.png b/public/cards/security/029-gary-mcgraw-back.png new file mode 100644 index 0000000..c7086da Binary files /dev/null and b/public/cards/security/029-gary-mcgraw-back.png differ diff --git a/public/cards/security/029-gary-mcgraw-front.png b/public/cards/security/029-gary-mcgraw-front.png new file mode 100644 index 0000000..e272c3a Binary files /dev/null and b/public/cards/security/029-gary-mcgraw-front.png differ diff --git a/public/cards/security/030-michael-howard-back.png b/public/cards/security/030-michael-howard-back.png new file mode 100644 index 0000000..05e5226 Binary files /dev/null and b/public/cards/security/030-michael-howard-back.png differ diff --git a/public/cards/security/030-michael-howard-front.png b/public/cards/security/030-michael-howard-front.png new file mode 100644 index 0000000..b29227e Binary files /dev/null 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 new file mode 100644 index 0000000..a68f21a Binary files /dev/null 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 new file mode 100644 index 0000000..c30a674 Binary files /dev/null and b/public/cards/security/031-adam-shostack-front.png differ diff --git a/public/cards/security/032-ivan-ristic-back.png b/public/cards/security/032-ivan-ristic-back.png new file mode 100644 index 0000000..33dd533 Binary files /dev/null and b/public/cards/security/032-ivan-ristic-back.png differ diff --git a/public/cards/security/032-ivan-ristic-front.png b/public/cards/security/032-ivan-ristic-front.png new file mode 100644 index 0000000..42ebd24 Binary files /dev/null and b/public/cards/security/032-ivan-ristic-front.png differ diff --git a/public/cards/security/033-mark-dowd-back.png b/public/cards/security/033-mark-dowd-back.png new file mode 100644 index 0000000..f231912 Binary files /dev/null and b/public/cards/security/033-mark-dowd-back.png differ diff --git a/public/cards/security/033-mark-dowd-front.png b/public/cards/security/033-mark-dowd-front.png new file mode 100644 index 0000000..d57f13c Binary files /dev/null and b/public/cards/security/033-mark-dowd-front.png differ diff --git a/public/cards/security/034-halvar-flake-back.png b/public/cards/security/034-halvar-flake-back.png new file mode 100644 index 0000000..6a0d853 Binary files /dev/null and b/public/cards/security/034-halvar-flake-back.png differ diff --git a/public/cards/security/034-halvar-flake-front.png b/public/cards/security/034-halvar-flake-front.png new file mode 100644 index 0000000..8caa1fe Binary files /dev/null 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 new file mode 100644 index 0000000..e6ca68a Binary files /dev/null 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 new file mode 100644 index 0000000..b330006 Binary files /dev/null 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 new file mode 100644 index 0000000..887c6a6 Binary files /dev/null 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 new file mode 100644 index 0000000..80d67b8 Binary files /dev/null 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 new file mode 100644 index 0000000..6dd1e29 Binary files /dev/null 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 new file mode 100644 index 0000000..270c2f0 Binary files /dev/null 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 new file mode 100644 index 0000000..806661f Binary files /dev/null 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 new file mode 100644 index 0000000..47f6f07 Binary files /dev/null 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 new file mode 100644 index 0000000..4cfd6c5 Binary files /dev/null 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 new file mode 100644 index 0000000..7a9a407 Binary files /dev/null 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 new file mode 100644 index 0000000..d4b6cb5 Binary files /dev/null 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 new file mode 100644 index 0000000..fc1558c Binary files /dev/null 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 new file mode 100644 index 0000000..93cf0f4 Binary files /dev/null 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 new file mode 100644 index 0000000..d0678bb Binary files /dev/null and b/public/cards/security/041-window-snyder-front.png differ diff --git a/public/cards/security/042-heather-adkins-back.png b/public/cards/security/042-heather-adkins-back.png new file mode 100644 index 0000000..7a13a8b Binary files /dev/null and b/public/cards/security/042-heather-adkins-back.png differ diff --git a/public/cards/security/042-heather-adkins-front.png b/public/cards/security/042-heather-adkins-front.png new file mode 100644 index 0000000..c5c04b2 Binary files /dev/null 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 new file mode 100644 index 0000000..fb941b3 Binary files /dev/null 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 new file mode 100644 index 0000000..a139857 Binary files /dev/null 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 new file mode 100644 index 0000000..a53d389 Binary files /dev/null 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 new file mode 100644 index 0000000..e2a00a4 Binary files /dev/null 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 new file mode 100644 index 0000000..f691a64 Binary files /dev/null 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 new file mode 100644 index 0000000..21499e6 Binary files /dev/null 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 new file mode 100644 index 0000000..5c95854 Binary files /dev/null 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 new file mode 100644 index 0000000..1fabb79 Binary files /dev/null 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 new file mode 100644 index 0000000..c3a6b91 Binary files /dev/null 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 new file mode 100644 index 0000000..4ef3354 Binary files /dev/null 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 new file mode 100644 index 0000000..5dc332b Binary files /dev/null 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 new file mode 100644 index 0000000..a02063c Binary files /dev/null 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 new file mode 100644 index 0000000..ec156eb Binary files /dev/null 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 new file mode 100644 index 0000000..6d91a98 Binary files /dev/null and b/public/cards/security/049-lesley-carhart-front.png differ diff --git a/public/cards/security/050-katie-nickels-back.png b/public/cards/security/050-katie-nickels-back.png new file mode 100644 index 0000000..8fea037 Binary files /dev/null and b/public/cards/security/050-katie-nickels-back.png differ diff --git a/public/cards/security/050-katie-nickels-front.png b/public/cards/security/050-katie-nickels-front.png new file mode 100644 index 0000000..7e9b953 Binary files /dev/null and b/public/cards/security/050-katie-nickels-front.png differ diff --git a/scripts/security-pros.mjs b/scripts/security-pros.mjs index 50bdfde..59d2972 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 0000000..63aa105 --- /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 1ebc489..df8ef75 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; }
-
+
${ + portraitDataUri + ? `` + : `${emblem(accent, 150)}${esc( + pro.name.split(/\s+/).map((w) => w[0]).join('') + )}no freely-licensed photograph
of this person was found
` + }
${pad2(pro.number)}
${emblem(accent, 24)}SECURITY
PROFESSIONALS
${esc(pro.name)}
${alias}
${esc(pro.title)}
@@ -118,7 +131,7 @@ export function buildFront(pro, portraitDataUri) {
`; } -export function buildBack(pro) { +export function buildBack(pro, ref) { const accent = accentOf(pro); const rarity = String(pro.rarity).toUpperCase(); const domains = (pro.domains || []).slice(0, 5).map((d) => `
  • ${esc(d)}
  • `).join(''); @@ -167,6 +180,9 @@ export function buildBack(pro) { .foot { border-top:1px solid ${accent}55; padding-top:11px; color:#9a9ead; font-size:11px; letter-spacing:.3px; } .foot .r { display:flex; justify-content:space-between; align-items:center; } .foot b { color:#d7d9e2; } + /* CC BY and CC BY-SA reference photos oblige us to name the photographer, and + a conditioned portrait is a derivative work. */ + .credit { color:#7f8698; font-size:10px; letter-spacing:.2px; }
    @@ -189,6 +205,13 @@ export function buildBack(pro) {
    ERA ${esc(pro.era)}NATIONALITY ${esc(pro.nationality)}
    KNOWN FOR ${esc(pro.knownFor)}${lockGlyph(accent, 18)}
    + ${ + ref + ? `
    PORTRAIT AFTER A PHOTOGRAPH BY ${esc( + ref.credit + )}${ref.license && ref.license !== 'supplied' ? ` · ${esc(ref.license)}` : ''}
    ` + : '' + }
    `; } diff --git a/src/app/security-professionals/[slug]/page.tsx b/src/app/security-professionals/[slug]/page.tsx index edee9ba..5457ae0 100644 --- a/src/app/security-professionals/[slug]/page.tsx +++ b/src/app/security-professionals/[slug]/page.tsx @@ -1,7 +1,7 @@ import type { Metadata } from 'next'; import { notFound } from 'next/navigation'; import CardDetail from '@/components/CardDetail'; -import { pros, getPro, rarityLabel, statusLabel } from '@/data/security'; +import { pros, getPro, rarityLabel, statusLabel, getPortraitCredit } from '@/data/security'; import { site } from '@/data/site'; type Params = { params: Promise<{ slug: string }> }; @@ -94,6 +94,7 @@ export default async function ProPage({ params }: Params) { { label: 'Influence', value: p.influence }, ]} sources={p.sources} + portraitCredit={getPortraitCredit(p.slug)} front={p.front} back={p.back} prev={prev?.front ? { slug: prev.slug, name: prev.name, front: prev.front } : undefined} diff --git a/src/app/security-professionals/page.tsx b/src/app/security-professionals/page.tsx index 8874498..24db57b 100644 --- a/src/app/security-professionals/page.tsx +++ b/src/app/security-professionals/page.tsx @@ -42,6 +42,11 @@ const CRITERIA = [ head: 'Nobody appears twice', body: 'Series Two documents the people who broke things. This one documents the people whose job was to hold. A name in Hacking Legends cannot also be here.', }, + { + tag: 'Real faces', + head: 'No invented likenesses', + body: 'A portrait is painted from an identified photograph of that person, credited to the photographer. Where no properly licensed photo exists, the card carries no face. An earlier version of this set generated faces from text alone; those were fabrications and they were withdrawn.', + }, ]; export default function SecurityProfessionalsPage() { @@ -65,15 +70,11 @@ export default function SecurityProfessionalsPage() { line rather than crossing it.

    - {illustratedCount >= totalPlanned - ? `All ${totalPlanned} cards are illustrated. The set was built in public and the - roster stays open to argument — every stat line and scouting report is a pull - request away from changing.` - : `This set is being built in public. Copy is drafted, stats are in flux, and ${ - illustratedCount === 0 - ? 'not a single portrait has been illustrated yet' - : `${illustratedCount} of ${totalPlanned} cards are illustrated so far` - }. What you see below is the working roster.`} + Every portrait here is painted from a real, identified photograph of the person, + and the photographer is credited on the card. {totalPlanned - illustratedCount} of + the {totalPlanned} have no freely-licensed photograph we could find, so those cards + carry no face rather than an invented one. If you can point us at a properly + licensed photo of someone, that is the single most useful thing you can contribute.

    @@ -106,7 +107,7 @@ export default function SecurityProfessionalsPage() { {lockedCount}/{totalPlanned}
    - Artwork rendered + Portraits from a verified photo

    What ships next

      - {illustratedCount >= totalPlanned ? ( - <> -
    1. Take nominations and re-cut any slot the argument goes against.
    2. -
    3. Re-render any card whose copy or stat line changes in review.
    4. -
    5. Print run and packs, alongside the open-licensed downloads.
    6. - - ) : ( - <> -
    7. Take nominations on the {totalPlanned} names and settle the disputed slots.
    8. -
    9. Lock the stat lines and freeze the list.
    10. -
    11. Illustrate fronts and backs, same treatment as Series One and Two.
    12. -
    13. Card pages, print run and packs, alongside the open-licensed downloads.
    14. - - )} +
    15. + Find licensed photographs for the {totalPlanned - illustratedCount} cards with + no face. Pull requests welcome, and so are pointers. +
    16. +
    17. Take nominations and re-cut any slot the argument goes against.
    18. +
    19. Re-render any card whose copy or stat line changes in review.
    20. +
    21. Print run and packs, alongside the open-licensed downloads.
    diff --git a/src/components/CardDetail.tsx b/src/components/CardDetail.tsx index e6c13f8..872ed73 100644 --- a/src/components/CardDetail.tsx +++ b/src/components/CardDetail.tsx @@ -9,6 +9,18 @@ const CARD_VERSION = 'g1'; export type DetailStat = { label: string; value: number }; export type DetailSource = { label: string; url: string }; +/** + * The photograph a portrait was painted from. Most are CC BY or CC BY-SA, which + * oblige us to name the photographer — and a conditioned portrait is a derivative + * work, so the obligation travels with the card. + */ +export type DetailPortraitCredit = { + credit: string; + license: string; + licenseUrl?: string; + sourceUrl?: string; +}; + export type DetailNeighbour = { slug: string; name: string; front: string }; export type CardDetailProps = { @@ -38,6 +50,7 @@ export type CardDetailProps = { /** Secondary stat bars. Series Two prints four; Series One has none. */ stats?: DetailStat[]; sources?: DetailSource[]; + portraitCredit?: DetailPortraitCredit; statusLabel?: string; front: string; back: string; @@ -76,6 +89,7 @@ export default function CardDetail(props: CardDetailProps) { noteLabel = 'Collector’s note', stats, sources, + portraitCredit, statusLabel, front, back, @@ -229,6 +243,37 @@ export default function CardDetail(props: CardDetailProps) {
    )} + {portraitCredit && ( +
    +

    Portrait

    +

    + Painted from a photograph of {name} by{' '} + {portraitCredit.sourceUrl ? ( + + {portraitCredit.credit} + + ) : ( + portraitCredit.credit + )} + {portraitCredit.license === 'supplied' ? ( + ', supplied by the subject.' + ) : ( + <> + , licensed{' '} + {portraitCredit.licenseUrl ? ( + + {portraitCredit.license} + + ) : ( + portraitCredit.license + )} + . + + )} +

    +
    + )} + {sources && sources.length > 0 && (

    Sources

    diff --git a/src/data/security.ts b/src/data/security.ts index 4eed104..371f256 100644 --- a/src/data/security.ts +++ b/src/data/security.ts @@ -60,6 +60,8 @@ export const pros: Pro[] = [ 'With Martin Hellman, published “New Directions in Cryptography” in 1976 and showed that two parties who had never met could agree on a secret over a wire anyone could read. It broke a problem the field had considered permanent, and every TLS handshake since is a descendant. Shared the 2015 Turing Award for it, and spent decades afterwards arguing the civil-liberties side of the crypto policy fights.', note: 'The paper that made secure commerce on a public network conceivable at all.', status: 'locked', + front: '/cards/security/001-whitfield-diffie-front.png', + back: '/cards/security/001-whitfield-diffie-back.png', sources: [ { label: 'Whitfield Diffie', url: 'https://en.wikipedia.org/wiki/Whitfield_Diffie' }, { label: 'New Directions in Cryptography (1976)', url: 'https://ee.stanford.edu/~hellman/publications/24.pdf' }, @@ -85,6 +87,8 @@ export const pros: Pro[] = [ 'Co-author of the 1976 paper and Stanford professor who then refused to let the NSA classify academic cryptography out of existence, publishing and teaching through explicit pressure to stop. Also co-authored the early analysis of DES key length that argued, correctly and years early, that 56 bits was not enough. Shared the 2015 Turing Award with Diffie.', note: 'Won the maths, then won the argument about who is allowed to do the maths.', status: 'locked', + front: '/cards/security/002-martin-hellman-front.png', + back: '/cards/security/002-martin-hellman-back.png', sources: [ { label: 'Martin Hellman', url: 'https://en.wikipedia.org/wiki/Martin_Hellman' }, { label: 'ACM A.M. Turing Award 2015', url: 'https://amturing.acm.org/award_winners/hellman_4055781.cfm' }, @@ -109,6 +113,8 @@ export const pros: Pro[] = [ 'Co-invented the RSA cryptosystem at MIT in 1977 with Shamir and Adleman, turning public-key cryptography from a proposal into something you could ship. Went on to design MD2, MD4, MD5, RC4, RC5 and RC6, co-author the standard algorithms textbook, and spend his later career on verifiable election systems. Turing Award, 2002.', note: 'Half the acronyms in a 1990s protocol stack trace back to one office at MIT.', status: 'locked', + front: '/cards/security/003-ron-rivest-front.png', + back: '/cards/security/003-ron-rivest-back.png', sources: [ { label: 'Ron Rivest', url: 'https://en.wikipedia.org/wiki/Ron_Rivest' }, { label: 'ACM A.M. Turing Award 2002', url: 'https://amturing.acm.org/award_winners/rivest_1403005.cfm' }, @@ -133,6 +139,8 @@ export const pros: Pro[] = [ 'Co-invented RSA, then invented Shamir secret sharing, then co-developed differential cryptanalysis with Eli Biham — the technique that turned out to be the one the DES designers had quietly hardened against seventeen years earlier. Also a co-author of the cube attack and a long line of side-channel work. Turing Award, 2002.', note: 'Builds the lock and breaks the lock, usually in the same decade.', status: 'locked', + front: '/cards/security/004-adi-shamir-front.png', + back: '/cards/security/004-adi-shamir-back.png', sources: [ { label: 'Adi Shamir', url: 'https://en.wikipedia.org/wiki/Adi_Shamir' }, { label: 'Differential cryptanalysis', url: 'https://en.wikipedia.org/wiki/Differential_cryptanalysis' }, @@ -157,6 +165,8 @@ export const pros: Pro[] = [ 'The third name on the RSA paper, whose role was to keep breaking the schemes Rivest and Shamir proposed until one survived. Later coined the term “computer virus” for Fred Cohen’s 1983 experiments, and founded DNA computing with a molecular solution to the Hamiltonian path problem. Turing Award, 2002.', note: 'The designated attacker on the RSA team. The set needs more of those.', status: 'locked', + front: '/cards/security/005-leonard-adleman-front.png', + back: '/cards/security/005-leonard-adleman-back.png', sources: [ { label: 'Leonard Adleman', url: 'https://en.wikipedia.org/wiki/Leonard_Adleman' }, { label: 'ACM A.M. Turing Award 2002', url: 'https://amturing.acm.org/award_winners/adleman_7308544.cfm' }, @@ -181,6 +191,8 @@ export const pros: Pro[] = [ 'Released Pretty Good Privacy as free software in 1991, and it escaped onto the internet worldwide. The US government opened a three-year criminal investigation into him for munitions export, dropped it in 1996 without charges, and the crypto-export regime never recovered. He later co-founded Silent Circle and worked on ZRTP for encrypted voice.', note: 'The card for everyone who has ever typed a passphrase to read their own mail.', status: 'locked', + front: '/cards/security/006-phil-zimmermann-front.png', + back: '/cards/security/006-phil-zimmermann-back.png', sources: [ { label: 'Phil Zimmermann', url: 'https://en.wikipedia.org/wiki/Phil_Zimmermann' }, { label: 'Pretty Good Privacy', url: 'https://en.wikipedia.org/wiki/Pretty_Good_Privacy' }, @@ -205,6 +217,8 @@ export const pros: Pro[] = [ 'Wrote Applied Cryptography in 1994, the book that taught a generation of engineers how ciphers actually work, then designed Blowfish and co-designed Twofish. Spent the two decades after arguing the harder point: that security is a systems and incentives problem, not a maths problem. Coined “security theatre”, has written the Crypto-Gram newsletter since 1998, and helped report on the Snowden documents.', note: 'More people entered this field because of one book than any other single cause.', status: 'locked', + front: '/cards/security/007-bruce-schneier-front.png', + back: '/cards/security/007-bruce-schneier-back.png', sources: [ { label: 'Bruce Schneier', url: 'https://en.wikipedia.org/wiki/Bruce_Schneier' }, { label: 'Schneier on Security', url: 'https://www.schneier.com/' }, @@ -229,6 +243,8 @@ export const pros: Pro[] = [ 'Published the timing-attack paper in 1996 and differential power analysis in 1999, establishing that a correct implementation of a correct algorithm can still leak its key through how long it takes or how much current it draws. Co-authored the SSL 3.0 specification, founded Cryptography Research, and in 2018 was one of the co-discoverers of Spectre.', note: 'Twice made the whole industry re-audit hardware it had already shipped.', status: 'locked', + front: '/cards/security/008-paul-kocher-front.png', + back: '/cards/security/008-paul-kocher-back.png', sources: [ { label: 'Paul Kocher', url: 'https://en.wikipedia.org/wiki/Paul_Kocher' }, { label: 'Spectre (security vulnerability)', url: 'https://en.wikipedia.org/wiki/Spectre_(security_vulnerability)' }, @@ -254,6 +270,8 @@ export const pros: Pro[] = [ 'Published “An Intrusion-Detection Model” in 1987, the paper that framed detection as statistical deviation from a profile of normal behaviour and gave the entire monitoring industry its foundation. Also wrote Cryptography and Data Security, worked on database security and lattice-based information flow, and was one of the few academics engaging seriously with both sides of the Clipper chip debate.', note: 'Every alert queue in every SOC is downstream of one 1987 paper.', status: 'locked', + front: '/cards/security/009-dorothy-denning-front.png', + back: '/cards/security/009-dorothy-denning-back.png', sources: [ { label: 'Dorothy E. Denning', url: 'https://en.wikipedia.org/wiki/Dorothy_E._Denning' }, { label: 'An Intrusion-Detection Model (1987)', url: 'https://www.cs.colostate.edu/~cs656/reading/ieee-se-13-2.pdf' }, @@ -279,6 +297,8 @@ export const pros: Pro[] = [ 'Produced one of the first detailed technical analyses of the 1988 Morris worm while the internet was still smoking, co-wrote Practical UNIX and Internet Security with Simson Garfinkel, co-created Tripwire, and founded Purdue’s COAST lab and then CERIAS — for years the largest academic security centre in the US. Has advised more federal panels than most people have attended.', note: 'Trained a large fraction of the people on the rest of these cards.', status: 'locked', + front: '/cards/security/010-gene-spafford-front.png', + back: '/cards/security/010-gene-spafford-back.png', sources: [ { label: 'Gene Spafford', url: 'https://en.wikipedia.org/wiki/Gene_Spafford' }, { label: 'CERIAS, Purdue University', url: 'https://www.cerias.purdue.edu/' }, @@ -303,6 +323,8 @@ export const pros: Pro[] = [ 'Cambridge professor who wrote Security Engineering, the closest thing the field has to a canonical text, and gave away earlier editions free online. Founded the security-economics research programme with the observation that systems fail because the people who could fix them are not the people who bear the loss. Spent years demonstrating that bank chip-and-PIN systems were weaker than the banks told courts they were. Died in 2024.', note: 'If you only read one book on this list, it is this one.', status: 'locked', + front: '/cards/security/011-ross-anderson-front.png', + back: '/cards/security/011-ross-anderson-back.png', sources: [ { label: 'Ross J. Anderson', url: 'https://en.wikipedia.org/wiki/Ross_J._Anderson' }, { label: 'Security Engineering, third edition', url: 'https://www.cl.cam.ac.uk/~rja14/book.html' }, @@ -327,6 +349,8 @@ export const pros: Pro[] = [ 'Found the protocol flaw in the NSA’s Clipper chip escrow scheme in 1994 and published it, which did more to end mandated key escrow than any amount of lobbying. Later led state-commissioned source reviews of US voting systems, published work on the vulnerabilities of master-keyed physical locks and wiretap systems, and became a law-school professor arguing the same points to a different audience.', note: 'Has broken the government’s preferred backdoor design once already.', status: 'locked', + front: '/cards/security/012-matt-blaze-front.png', + back: '/cards/security/012-matt-blaze-back.png', sources: [ { label: 'Matt Blaze', url: 'https://en.wikipedia.org/wiki/Matt_Blaze' }, { label: 'Protocol Failure in the Escrowed Encryption Standard (1994)', url: 'https://www.mattblaze.org/papers/eesproto.pdf' }, @@ -351,6 +375,8 @@ export const pros: Pro[] = [ 'Invented the spanning-tree protocol that made large bridged Ethernets possible, and did the early work on routing protocols designed to keep functioning when a participating node is actively hostile rather than merely broken. Co-wrote Network Security: Private Communication in a Public World, and designed the “ephemerizer” approach to making data reliably expire.', note: 'Designed for the assumption that some of the network is lying to you.', status: 'locked', + front: '/cards/security/013-radia-perlman-front.png', + back: '/cards/security/013-radia-perlman-back.png', sources: [ { label: 'Radia Perlman', url: 'https://en.wikipedia.org/wiki/Radia_Perlman' }, { label: 'Internet Hall of Fame profile', url: 'https://www.internethalloffame.org/inductee/radia-perlman/' }, @@ -375,6 +401,8 @@ export const pros: Pro[] = [ 'Wrote the 1989 paper cataloguing the security problems in TCP/IP — sequence-number prediction, source routing, DNS spoofing — years before anyone was exploiting them at scale. Co-authored Firewalls and Internet Security with Cheswick, co-invented encrypted key exchange, was a Usenet co-creator, and served as chief technologist at the FTC and on the Privacy and Civil Liberties Oversight Board.', note: 'Published the attack list first and spent thirty years watching it come true.', status: 'locked', + front: '/cards/security/014-steven-bellovin-front.png', + back: '/cards/security/014-steven-bellovin-back.png', sources: [ { label: 'Steven M. Bellovin', url: 'https://en.wikipedia.org/wiki/Steven_M._Bellovin' }, { label: 'Security Problems in the TCP/IP Protocol Suite (1989)', url: 'https://www.cs.columbia.edu/~smb/papers/ipext.pdf' }, @@ -400,6 +428,8 @@ export const pros: Pro[] = [ 'Co-wrote the 1994 book that taught the industry what a firewall was and how to reason about a perimeter. “An Evening with Berferd” documented him feeding a live intruder a fake environment for months and writing down everything, which is the honeypot genre’s founding text. Later ran the Internet Mapping Project at Bell Labs.', note: 'Invented the practice of watching the attacker instead of just shutting the door.', status: 'locked', + front: '/cards/security/015-william-cheswick-front.png', + back: '/cards/security/015-william-cheswick-back.png', sources: [ { label: 'William Cheswick', url: 'https://en.wikipedia.org/wiki/William_Cheswick' }, { label: 'An Evening with Berferd (1992)', url: 'https://www.cheswick.com/ches/papers/berferd.pdf' }, @@ -424,6 +454,8 @@ export const pros: Pro[] = [ 'Built DEC SEAL, generally credited as the first commercial firewall product, then the TIS Firewall Toolkit and Gauntlet — the code most early internet perimeters were actually made of. Ran whitehouse.gov’s first email server. Has spent the decades since publicly arguing that most of what the industry sells does not work, which has aged better than the products did.', note: 'The perimeter, as an idea people could buy, is largely his fault and his credit.', status: 'locked', + front: '/cards/security/016-marcus-ranum-front.png', + back: '/cards/security/016-marcus-ranum-back.png', sources: [ { label: 'Marcus J. Ranum', url: 'https://en.wikipedia.org/wiki/Marcus_J._Ranum' }, { label: 'Firewall (computing) — history', url: 'https://en.wikipedia.org/wiki/Firewall_(computing)' }, @@ -448,6 +480,8 @@ export const pros: Pro[] = [ 'Wrote TCP Wrapper, which for years was the access control on a very large share of Unix hosts on the internet, and Postfix, a mail server written from the start around privilege separation. Co-wrote SATAN with Dan Farmer and later the Coroner’s Toolkit and Forensic Discovery, giving incident responders their first real open tooling.', note: 'Shipped the defence, then shipped the tools to work out how it was beaten.', status: 'locked', + front: '/cards/security/017-wietse-venema-front.png', + back: '/cards/security/017-wietse-venema-back.png', sources: [ { label: 'Wietse Venema', url: 'https://en.wikipedia.org/wiki/Wietse_Venema' }, { label: 'Postfix', url: 'https://www.postfix.org/' }, @@ -472,6 +506,8 @@ export const pros: Pro[] = [ 'Wrote COPS as a student, then in 1995 released SATAN with Wietse Venema — a scanner that audited a network the way an attacker would, published openly so defenders could run it first. The press predicted the end of the internet; SGI fired him over it; the security scanner became a permanent product category. Later co-wrote Forensic Discovery.', note: 'The original argument that defenders should get the attacker’s tools too.', status: 'locked', + front: '/cards/security/018-dan-farmer-front.png', + back: '/cards/security/018-dan-farmer-back.png', sources: [ { label: 'Dan Farmer', url: 'https://en.wikipedia.org/wiki/Dan_Farmer' }, { label: 'SATAN', url: 'https://en.wikipedia.org/wiki/Security_Administrator_Tool_for_Analyzing_Networks' }, @@ -497,6 +533,8 @@ export const pros: Pro[] = [ 'Released Nmap in Phrack in 1997 and has maintained it ever since, adding OS fingerprinting, service detection and the NSE scripting engine. Runs seclists.org, hosting the Bugtraq and Full Disclosure archives that are the field’s institutional memory, and fought MPAA and studio takedown attempts over the tool. Nmap is the first command most defenders learn.', note: 'Twenty-eight years of one maintainer. The reliability is the achievement.', status: 'locked', + front: '/cards/security/019-gordon-lyon-front.png', + back: '/cards/security/019-gordon-lyon-back.png', sources: [ { label: 'Gordon Lyon', url: 'https://en.wikipedia.org/wiki/Gordon_Lyon' }, { label: 'Nmap', url: 'https://nmap.org/' }, @@ -521,6 +559,8 @@ export const pros: Pro[] = [ 'Wrote Snort in 1998 as a lightweight packet sniffer, and it became the intrusion detection system that put IDS within reach of organisations that could never have bought one. Founded Sourcefire around it, which Cisco acquired in 2013. The Snort rule syntax outlived the product and is still how a large part of the industry writes network detections.', note: 'A weekend tool that became the de facto standard for network detection.', status: 'locked', + front: '/cards/security/020-martin-roesch-front.png', + back: '/cards/security/020-martin-roesch-back.png', sources: [ { label: 'Martin Roesch', url: 'https://en.wikipedia.org/wiki/Martin_Roesch' }, { label: 'Snort', url: 'https://www.snort.org/' }, @@ -545,6 +585,8 @@ export const pros: Pro[] = [ 'Started Ethereal in 1998 because commercial protocol analysers cost more than his employer would spend, renamed it Wireshark in 2006 after a trademark problem, and has shepherded it ever since through thousands of contributed protocol dissectors. It is the default answer to “what is actually on the wire” for network engineers and incident responders alike.', note: 'The tool you reach for when nobody can agree on what the traffic is doing.', status: 'locked', + front: '/cards/security/021-gerald-combs-front.png', + back: '/cards/security/021-gerald-combs-back.png', sources: [ { label: 'Wireshark', url: 'https://en.wikipedia.org/wiki/Wireshark' }, { label: 'Wireshark project', url: 'https://www.wireshark.org/about.html' }, @@ -569,6 +611,8 @@ export const pros: Pro[] = [ 'Released Nessus in 1998 as a free scanner with its own plugin language, at a point when the alternative was an expensive appliance. Co-founded Tenable Network Security around it in 2002 and took the product closed-source in 2005, a decision the community argued about for years. Vulnerability management as a routine operational practice largely grew out of that codebase.', note: 'The scan report that lands in an admin’s inbox every Monday started here.', status: 'locked', + front: '/cards/security/022-renaud-deraison-front.png', + back: '/cards/security/022-renaud-deraison-back.png', sources: [ { label: 'Nessus (software)', url: 'https://en.wikipedia.org/wiki/Nessus_(software)' }, { label: 'Tenable leadership', url: 'https://www.tenable.com/about-tenable/leadership' }, @@ -593,6 +637,8 @@ export const pros: Pro[] = [ 'Maintained BIND for years and wrote or co-wrote a long run of DNS RFCs, then built the response-policy zone mechanism that lets defenders block malicious domains at the resolver. Founded the first anti-spam DNSBL, ran the Internet Software Consortium, operates F-root, and co-founded Farsight Security around passive DNS. Internet Hall of Fame, 2014.', note: 'Ran the plumbing, then built the filters when the plumbing got abused.', status: 'locked', + front: '/cards/security/023-paul-vixie-front.png', + back: '/cards/security/023-paul-vixie-back.png', sources: [ { label: 'Paul Vixie', url: 'https://en.wikipedia.org/wiki/Paul_Vixie' }, { label: 'Internet Hall of Fame profile', url: 'https://www.internethalloffame.org/inductee/paul-vixie/' }, @@ -617,6 +663,8 @@ export const pros: Pro[] = [ 'Wrote honeyd, co-designed the bcrypt password hash with David Mazières, and created systrace and privilege separation work in OpenSSH. At Google he led the Safe Browsing malware research that put interstitial warnings in front of drive-by download sites, and co-authored the papers that measured how large that problem actually was.', note: 'bcrypt alone has protected more passwords than most companies have users.', status: 'locked', + front: '/cards/security/024-niels-provos-front.png', + back: '/cards/security/024-niels-provos-back.png', sources: [ { label: 'Niels Provos', url: 'https://en.wikipedia.org/wiki/Niels_Provos' }, { label: 'A Future-Adaptable Password Scheme (bcrypt, 1999)', url: 'https://www.usenix.org/legacy/events/usenix99/provos/provos.pdf' }, @@ -641,6 +689,8 @@ export const pros: Pro[] = [ 'Was an information security officer at Yahoo before founding WhiteHat Security in 2001, where continuous scanning of thousands of production sites produced the first credible public statistics on how long real web vulnerabilities actually stay open. Co-founded the Web Application Security Consortium, and co-authored XSS Attacks: Cross Site Scripting Exploits and Defense in 2007 — the book that turned cross-site scripting from a curiosity into a class of bug developers were expected to know.', note: 'The XSS book is still the reference. Web appsec became a discipline on his watch.', status: 'locked', + front: '/cards/security/025-jeremiah-grossman-front.png', + back: '/cards/security/025-jeremiah-grossman-back.png', sources: [ { label: 'XSS Attacks: Cross Site Scripting Exploits and Defense', url: 'https://books.google.com/books/about/XSS_Attacks.html?id=FKN5uL57tyAC' }, { label: 'Jeremiah Grossman’s blog', url: 'https://blog.jeremiahgrossman.com/' }, @@ -667,6 +717,8 @@ export const pros: Pro[] = [ 'Ran ha.ckers.org and published the XSS cheat sheet, which for years was the reference list of filter-evasion payloads that every web application firewall was tested against. Co-authored XSS Attacks with Grossman, released Slowloris to demonstrate that a single machine could hold a web server’s connections open indefinitely, and co-wrote Detecting Malice on behavioural detection.', note: 'The cheat sheet did more for input validation than any standards document.', status: 'locked', + front: '/cards/security/026-robert-hansen-front.png', + back: '/cards/security/026-robert-hansen-back.png', sources: [ { label: 'XSS Attacks: Cross Site Scripting Exploits and Defense', url: 'https://books.google.com/books/about/XSS_Attacks.html?id=FKN5uL57tyAC' }, { label: 'Slowloris (computer security)', url: 'https://en.wikipedia.org/wiki/Slowloris_(cyber_attack)' }, @@ -691,6 +743,8 @@ export const pros: Pro[] = [ 'Started the Open Web Application Security Project in September 2001 as a mailing list and a few documents, and it became the vendor-neutral body that produced the Top Ten, the ASVS, ZAP and the testing guides that appsec teams still work from. Later founded SourceClear on software composition analysis and has kept publicly pushing OWASP to stay relevant to modern supply-chain risk.', note: 'Built the commons the whole application security industry now cites.', status: 'locked', + front: '/cards/security/027-mark-curphey-front.png', + back: '/cards/security/027-mark-curphey-back.png', sources: [ { label: 'OWASP', url: 'https://en.wikipedia.org/wiki/OWASP' }, { label: 'Mark Curphey — OWASP Foundation', url: 'https://owasp.org/www-board-candidates/2022/mark_curphey_2022' }, @@ -715,6 +769,8 @@ export const pros: Pro[] = [ 'Wrote the first OWASP Top Ten and served as the volunteer chair of OWASP from 2003 to 2011, the years in which it went from a mailing list to the reference that PCI DSS and a long line of other standards cite by name. Co-founded Aspect Security and then Contrast Security, arguing for instrumentation inside the running application rather than scanning it from outside.', note: 'One list, and suddenly every auditor knew what SQL injection was.', status: 'locked', + front: '/cards/security/028-jeff-williams-front.png', + back: '/cards/security/028-jeff-williams-back.png', sources: [ { label: 'OWASP', url: 'https://en.wikipedia.org/wiki/OWASP' }, { label: 'OWASP Top Ten project', url: 'https://owasp.org/www-project-top-ten/' }, @@ -739,6 +795,8 @@ export const pros: Pro[] = [ 'Wrote Building Secure Software, Exploiting Software and Software Security, making the case that you cannot test defects out of a system you designed insecurely. Co-created BSIMM, which measures what security programmes actually do rather than what a standard says they should, and has run a long-standing interview series documenting the field’s own history.', note: 'Moved the argument from “scan it later” to “design it right”.', status: 'locked', + front: '/cards/security/029-gary-mcgraw-front.png', + back: '/cards/security/029-gary-mcgraw-back.png', sources: [ { label: 'Gary McGraw', url: 'https://en.wikipedia.org/wiki/Gary_McGraw' }, { label: 'BSIMM', url: 'https://www.bsimm.com/' }, @@ -763,6 +821,8 @@ export const pros: Pro[] = [ 'Co-wrote Writing Secure Code, which Bill Gates ordered Windows engineers to read during the 2002 Trustworthy Computing halt, and helped build the Security Development Lifecycle that came out of it — threat modelling, banned APIs, fuzzing and a final security review before ship. The SDL became the template most large software organisations copied.', note: 'Turned “be careful” into a checklist an entire company could be held to.', status: 'locked', + front: '/cards/security/030-michael-howard-front.png', + back: '/cards/security/030-michael-howard-back.png', sources: [ { label: 'Microsoft Security Development Lifecycle', url: 'https://en.wikipedia.org/wiki/Microsoft_Security_Development_Lifecycle' }, { label: 'Writing Secure Code, second edition', url: 'https://www.microsoftpressstore.com/store/writing-secure-code-9780735617223' }, @@ -787,6 +847,8 @@ export const pros: Pro[] = [ 'Drove threat modelling into shippable practice at Microsoft, including the Elevation of Privilege card game that got non-security engineers doing it voluntarily, then wrote Threat Modeling: Designing for Security and later Threats: What Every Engineer Should Learn. Served on the CVE editorial board and co-wrote The New School of Information Security on learning from breach data.', note: 'Four questions — what are we building, what can go wrong, what do we do, did we do a good job.', status: 'locked', + front: '/cards/security/031-adam-shostack-front.png', + back: '/cards/security/031-adam-shostack-back.png', sources: [ { label: 'Adam Shostack’s site', url: 'https://shostack.org/about/adam' }, { label: 'Elevation of Privilege threat modelling game', url: 'https://shostack.org/games/elevation-of-privilege' }, @@ -811,6 +873,8 @@ export const pros: Pro[] = [ 'Wrote ModSecurity, the open-source web application firewall that became the reference implementation and the base of the OWASP Core Rule Set. Then built SSL Labs, whose A-to-F grade for a server’s TLS configuration turned an obscure ops detail into something executives asked about, and wrote Bulletproof TLS and PKI as the manual for fixing the grade.', note: 'Made bad TLS visible and embarrassing, which fixed more of it than any RFC.', status: 'locked', + front: '/cards/security/032-ivan-ristic-front.png', + back: '/cards/security/032-ivan-ristic-back.png', sources: [ { label: 'Ivan Ristić', url: 'https://en.wikipedia.org/wiki/Ivan_Risti%C4%87' }, { label: 'Qualys SSL Labs', url: 'https://www.ssllabs.com/' }, @@ -835,6 +899,8 @@ export const pros: Pro[] = [ 'Co-wrote The Art of Software Security Assessment with John McDonald and Justin Schuh, still the most thorough published treatment of how to find memory-safety and logic flaws by reading code. Found deep bugs in Sendmail, OpenSSH and Flash while at ISS X-Force and IBM, and founded Azimuth Security. His work is why a generation of auditors know what to look for in a parser.', note: 'The book that turned code review from a chore into a craft.', status: 'locked', + front: '/cards/security/033-mark-dowd-front.png', + back: '/cards/security/033-mark-dowd-back.png', sources: [ { label: 'The Art of Software Security Assessment', url: 'https://www.informit.com/store/art-of-software-security-assessment-identifying-and-9780321444424' }, { label: 'Azimuth Security', url: 'https://www.azimuthsecurity.com/' }, @@ -860,6 +926,8 @@ export const pros: Pro[] = [ 'Built the graph-based binary diffing that made it routine to extract the vulnerability out of a vendor patch, founded zynamics around BinDiff and VxClass, and sold it to Google in 2011. Later worked in Project Zero and wrote the “weird machines” framing that treats exploitation as programming an unintended state machine inside the target.', note: 'Turned Patch Tuesday into a reliable source of exploit intelligence.', status: 'locked', + front: '/cards/security/034-halvar-flake-front.png', + back: '/cards/security/034-halvar-flake-back.png', sources: [ { label: 'BinDiff', url: 'https://www.zynamics.com/bindiff.html' }, { label: 'Thomas Dullien’s research blog', url: 'https://addxorrol.blogspot.com/' }, @@ -884,6 +952,8 @@ export const pros: Pro[] = [ 'Co-authored “Bypassing Browser Memory Protections”, the 2008 paper that showed how attackers were routing around DEP and ASLR, then at 25C3 the same year demonstrated a working rogue certificate authority built on MD5 collisions — which pushed the CA industry off MD5 for good. Co-founded Trail of Bits.', note: 'Demonstrated the theoretical break against a live CA, which is the only kind that moves anyone.', status: 'locked', + front: '/cards/security/035-alex-sotirov-front.png', + back: '/cards/security/035-alex-sotirov-back.png', sources: [ { label: 'MD5 considered harmful today (2008)', url: 'https://www.win.tue.nl/hashclash/rogue-ca/' }, { label: 'Bypassing Browser Memory Protections (2008)', url: 'https://www.blackhat.com/presentations/bh-usa-08/Sotirov_Dowd/bh08-sotirov-dowd.pdf' }, @@ -908,6 +978,8 @@ export const pros: Pro[] = [ 'Co-wrote the 1998 paper with Timothy Newsham showing that a network IDS which reassembles traffic differently from the host it protects can be walked straight past — the work that forced every vendor to rethink normalisation. Co-founded Matasano and later Latacora, and wrote the Cryptopals challenges that taught a generation how crypto actually breaks.', note: 'One paper made every IDS vendor rewrite their TCP stack.', status: 'locked', + front: '/cards/security/036-thomas-ptacek-front.png', + back: '/cards/security/036-thomas-ptacek-back.png', sources: [ { label: 'Insertion, Evasion, and Denial of Service (1998)', url: 'https://insecure.org/stf/secnet_ids/secnet_ids.html' }, { label: 'The Cryptopals crypto challenges', url: 'https://cryptopals.com/' }, @@ -932,6 +1004,8 @@ export const pros: Pro[] = [ 'Johns Hopkins cryptographer who co-led the Open Crypto Audit Project’s review of TrueCrypt, contributed to the analysis of Dual_EC_DRBG and the RSA BSAFE backdoor, and worked on the attacks behind Logjam and DROWN. His blog is the standard place the rest of the industry goes to find out how badly a new crypto story is being reported.', note: 'Does the audit, then does the translation so the rest of us can follow it.', status: 'locked', + front: '/cards/security/037-matthew-green-front.png', + back: '/cards/security/037-matthew-green-back.png', sources: [ { label: 'Matthew D. Green', url: 'https://en.wikipedia.org/wiki/Matthew_D._Green' }, { label: 'A Few Thoughts on Cryptographic Engineering', url: 'https://blog.cryptographyengineering.com/' }, @@ -956,6 +1030,8 @@ export const pros: Pro[] = [ 'Co-authored the cold boot attack paper showing DRAM retains keys after power off, then spent a career demonstrating — in court and in front of legislatures — that deployed voting machines could be reprogrammed. Co-founded the Internet Security Research Group behind Let’s Encrypt, and co-authored the ZMap internet-wide scanner and the Logjam and FREAK results.', note: 'Let’s Encrypt alone changed the default state of the web from plaintext to TLS.', status: 'locked', + front: '/cards/security/038-j-alex-halderman-front.png', + back: '/cards/security/038-j-alex-halderman-back.png', sources: [ { label: 'J. Alex Halderman', url: 'https://en.wikipedia.org/wiki/J._Alex_Halderman' }, { label: 'Lest We Remember: Cold Boot Attacks on Encryption Keys', url: 'https://citp.princeton.edu/our-work/memory/' }, @@ -980,6 +1056,8 @@ export const pros: Pro[] = [ 'Co-authored “Mining Your Ps and Qs”, which scanned the whole IPv4 internet and found that a meaningful share of TLS and SSH keys shared factors because embedded devices generated them without entropy — and then factored them. Followed it with Logjam, DROWN and FREAK, a body of work whose method is: measure what is actually deployed, then break the weak part.', note: 'Proved the crypto was fine and the random number generators were not.', status: 'locked', + front: '/cards/security/039-nadia-heninger-front.png', + back: '/cards/security/039-nadia-heninger-back.png', sources: [ { label: 'Nadia Heninger', url: 'https://en.wikipedia.org/wiki/Nadia_Heninger' }, { label: 'Mining Your Ps and Qs (USENIX Security 2012)', url: 'https://factorable.net/paper.html' }, @@ -1004,6 +1082,8 @@ export const pros: Pro[] = [ 'Built Have I Been Pwned in 2013 as a way for ordinary people to find out whether their account was in a dump, and it became infrastructure: browsers, password managers and the NIST-endorsed practice of checking passwords against known-breached lists all query it, using a k-anonymity scheme that never sends the password. Runs it as a public good rather than a product.', note: 'One person’s side project ended up inside the login flow of half the web.', status: 'locked', + front: '/cards/security/040-troy-hunt-front.png', + back: '/cards/security/040-troy-hunt-back.png', sources: [ { label: 'Troy Hunt', url: 'https://en.wikipedia.org/wiki/Troy_Hunt' }, { label: 'Have I Been Pwned', url: 'https://haveibeenpwned.com/' }, @@ -1028,6 +1108,8 @@ export const pros: Pro[] = [ 'Was security lead and signoff for Windows XP Service Pack 2 and Windows Server 2003, co-wrote the Threat Modeling book that came out of that era, then ran security at Mozilla for Firefox, at Apple on privacy and security strategy, at Fastly and Square, and became Intel’s first chief software security officer. Founded Thistle Technologies in 2020 to bring update infrastructure to embedded devices.', note: 'Has been the person accountable for a shipping product’s security more times than anyone.', status: 'locked', + front: '/cards/security/041-window-snyder-front.png', + back: '/cards/security/041-window-snyder-back.png', sources: [ { label: 'Window Snyder', url: 'https://en.wikipedia.org/wiki/Window_Snyder' }, { label: 'Thistle Technologies', url: 'https://thistle.tech/' }, @@ -1052,6 +1134,8 @@ export const pros: Pro[] = [ 'A founding member of Google’s security team who built and ran its incident response function, including the response to the 2009 Operation Aurora intrusion that Google chose to disclose publicly — a decision that reset industry norms on breach transparency. Co-authored Building Secure and Reliable Systems, and has served on US federal cybersecurity advisory bodies.', note: 'Ran the response that made public disclosure of a state intrusion normal.', status: 'locked', + front: '/cards/security/042-heather-adkins-front.png', + back: '/cards/security/042-heather-adkins-back.png', sources: [ { label: 'Building Secure and Reliable Systems', url: 'https://sre.google/books/building-secure-reliable-systems/' }, { label: 'Operation Aurora', url: 'https://en.wikipedia.org/wiki/Operation_Aurora' }, @@ -1076,6 +1160,8 @@ export const pros: Pro[] = [ 'Joined Google as a penetration tester, printed “Security Princess” on her business card, and rose to run Chrome. Led the multi-year project to mark plain HTTP as “Not secure” in the world’s most-used browser, which more than anything else moved the web to encryption by default, and pushed Project Zero’s 90-day disclosure deadline as an industry-wide forcing function.', note: 'Changed one string in a browser UI and moved the whole web to TLS.', status: 'locked', + front: '/cards/security/043-parisa-tabriz-front.png', + back: '/cards/security/043-parisa-tabriz-back.png', sources: [ { label: 'Parisa Tabriz', url: 'https://en.wikipedia.org/wiki/Parisa_Tabriz' }, { label: 'Chromium: marking HTTP as not secure', url: 'https://blog.chromium.org/2018/02/a-secure-web-is-here-to-stay.html' }, @@ -1100,6 +1186,8 @@ export const pros: Pro[] = [ 'Was CISO at Yahoo, where he objected to a government email-scanning programme, then CSO at Facebook through the investigation into foreign influence operations, leaving in 2018 over how much the company would disclose. Publicly challenged the NSA director on encryption backdoors, founded the Stanford Internet Observatory, and co-founded iSEC Partners and later Krebs Stamos Group.', note: 'The card about what the job costs when you do it honestly.', status: 'locked', + front: '/cards/security/044-alex-stamos-front.png', + back: '/cards/security/044-alex-stamos-back.png', sources: [ { label: 'Alex Stamos', url: 'https://en.wikipedia.org/wiki/Alex_Stamos' }, { label: 'Stanford Internet Observatory', url: 'https://cyber.fsi.stanford.edu/io' }, @@ -1124,6 +1212,8 @@ export const pros: Pro[] = [ 'Co-authored the 2003 CCIA report arguing that monoculture in operating systems was itself a national security risk, and was fired by @stake, then a Microsoft consultancy, the day it was published. Went on to serve as chief information security officer of In-Q-Tel and to give a run of Black Hat and USENIX keynotes that are still the most quoted long-form arguments about security policy the field has produced.', note: 'Paid for the paper with his job, and the paper was right.', status: 'locked', + front: '/cards/security/045-dan-geer-front.png', + back: '/cards/security/045-dan-geer-back.png', sources: [ { label: 'Dan Geer', url: 'https://en.wikipedia.org/wiki/Dan_Geer' }, { label: 'CyberInsecurity: The Cost of Monopoly (2003)', url: 'https://www.schneier.com/essays/archives/2003/09/cyberinsecurity_the.html' }, @@ -1148,6 +1238,8 @@ export const pros: Pro[] = [ 'Founded Mandiant in 2004 and built breach response into a business that governments and Fortune 100 boards call at 2am. In 2013 the company published APT1, a report attributing years of intrusions to a specific People’s Liberation Army unit with building photographs and operator handles — the moment public, named attribution became something private companies did.', note: 'Made attribution a commercial deliverable instead of a classified one.', status: 'locked', + front: '/cards/security/046-kevin-mandia-front.png', + back: '/cards/security/046-kevin-mandia-back.png', sources: [ { label: 'Mandiant', url: 'https://en.wikipedia.org/wiki/Mandiant' }, { label: 'APT1: Exposing One of China’s Cyber Espionage Units (2013)', url: 'https://www.mandiant.com/resources/reports/apt1-exposing-one-chinas-cyber-espionage-units' }, @@ -1172,6 +1264,8 @@ export const pros: Pro[] = [ 'Has been analysing malware at F-Secure since 1991, through the DOS virus era, Sasser and Blaster, Stuxnet and modern ransomware. Tracked down and interviewed the Pakistani brothers who wrote Brain, the first PC virus, twenty years after the fact. Formulated Hyppönen’s law — if it is smart, it is vulnerable — and is the researcher most often trusted to explain a live incident to the public.', note: 'The continuity card. One person watched the entire history of malware happen.', status: 'locked', + front: '/cards/security/047-mikko-hypponen-front.png', + back: '/cards/security/047-mikko-hypponen-back.png', sources: [ { label: 'Mikko Hyppönen', url: 'https://en.wikipedia.org/wiki/Mikko_Hypp%C3%B6nen' }, { label: 'Brain (computer virus)', url: 'https://en.wikipedia.org/wiki/Brain_(computer_virus)' }, @@ -1196,6 +1290,8 @@ export const pros: Pro[] = [ 'A former US Air Force cyber warfare officer who co-authored the analysis of the 2015 Ukrainian power grid attack and the later CRASHOVERRIDE and TRISIS malware, then founded Dragos to do industrial control system defence as a specialism rather than an IT afterthought. Co-wrote the SANS ICS courses that most OT defenders come through.', note: 'The set’s reminder that some networks fail into a physical consequence.', status: 'locked', + front: '/cards/security/048-robert-m-lee-front.png', + back: '/cards/security/048-robert-m-lee-back.png', sources: [ { label: 'Robert M. Lee — Dragos', url: 'https://www.dragos.com/leadership/robert-m-lee/' }, { label: 'Analysis of the Cyber Attack on the Ukrainian Power Grid (SANS/E-ISAC, 2016)', url: 'https://www.nerc.com/pa/CI/ESISAC/Documents/E-ISAC_SANS_Ukraine_DUC_18Mar2016.pdf' }, @@ -1221,6 +1317,8 @@ export const pros: Pro[] = [ 'Led incident response at Motorola Solutions and then at Dragos, running investigations inside live industrial networks where you cannot simply reimage the plant. Writes long-running public guidance on breaking into and surviving the profession, founded PancakesCon, and holds the SANS Lifetime Achievement Award for that work as much as the casework.', note: 'The mentorship is the contribution. Half a conference floor learned from those posts.', status: 'locked', + front: '/cards/security/049-lesley-carhart-front.png', + back: '/cards/security/049-lesley-carhart-back.png', sources: [ { label: 'Lesley Carhart', url: 'https://en.wikipedia.org/wiki/Lesley_Carhart' }, { label: 'Lesley Carhart’s blog', url: 'https://tisiphone.net/about/' }, @@ -1245,6 +1343,8 @@ export const pros: Pro[] = [ 'Was the threat intelligence lead for MITRE ATT&CK during the years it became the shared vocabulary defenders use to describe adversary behaviour, then went to Red Canary as director of intelligence. Teaches the SANS cyber threat intelligence course and spends most of her public work on the unglamorous question of how an intelligence report turns into a detection someone actually deploys.', note: 'ATT&CK gave the blue team a language. This is the card for the person who taught it.', status: 'locked', + front: '/cards/security/050-katie-nickels-front.png', + back: '/cards/security/050-katie-nickels-back.png', sources: [ { label: 'MITRE ATT&CK', url: 'https://attack.mitre.org/' }, { label: 'Katie Nickels — SANS Institute', url: 'https://www.sans.org/profiles/katie-nickels/' }, @@ -1264,3 +1364,27 @@ export const featuredPros = pros.filter((p) => p.status === 'locked').slice(0, 6 export function getPro(slug: string) { return pros.find((p) => p.slug === slug); } + +export type PortraitCredit = { + credit: string; + license: string; + licenseUrl?: string; + sourceUrl?: string; +}; + +type RefRow = { slug: string; ref?: (PortraitCredit & { url?: string }) | null }; + +/** + * Photographer credits for the portraits, written by scripts/security-refs.mjs. + * Every portrait in this set is painted from a real, identified photograph — most + * under CC BY or CC BY-SA, which require attribution, and a conditioned portrait + * is a derivative work. A card with no entry here has no face at all. + */ +import refs from '../../data/security-references.json' with { type: 'json' }; + +export function getPortraitCredit(slug: string): PortraitCredit | undefined { + const row = (refs as RefRow[]).find((r) => r.slug === slug); + if (!row?.ref) return undefined; + const { credit, license, licenseUrl, sourceUrl } = row.ref; + return { credit, license, licenseUrl, sourceUrl }; +}