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 index a984533..d904bce 100644 Binary files a/assets/portraits/security-pros/card_001.png and b/assets/portraits/security-pros/card_001.png differ diff --git a/assets/portraits/security-pros/card_002.png b/assets/portraits/security-pros/card_002.png index 33e3bf0..c80a362 100644 Binary files a/assets/portraits/security-pros/card_002.png and b/assets/portraits/security-pros/card_002.png differ diff --git a/assets/portraits/security-pros/card_003.png b/assets/portraits/security-pros/card_003.png index 99951f1..08b95b2 100644 Binary files a/assets/portraits/security-pros/card_003.png and b/assets/portraits/security-pros/card_003.png differ diff --git a/assets/portraits/security-pros/card_004.png b/assets/portraits/security-pros/card_004.png index 43e7367..08cedcd 100644 Binary files a/assets/portraits/security-pros/card_004.png and b/assets/portraits/security-pros/card_004.png differ diff --git a/assets/portraits/security-pros/card_005.png b/assets/portraits/security-pros/card_005.png index f470845..5a876b3 100644 Binary files a/assets/portraits/security-pros/card_005.png and b/assets/portraits/security-pros/card_005.png differ diff --git a/assets/portraits/security-pros/card_006.png b/assets/portraits/security-pros/card_006.png index 7ddc4c6..3684779 100644 Binary files a/assets/portraits/security-pros/card_006.png and b/assets/portraits/security-pros/card_006.png differ diff --git a/assets/portraits/security-pros/card_007.png b/assets/portraits/security-pros/card_007.png index d790e1f..8ed44fc 100644 Binary files a/assets/portraits/security-pros/card_007.png and b/assets/portraits/security-pros/card_007.png differ diff --git a/assets/portraits/security-pros/card_008.png b/assets/portraits/security-pros/card_008.png index d9ab256..b864b84 100644 Binary files a/assets/portraits/security-pros/card_008.png and b/assets/portraits/security-pros/card_008.png differ diff --git a/assets/portraits/security-pros/card_009.png b/assets/portraits/security-pros/card_009.png index debf88d..bdd32f1 100644 Binary files a/assets/portraits/security-pros/card_009.png and b/assets/portraits/security-pros/card_009.png differ diff --git a/assets/portraits/security-pros/card_010.png b/assets/portraits/security-pros/card_010.png index 795a602..cb59e4e 100644 Binary files a/assets/portraits/security-pros/card_010.png and b/assets/portraits/security-pros/card_010.png differ diff --git a/assets/portraits/security-pros/card_011.png b/assets/portraits/security-pros/card_011.png index e2787a2..17e688b 100644 Binary files a/assets/portraits/security-pros/card_011.png and b/assets/portraits/security-pros/card_011.png differ diff --git a/assets/portraits/security-pros/card_012.png b/assets/portraits/security-pros/card_012.png index 22054c3..6867f81 100644 Binary files a/assets/portraits/security-pros/card_012.png and b/assets/portraits/security-pros/card_012.png differ diff --git a/assets/portraits/security-pros/card_013.png b/assets/portraits/security-pros/card_013.png index 76ce6dc..a917e03 100644 Binary files a/assets/portraits/security-pros/card_013.png and b/assets/portraits/security-pros/card_013.png differ diff --git a/assets/portraits/security-pros/card_014.png b/assets/portraits/security-pros/card_014.png index 2770667..99796ce 100644 Binary files a/assets/portraits/security-pros/card_014.png and b/assets/portraits/security-pros/card_014.png differ diff --git a/assets/portraits/security-pros/card_015.png b/assets/portraits/security-pros/card_015.png index 584188f..d07daa8 100644 Binary files a/assets/portraits/security-pros/card_015.png and b/assets/portraits/security-pros/card_015.png differ diff --git a/assets/portraits/security-pros/card_016.png b/assets/portraits/security-pros/card_016.png index bca299f..780cd93 100644 Binary files a/assets/portraits/security-pros/card_016.png and b/assets/portraits/security-pros/card_016.png differ diff --git a/assets/portraits/security-pros/card_017.png b/assets/portraits/security-pros/card_017.png index 5d282fd..9043a34 100644 Binary files a/assets/portraits/security-pros/card_017.png and b/assets/portraits/security-pros/card_017.png differ diff --git a/assets/portraits/security-pros/card_018.png b/assets/portraits/security-pros/card_018.png index dbc2054..0bb698a 100644 Binary files a/assets/portraits/security-pros/card_018.png and b/assets/portraits/security-pros/card_018.png differ diff --git a/assets/portraits/security-pros/card_019.png b/assets/portraits/security-pros/card_019.png index 4a15018..e789c3c 100644 Binary files a/assets/portraits/security-pros/card_019.png and b/assets/portraits/security-pros/card_019.png differ diff --git a/assets/portraits/security-pros/card_020.png b/assets/portraits/security-pros/card_020.png index 8c946de..1af7f25 100644 Binary files a/assets/portraits/security-pros/card_020.png and b/assets/portraits/security-pros/card_020.png differ diff --git a/assets/portraits/security-pros/card_021.png b/assets/portraits/security-pros/card_021.png deleted file mode 100644 index 2735548..0000000 Binary files a/assets/portraits/security-pros/card_021.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_022.png b/assets/portraits/security-pros/card_022.png index 7b5ffa0..c49e22c 100644 Binary files a/assets/portraits/security-pros/card_022.png and b/assets/portraits/security-pros/card_022.png differ diff --git a/assets/portraits/security-pros/card_023.png b/assets/portraits/security-pros/card_023.png index d8867a9..920b10d 100644 Binary files a/assets/portraits/security-pros/card_023.png and b/assets/portraits/security-pros/card_023.png differ diff --git a/assets/portraits/security-pros/card_024.png b/assets/portraits/security-pros/card_024.png index c2edea6..8753f02 100644 Binary files a/assets/portraits/security-pros/card_024.png and b/assets/portraits/security-pros/card_024.png differ diff --git a/assets/portraits/security-pros/card_025.png b/assets/portraits/security-pros/card_025.png index 5d858e1..bb3a9ee 100644 Binary files a/assets/portraits/security-pros/card_025.png and b/assets/portraits/security-pros/card_025.png differ diff --git a/assets/portraits/security-pros/card_026.png b/assets/portraits/security-pros/card_026.png deleted file mode 100644 index 02d3847..0000000 Binary files a/assets/portraits/security-pros/card_026.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_027.png b/assets/portraits/security-pros/card_027.png index 1ced391..1d5a67f 100644 Binary files a/assets/portraits/security-pros/card_027.png and b/assets/portraits/security-pros/card_027.png differ diff --git a/assets/portraits/security-pros/card_028.png b/assets/portraits/security-pros/card_028.png deleted file mode 100644 index 73c6a6f..0000000 Binary files a/assets/portraits/security-pros/card_028.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_029.png b/assets/portraits/security-pros/card_029.png deleted file mode 100644 index 63011ec..0000000 Binary files a/assets/portraits/security-pros/card_029.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_030.png b/assets/portraits/security-pros/card_030.png deleted file mode 100644 index e076d03..0000000 Binary files a/assets/portraits/security-pros/card_030.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_031.png b/assets/portraits/security-pros/card_031.png index b798931..4864e7c 100644 Binary files a/assets/portraits/security-pros/card_031.png and b/assets/portraits/security-pros/card_031.png differ diff --git a/assets/portraits/security-pros/card_032.png b/assets/portraits/security-pros/card_032.png deleted file mode 100644 index ed0648a..0000000 Binary files a/assets/portraits/security-pros/card_032.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_033.png b/assets/portraits/security-pros/card_033.png deleted file mode 100644 index c28a487..0000000 Binary files a/assets/portraits/security-pros/card_033.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_034.png b/assets/portraits/security-pros/card_034.png deleted file mode 100644 index 2a92e9c..0000000 Binary files a/assets/portraits/security-pros/card_034.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_035.png b/assets/portraits/security-pros/card_035.png index eaf43ec..09c15e9 100644 Binary files a/assets/portraits/security-pros/card_035.png and b/assets/portraits/security-pros/card_035.png differ diff --git a/assets/portraits/security-pros/card_036.png b/assets/portraits/security-pros/card_036.png index d0b980e..73f4af3 100644 Binary files a/assets/portraits/security-pros/card_036.png and b/assets/portraits/security-pros/card_036.png differ diff --git a/assets/portraits/security-pros/card_037.png b/assets/portraits/security-pros/card_037.png index d93595f..258ef48 100644 Binary files a/assets/portraits/security-pros/card_037.png and b/assets/portraits/security-pros/card_037.png differ diff --git a/assets/portraits/security-pros/card_038.png b/assets/portraits/security-pros/card_038.png index 32d741c..8d966be 100644 Binary files a/assets/portraits/security-pros/card_038.png and b/assets/portraits/security-pros/card_038.png differ diff --git a/assets/portraits/security-pros/card_039.png b/assets/portraits/security-pros/card_039.png index 99709be..4dd76ef 100644 Binary files a/assets/portraits/security-pros/card_039.png and b/assets/portraits/security-pros/card_039.png differ diff --git a/assets/portraits/security-pros/card_040.png b/assets/portraits/security-pros/card_040.png index 8395c29..b886319 100644 Binary files a/assets/portraits/security-pros/card_040.png and b/assets/portraits/security-pros/card_040.png differ diff --git a/assets/portraits/security-pros/card_041.png b/assets/portraits/security-pros/card_041.png index f19528a..60ae166 100644 Binary files a/assets/portraits/security-pros/card_041.png and b/assets/portraits/security-pros/card_041.png differ diff --git a/assets/portraits/security-pros/card_042.png b/assets/portraits/security-pros/card_042.png deleted file mode 100644 index 47a8fa2..0000000 Binary files a/assets/portraits/security-pros/card_042.png and /dev/null differ diff --git a/assets/portraits/security-pros/card_043.png b/assets/portraits/security-pros/card_043.png index b4ddc87..667f780 100644 Binary files a/assets/portraits/security-pros/card_043.png and b/assets/portraits/security-pros/card_043.png differ diff --git a/assets/portraits/security-pros/card_044.png b/assets/portraits/security-pros/card_044.png index 4586780..d0f7ecc 100644 Binary files a/assets/portraits/security-pros/card_044.png and b/assets/portraits/security-pros/card_044.png differ diff --git a/assets/portraits/security-pros/card_045.png b/assets/portraits/security-pros/card_045.png index 54611ab..5bc5ddb 100644 Binary files a/assets/portraits/security-pros/card_045.png and b/assets/portraits/security-pros/card_045.png differ diff --git a/assets/portraits/security-pros/card_046.png b/assets/portraits/security-pros/card_046.png index b778eb1..e81e6d1 100644 Binary files a/assets/portraits/security-pros/card_046.png and b/assets/portraits/security-pros/card_046.png differ diff --git a/assets/portraits/security-pros/card_047.png b/assets/portraits/security-pros/card_047.png index 093ccf8..a2b7df9 100644 Binary files a/assets/portraits/security-pros/card_047.png and b/assets/portraits/security-pros/card_047.png differ diff --git a/assets/portraits/security-pros/card_048.png b/assets/portraits/security-pros/card_048.png index d804524..63cfaef 100644 Binary files a/assets/portraits/security-pros/card_048.png and b/assets/portraits/security-pros/card_048.png differ diff --git a/assets/portraits/security-pros/card_049.png b/assets/portraits/security-pros/card_049.png index bb4eb64..cfe561a 100644 Binary files a/assets/portraits/security-pros/card_049.png and b/assets/portraits/security-pros/card_049.png differ diff --git a/assets/portraits/security-pros/card_050.png b/assets/portraits/security-pros/card_050.png deleted file mode 100644 index d872803..0000000 Binary files a/assets/portraits/security-pros/card_050.png and /dev/null differ diff --git a/data/security-references.json b/data/security-references.json new file mode 100644 index 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 index 915e9f9..22db10a 100644 Binary files a/public/cards/security/001-whitfield-diffie-back.png and b/public/cards/security/001-whitfield-diffie-back.png differ diff --git a/public/cards/security/001-whitfield-diffie-front.png b/public/cards/security/001-whitfield-diffie-front.png index a94cd2a..456a390 100644 Binary files a/public/cards/security/001-whitfield-diffie-front.png and b/public/cards/security/001-whitfield-diffie-front.png differ diff --git a/public/cards/security/002-martin-hellman-back.png b/public/cards/security/002-martin-hellman-back.png index 13e8958..35c28d3 100644 Binary files a/public/cards/security/002-martin-hellman-back.png and b/public/cards/security/002-martin-hellman-back.png differ diff --git a/public/cards/security/002-martin-hellman-front.png b/public/cards/security/002-martin-hellman-front.png index 4c44c51..94fecf8 100644 Binary files a/public/cards/security/002-martin-hellman-front.png and b/public/cards/security/002-martin-hellman-front.png differ diff --git a/public/cards/security/003-ron-rivest-back.png b/public/cards/security/003-ron-rivest-back.png index 065bb24..8d8dc62 100644 Binary files a/public/cards/security/003-ron-rivest-back.png and b/public/cards/security/003-ron-rivest-back.png differ diff --git a/public/cards/security/003-ron-rivest-front.png b/public/cards/security/003-ron-rivest-front.png index 9e84ffd..d366c2c 100644 Binary files a/public/cards/security/003-ron-rivest-front.png and b/public/cards/security/003-ron-rivest-front.png differ diff --git a/public/cards/security/004-adi-shamir-back.png b/public/cards/security/004-adi-shamir-back.png index 5a55fd0..c316c6d 100644 Binary files a/public/cards/security/004-adi-shamir-back.png and b/public/cards/security/004-adi-shamir-back.png differ diff --git a/public/cards/security/004-adi-shamir-front.png b/public/cards/security/004-adi-shamir-front.png index 19d8ce3..6fa0c76 100644 Binary files a/public/cards/security/004-adi-shamir-front.png and b/public/cards/security/004-adi-shamir-front.png differ diff --git a/public/cards/security/005-leonard-adleman-back.png b/public/cards/security/005-leonard-adleman-back.png index ee6ecde..2aa1e27 100644 Binary files a/public/cards/security/005-leonard-adleman-back.png and b/public/cards/security/005-leonard-adleman-back.png differ diff --git a/public/cards/security/005-leonard-adleman-front.png b/public/cards/security/005-leonard-adleman-front.png index 1e72d2f..5667914 100644 Binary files a/public/cards/security/005-leonard-adleman-front.png and b/public/cards/security/005-leonard-adleman-front.png differ diff --git a/public/cards/security/006-phil-zimmermann-back.png b/public/cards/security/006-phil-zimmermann-back.png index 013dcb2..32cad92 100644 Binary files a/public/cards/security/006-phil-zimmermann-back.png and b/public/cards/security/006-phil-zimmermann-back.png differ diff --git a/public/cards/security/006-phil-zimmermann-front.png b/public/cards/security/006-phil-zimmermann-front.png index 88e40f7..b30f44d 100644 Binary files a/public/cards/security/006-phil-zimmermann-front.png and b/public/cards/security/006-phil-zimmermann-front.png differ diff --git a/public/cards/security/007-bruce-schneier-back.png b/public/cards/security/007-bruce-schneier-back.png index 0aa4626..665c466 100644 Binary files a/public/cards/security/007-bruce-schneier-back.png and b/public/cards/security/007-bruce-schneier-back.png differ diff --git a/public/cards/security/007-bruce-schneier-front.png b/public/cards/security/007-bruce-schneier-front.png index 09ee26d..8192b1d 100644 Binary files a/public/cards/security/007-bruce-schneier-front.png and b/public/cards/security/007-bruce-schneier-front.png differ diff --git a/public/cards/security/008-paul-kocher-back.png b/public/cards/security/008-paul-kocher-back.png index f59ff81..beee244 100644 Binary files a/public/cards/security/008-paul-kocher-back.png and b/public/cards/security/008-paul-kocher-back.png differ diff --git a/public/cards/security/008-paul-kocher-front.png b/public/cards/security/008-paul-kocher-front.png index 1ad6909..c2148af 100644 Binary files a/public/cards/security/008-paul-kocher-front.png and b/public/cards/security/008-paul-kocher-front.png differ diff --git a/public/cards/security/009-dorothy-denning-back.png b/public/cards/security/009-dorothy-denning-back.png index 316ee33..a6aed79 100644 Binary files a/public/cards/security/009-dorothy-denning-back.png and b/public/cards/security/009-dorothy-denning-back.png differ diff --git a/public/cards/security/009-dorothy-denning-front.png b/public/cards/security/009-dorothy-denning-front.png index 7cc90d9..4528550 100644 Binary files a/public/cards/security/009-dorothy-denning-front.png and b/public/cards/security/009-dorothy-denning-front.png differ diff --git a/public/cards/security/010-gene-spafford-back.png b/public/cards/security/010-gene-spafford-back.png index 0e7499e..2ccf828 100644 Binary files a/public/cards/security/010-gene-spafford-back.png and b/public/cards/security/010-gene-spafford-back.png differ diff --git a/public/cards/security/010-gene-spafford-front.png b/public/cards/security/010-gene-spafford-front.png index 53deabb..6aa9e79 100644 Binary files a/public/cards/security/010-gene-spafford-front.png and b/public/cards/security/010-gene-spafford-front.png differ diff --git a/public/cards/security/011-ross-anderson-back.png b/public/cards/security/011-ross-anderson-back.png index 5b828bd..90a8bbe 100644 Binary files a/public/cards/security/011-ross-anderson-back.png and b/public/cards/security/011-ross-anderson-back.png differ diff --git a/public/cards/security/011-ross-anderson-front.png b/public/cards/security/011-ross-anderson-front.png index 3e2d83e..69d034a 100644 Binary files a/public/cards/security/011-ross-anderson-front.png and b/public/cards/security/011-ross-anderson-front.png differ diff --git a/public/cards/security/012-matt-blaze-back.png b/public/cards/security/012-matt-blaze-back.png index f62ff8a..8d2e733 100644 Binary files a/public/cards/security/012-matt-blaze-back.png and b/public/cards/security/012-matt-blaze-back.png differ diff --git a/public/cards/security/012-matt-blaze-front.png b/public/cards/security/012-matt-blaze-front.png index 7a04bb5..513dbf4 100644 Binary files a/public/cards/security/012-matt-blaze-front.png and b/public/cards/security/012-matt-blaze-front.png differ diff --git a/public/cards/security/013-radia-perlman-back.png b/public/cards/security/013-radia-perlman-back.png index 1ec4b05..e382a9a 100644 Binary files a/public/cards/security/013-radia-perlman-back.png and b/public/cards/security/013-radia-perlman-back.png differ diff --git a/public/cards/security/013-radia-perlman-front.png b/public/cards/security/013-radia-perlman-front.png index 52e33b4..7b7052c 100644 Binary files a/public/cards/security/013-radia-perlman-front.png and b/public/cards/security/013-radia-perlman-front.png differ diff --git a/public/cards/security/014-steven-bellovin-back.png b/public/cards/security/014-steven-bellovin-back.png index 1b0a1bf..76121f3 100644 Binary files a/public/cards/security/014-steven-bellovin-back.png and b/public/cards/security/014-steven-bellovin-back.png differ diff --git a/public/cards/security/014-steven-bellovin-front.png b/public/cards/security/014-steven-bellovin-front.png index a6c48d4..ce3f9d8 100644 Binary files a/public/cards/security/014-steven-bellovin-front.png and b/public/cards/security/014-steven-bellovin-front.png differ diff --git a/public/cards/security/015-william-cheswick-back.png b/public/cards/security/015-william-cheswick-back.png index 1b2d5b6..c785144 100644 Binary files a/public/cards/security/015-william-cheswick-back.png and b/public/cards/security/015-william-cheswick-back.png differ diff --git a/public/cards/security/015-william-cheswick-front.png b/public/cards/security/015-william-cheswick-front.png index f96a23e..2880d76 100644 Binary files a/public/cards/security/015-william-cheswick-front.png and b/public/cards/security/015-william-cheswick-front.png differ diff --git a/public/cards/security/016-marcus-ranum-back.png b/public/cards/security/016-marcus-ranum-back.png index ce73574..3fc6923 100644 Binary files a/public/cards/security/016-marcus-ranum-back.png and b/public/cards/security/016-marcus-ranum-back.png differ diff --git a/public/cards/security/016-marcus-ranum-front.png b/public/cards/security/016-marcus-ranum-front.png index c877732..6bb4d11 100644 Binary files a/public/cards/security/016-marcus-ranum-front.png and b/public/cards/security/016-marcus-ranum-front.png differ diff --git a/public/cards/security/017-wietse-venema-back.png b/public/cards/security/017-wietse-venema-back.png index e667238..4eaabb4 100644 Binary files a/public/cards/security/017-wietse-venema-back.png and b/public/cards/security/017-wietse-venema-back.png differ diff --git a/public/cards/security/017-wietse-venema-front.png b/public/cards/security/017-wietse-venema-front.png index b40d480..52efeaf 100644 Binary files a/public/cards/security/017-wietse-venema-front.png and b/public/cards/security/017-wietse-venema-front.png differ diff --git a/public/cards/security/018-dan-farmer-back.png b/public/cards/security/018-dan-farmer-back.png index 5cb3d65..a5bbdae 100644 Binary files a/public/cards/security/018-dan-farmer-back.png and b/public/cards/security/018-dan-farmer-back.png differ diff --git a/public/cards/security/018-dan-farmer-front.png b/public/cards/security/018-dan-farmer-front.png index 473abf7..bb4bbbb 100644 Binary files a/public/cards/security/018-dan-farmer-front.png and b/public/cards/security/018-dan-farmer-front.png differ diff --git a/public/cards/security/019-gordon-lyon-back.png b/public/cards/security/019-gordon-lyon-back.png index cd2b3f9..6624303 100644 Binary files a/public/cards/security/019-gordon-lyon-back.png and b/public/cards/security/019-gordon-lyon-back.png differ diff --git a/public/cards/security/019-gordon-lyon-front.png b/public/cards/security/019-gordon-lyon-front.png index 9ec0efb..84437d4 100644 Binary files a/public/cards/security/019-gordon-lyon-front.png and b/public/cards/security/019-gordon-lyon-front.png differ diff --git a/public/cards/security/020-martin-roesch-back.png b/public/cards/security/020-martin-roesch-back.png index 02d4a65..3b38d35 100644 Binary files a/public/cards/security/020-martin-roesch-back.png and b/public/cards/security/020-martin-roesch-back.png differ diff --git a/public/cards/security/020-martin-roesch-front.png b/public/cards/security/020-martin-roesch-front.png index 8c832f0..d1ec09b 100644 Binary files a/public/cards/security/020-martin-roesch-front.png and b/public/cards/security/020-martin-roesch-front.png differ diff --git a/public/cards/security/021-gerald-combs-front.png b/public/cards/security/021-gerald-combs-front.png index e1a455e..65f8bd8 100644 Binary files a/public/cards/security/021-gerald-combs-front.png and b/public/cards/security/021-gerald-combs-front.png differ diff --git a/public/cards/security/022-renaud-deraison-back.png b/public/cards/security/022-renaud-deraison-back.png index 0217894..bb38908 100644 Binary files a/public/cards/security/022-renaud-deraison-back.png and b/public/cards/security/022-renaud-deraison-back.png differ diff --git a/public/cards/security/022-renaud-deraison-front.png b/public/cards/security/022-renaud-deraison-front.png index ce87913..15424ec 100644 Binary files a/public/cards/security/022-renaud-deraison-front.png and b/public/cards/security/022-renaud-deraison-front.png differ diff --git a/public/cards/security/023-paul-vixie-back.png b/public/cards/security/023-paul-vixie-back.png index e6c6456..f850fa4 100644 Binary files a/public/cards/security/023-paul-vixie-back.png and b/public/cards/security/023-paul-vixie-back.png differ diff --git a/public/cards/security/023-paul-vixie-front.png b/public/cards/security/023-paul-vixie-front.png index 821a620..1785230 100644 Binary files a/public/cards/security/023-paul-vixie-front.png and b/public/cards/security/023-paul-vixie-front.png differ diff --git a/public/cards/security/024-niels-provos-back.png b/public/cards/security/024-niels-provos-back.png index f89601b..593d746 100644 Binary files a/public/cards/security/024-niels-provos-back.png and b/public/cards/security/024-niels-provos-back.png differ diff --git a/public/cards/security/024-niels-provos-front.png b/public/cards/security/024-niels-provos-front.png index cf9a1aa..d713bc2 100644 Binary files a/public/cards/security/024-niels-provos-front.png and b/public/cards/security/024-niels-provos-front.png differ diff --git a/public/cards/security/025-jeremiah-grossman-back.png b/public/cards/security/025-jeremiah-grossman-back.png index d047bb6..667dd89 100644 Binary files a/public/cards/security/025-jeremiah-grossman-back.png and b/public/cards/security/025-jeremiah-grossman-back.png differ diff --git a/public/cards/security/025-jeremiah-grossman-front.png b/public/cards/security/025-jeremiah-grossman-front.png index ad6925a..a80bca1 100644 Binary files a/public/cards/security/025-jeremiah-grossman-front.png and b/public/cards/security/025-jeremiah-grossman-front.png differ diff --git a/public/cards/security/026-robert-hansen-front.png b/public/cards/security/026-robert-hansen-front.png index cb5823c..247ea7f 100644 Binary files a/public/cards/security/026-robert-hansen-front.png and b/public/cards/security/026-robert-hansen-front.png differ diff --git a/public/cards/security/027-mark-curphey-back.png b/public/cards/security/027-mark-curphey-back.png index bb236cb..2148450 100644 Binary files a/public/cards/security/027-mark-curphey-back.png and b/public/cards/security/027-mark-curphey-back.png differ diff --git a/public/cards/security/027-mark-curphey-front.png b/public/cards/security/027-mark-curphey-front.png index 11ec5d1..16ca9dc 100644 Binary files a/public/cards/security/027-mark-curphey-front.png and b/public/cards/security/027-mark-curphey-front.png differ diff --git a/public/cards/security/028-jeff-williams-front.png b/public/cards/security/028-jeff-williams-front.png index d5c7f86..9cf3ff4 100644 Binary files a/public/cards/security/028-jeff-williams-front.png and b/public/cards/security/028-jeff-williams-front.png differ diff --git a/public/cards/security/029-gary-mcgraw-front.png b/public/cards/security/029-gary-mcgraw-front.png index 0605914..e272c3a 100644 Binary files a/public/cards/security/029-gary-mcgraw-front.png and b/public/cards/security/029-gary-mcgraw-front.png differ diff --git a/public/cards/security/030-michael-howard-front.png b/public/cards/security/030-michael-howard-front.png index 8ecbb8d..b29227e 100644 Binary files a/public/cards/security/030-michael-howard-front.png and b/public/cards/security/030-michael-howard-front.png differ diff --git a/public/cards/security/031-adam-shostack-back.png b/public/cards/security/031-adam-shostack-back.png index 2fba100..a68f21a 100644 Binary files a/public/cards/security/031-adam-shostack-back.png and b/public/cards/security/031-adam-shostack-back.png differ diff --git a/public/cards/security/031-adam-shostack-front.png b/public/cards/security/031-adam-shostack-front.png index 2120988..c30a674 100644 Binary files a/public/cards/security/031-adam-shostack-front.png and b/public/cards/security/031-adam-shostack-front.png differ diff --git a/public/cards/security/032-ivan-ristic-front.png b/public/cards/security/032-ivan-ristic-front.png index 8b9f45d..42ebd24 100644 Binary files a/public/cards/security/032-ivan-ristic-front.png and b/public/cards/security/032-ivan-ristic-front.png differ diff --git a/public/cards/security/033-mark-dowd-front.png b/public/cards/security/033-mark-dowd-front.png index 8700220..d57f13c 100644 Binary files a/public/cards/security/033-mark-dowd-front.png and b/public/cards/security/033-mark-dowd-front.png differ diff --git a/public/cards/security/034-halvar-flake-front.png b/public/cards/security/034-halvar-flake-front.png index 86064ce..8caa1fe 100644 Binary files a/public/cards/security/034-halvar-flake-front.png and b/public/cards/security/034-halvar-flake-front.png differ diff --git a/public/cards/security/035-alex-sotirov-back.png b/public/cards/security/035-alex-sotirov-back.png index fd1cb3f..e6ca68a 100644 Binary files a/public/cards/security/035-alex-sotirov-back.png and b/public/cards/security/035-alex-sotirov-back.png differ diff --git a/public/cards/security/035-alex-sotirov-front.png b/public/cards/security/035-alex-sotirov-front.png index 94dba9a..b330006 100644 Binary files a/public/cards/security/035-alex-sotirov-front.png and b/public/cards/security/035-alex-sotirov-front.png differ diff --git a/public/cards/security/036-thomas-ptacek-back.png b/public/cards/security/036-thomas-ptacek-back.png index 8112421..887c6a6 100644 Binary files a/public/cards/security/036-thomas-ptacek-back.png and b/public/cards/security/036-thomas-ptacek-back.png differ diff --git a/public/cards/security/036-thomas-ptacek-front.png b/public/cards/security/036-thomas-ptacek-front.png index d1bed8f..80d67b8 100644 Binary files a/public/cards/security/036-thomas-ptacek-front.png and b/public/cards/security/036-thomas-ptacek-front.png differ diff --git a/public/cards/security/037-matthew-green-back.png b/public/cards/security/037-matthew-green-back.png index 605ad9a..6dd1e29 100644 Binary files a/public/cards/security/037-matthew-green-back.png and b/public/cards/security/037-matthew-green-back.png differ diff --git a/public/cards/security/037-matthew-green-front.png b/public/cards/security/037-matthew-green-front.png index a7a100c..270c2f0 100644 Binary files a/public/cards/security/037-matthew-green-front.png and b/public/cards/security/037-matthew-green-front.png differ diff --git a/public/cards/security/038-j-alex-halderman-back.png b/public/cards/security/038-j-alex-halderman-back.png index 68721d7..806661f 100644 Binary files a/public/cards/security/038-j-alex-halderman-back.png and b/public/cards/security/038-j-alex-halderman-back.png differ diff --git a/public/cards/security/038-j-alex-halderman-front.png b/public/cards/security/038-j-alex-halderman-front.png index 9074d70..47f6f07 100644 Binary files a/public/cards/security/038-j-alex-halderman-front.png and b/public/cards/security/038-j-alex-halderman-front.png differ diff --git a/public/cards/security/039-nadia-heninger-back.png b/public/cards/security/039-nadia-heninger-back.png index fb9eaa0..4cfd6c5 100644 Binary files a/public/cards/security/039-nadia-heninger-back.png and b/public/cards/security/039-nadia-heninger-back.png differ diff --git a/public/cards/security/039-nadia-heninger-front.png b/public/cards/security/039-nadia-heninger-front.png index 8a47094..7a9a407 100644 Binary files a/public/cards/security/039-nadia-heninger-front.png and b/public/cards/security/039-nadia-heninger-front.png differ diff --git a/public/cards/security/040-troy-hunt-back.png b/public/cards/security/040-troy-hunt-back.png index d24a191..d4b6cb5 100644 Binary files a/public/cards/security/040-troy-hunt-back.png and b/public/cards/security/040-troy-hunt-back.png differ diff --git a/public/cards/security/040-troy-hunt-front.png b/public/cards/security/040-troy-hunt-front.png index 5572fad..fc1558c 100644 Binary files a/public/cards/security/040-troy-hunt-front.png and b/public/cards/security/040-troy-hunt-front.png differ diff --git a/public/cards/security/041-window-snyder-back.png b/public/cards/security/041-window-snyder-back.png index 984b62d..93cf0f4 100644 Binary files a/public/cards/security/041-window-snyder-back.png and b/public/cards/security/041-window-snyder-back.png differ diff --git a/public/cards/security/041-window-snyder-front.png b/public/cards/security/041-window-snyder-front.png index 9d4d275..d0678bb 100644 Binary files a/public/cards/security/041-window-snyder-front.png and b/public/cards/security/041-window-snyder-front.png differ diff --git a/public/cards/security/042-heather-adkins-front.png b/public/cards/security/042-heather-adkins-front.png index 1a6b554..c5c04b2 100644 Binary files a/public/cards/security/042-heather-adkins-front.png and b/public/cards/security/042-heather-adkins-front.png differ diff --git a/public/cards/security/043-parisa-tabriz-back.png b/public/cards/security/043-parisa-tabriz-back.png index 1f8a070..fb941b3 100644 Binary files a/public/cards/security/043-parisa-tabriz-back.png and b/public/cards/security/043-parisa-tabriz-back.png differ diff --git a/public/cards/security/043-parisa-tabriz-front.png b/public/cards/security/043-parisa-tabriz-front.png index 950dff9..a139857 100644 Binary files a/public/cards/security/043-parisa-tabriz-front.png and b/public/cards/security/043-parisa-tabriz-front.png differ diff --git a/public/cards/security/044-alex-stamos-back.png b/public/cards/security/044-alex-stamos-back.png index 7357be2..a53d389 100644 Binary files a/public/cards/security/044-alex-stamos-back.png and b/public/cards/security/044-alex-stamos-back.png differ diff --git a/public/cards/security/044-alex-stamos-front.png b/public/cards/security/044-alex-stamos-front.png index 2b3c68e..e2a00a4 100644 Binary files a/public/cards/security/044-alex-stamos-front.png and b/public/cards/security/044-alex-stamos-front.png differ diff --git a/public/cards/security/045-dan-geer-back.png b/public/cards/security/045-dan-geer-back.png index 9afe5c4..f691a64 100644 Binary files a/public/cards/security/045-dan-geer-back.png and b/public/cards/security/045-dan-geer-back.png differ diff --git a/public/cards/security/045-dan-geer-front.png b/public/cards/security/045-dan-geer-front.png index 98f7dd9..21499e6 100644 Binary files a/public/cards/security/045-dan-geer-front.png and b/public/cards/security/045-dan-geer-front.png differ diff --git a/public/cards/security/046-kevin-mandia-back.png b/public/cards/security/046-kevin-mandia-back.png index 0d7eadf..5c95854 100644 Binary files a/public/cards/security/046-kevin-mandia-back.png and b/public/cards/security/046-kevin-mandia-back.png differ diff --git a/public/cards/security/046-kevin-mandia-front.png b/public/cards/security/046-kevin-mandia-front.png index e4e68ae..1fabb79 100644 Binary files a/public/cards/security/046-kevin-mandia-front.png and b/public/cards/security/046-kevin-mandia-front.png differ diff --git a/public/cards/security/047-mikko-hypponen-back.png b/public/cards/security/047-mikko-hypponen-back.png index 49aefc2..c3a6b91 100644 Binary files a/public/cards/security/047-mikko-hypponen-back.png and b/public/cards/security/047-mikko-hypponen-back.png differ diff --git a/public/cards/security/047-mikko-hypponen-front.png b/public/cards/security/047-mikko-hypponen-front.png index d2aa740..4ef3354 100644 Binary files a/public/cards/security/047-mikko-hypponen-front.png and b/public/cards/security/047-mikko-hypponen-front.png differ diff --git a/public/cards/security/048-robert-m-lee-back.png b/public/cards/security/048-robert-m-lee-back.png index cb39e91..5dc332b 100644 Binary files a/public/cards/security/048-robert-m-lee-back.png and b/public/cards/security/048-robert-m-lee-back.png differ diff --git a/public/cards/security/048-robert-m-lee-front.png b/public/cards/security/048-robert-m-lee-front.png index 71b9c74..a02063c 100644 Binary files a/public/cards/security/048-robert-m-lee-front.png and b/public/cards/security/048-robert-m-lee-front.png differ diff --git a/public/cards/security/049-lesley-carhart-back.png b/public/cards/security/049-lesley-carhart-back.png index 39ae333..ec156eb 100644 Binary files a/public/cards/security/049-lesley-carhart-back.png and b/public/cards/security/049-lesley-carhart-back.png differ diff --git a/public/cards/security/049-lesley-carhart-front.png b/public/cards/security/049-lesley-carhart-front.png index 62a29c6..6d91a98 100644 Binary files a/public/cards/security/049-lesley-carhart-front.png and b/public/cards/security/049-lesley-carhart-front.png differ diff --git a/public/cards/security/050-katie-nickels-front.png b/public/cards/security/050-katie-nickels-front.png index 118e8db..7e9b953 100644 Binary files a/public/cards/security/050-katie-nickels-front.png and b/public/cards/security/050-katie-nickels-front.png differ diff --git a/scripts/security-pros.mjs b/scripts/security-pros.mjs index 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; }