├── .github ├── dco.yml ├── CODEOWNERS ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── feature-request.md │ └── bug-report.md ├── workflows │ ├── lint.yml │ ├── release-drafter.yml │ └── release.yml ├── dependabot.yml └── release-drafter-config.yml ├── test ├── testdata │ ├── local-policy-real │ │ ├── .gitignore │ │ ├── mapping.yaml │ │ └── policy.rego │ ├── local-policy-param │ │ ├── .gitignore │ │ ├── mapping.yaml │ │ └── policy.rego │ ├── unsigned-image │ │ ├── oci-layout │ │ ├── blobs │ │ │ └── sha256 │ │ │ │ ├── 07d9a868932bd092fa0a4c4df943785a7ba9cee12dbf446d02488319a5fbf336 │ │ │ │ ├── 363133d587b90ff7a21f7b32a96be8422c6799683f0e1e6d71de5c03a82ab35e │ │ │ │ └── 7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390 │ │ └── index.json │ ├── unsigned-index │ │ ├── oci-layout │ │ ├── blobs │ │ │ └── sha256 │ │ │ │ ├── 97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad │ │ │ │ ├── 2953164d6cc6c8bb8271f78f9fb2003318350a8026ea082b63a249cfa60918a3 │ │ │ │ ├── 2a9b671f3fc9bc5ca967b616d96cbbdb6493e32d4f6abd8f7a191990e8efb289 │ │ │ │ ├── 3e64f9d2888ed9211fbf2c6b5853ea559248fdb4ab711bcea34b65c62f0e026b │ │ │ │ ├── 4e5988d06eee647cb901d4435830fbf13cf3ab1ae27ec91246b280514e6a7b33 │ │ │ │ ├── a4cf4b24f3fa8cd49a59e8fd4ef5ce285f0aa928d2651f7ec3d5a78276249dec │ │ │ │ ├── c9f436179969b60ec0bbd406b1340c501e59376a658b14b53c1828924c0ac668 │ │ │ │ ├── f634c4c53b03bf8ff917b61165631fda0cfe691a383e7b333269a53bf9a79c34 │ │ │ │ ├── fed2c8841731e2cf1ceb53c49c6440fcd6d565a8658141914a8a07c127e00d7e │ │ │ │ ├── 1c70b3e7c3a57801501ec127aa6c918c390c373294ec4fc48f2c6fe703fcc6fe │ │ │ │ ├── d85d624a324422194b43cccd975b5752cf0acaedd668bb525fcd40c3587cc460 │ │ │ │ ├── 7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e │ │ │ │ ├── da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620 │ │ │ │ ├── a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850 │ │ │ │ ├── da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e │ │ │ │ ├── 26da286bbc886aa14d191808db8fcbbd5d8ec68cf0047f954133e76d8e73d71c │ │ │ │ ├── 3883faf6acc3cae029364ed17ec2ce917fc9a500aab72f813d26fed8404e7162 │ │ │ │ ├── 6658b8ba1e1221a6288bf50cd7813f814e2baad70141a3e315b7c3476b0f476f │ │ │ │ ├── 8049aa9ad3479085066b31d02b74310803129c3eb1e22d2e62279f8c72340b55 │ │ │ │ ├── 9638ca53d2795806cf51b7461575c51e4a626a091dc2842b35cac18c787ff80f │ │ │ │ ├── aeca14119e3242c51633a899438518217417e01414d18189a3cf71c07f2a02c3 │ │ │ │ ├── e0a9b9404ac2691b9b1c9ef217f22bb1e106efd5ee791640411764e1cf39ea2c │ │ │ │ ├── f2b95cecafef9c22a5d059fac8f20e3645a45370e52abf9581dd4eedd152fce0 │ │ │ │ ├── 0f2ee9a338149a5a1435a7383582e5ef981b8a6bb7415d07d8d70c90d8cfd326 │ │ │ │ ├── 6c3da8eeaba64ce5acfcbaeb1f2c06af73879adba0fcb4743339c9a54b377635 │ │ │ │ ├── 98e06f6b48edd74e21e8504c5538aec56315874a6db860fbf6874cd7a830e3c8 │ │ │ │ └── db8f2a6e112ea6396f57d073269ecfac61e8dcdad3a4a643dcb577522492f898 │ │ └── index.json │ ├── missing-subject-layout │ │ ├── oci-layout │ │ ├── blobs │ │ │ └── sha256 │ │ │ │ ├── 5049b0fd8de6fc8937065a0b26214e5a1e620e98488de6bac72c0284b1a5242f │ │ │ │ ├── cbeaa84fb2fdfc8fd5e437555e94a323ac8acc69e68278d127cb4adf595f9d46 │ │ │ │ ├── e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec │ │ │ │ ├── a051db630f91aae4fc649b455724f2c5c60ae0c508e87d88937e862524f488b5 │ │ │ │ ├── e2c3b7df754e062b0c6b17c5262ea237fc86d68432e86e68724c57f04be3d064 │ │ │ │ └── bba371330d0124ce45f669c5d73092a3f2078ed1491e2bc52189a82e279074a1 │ │ └── index.json │ ├── no-provenance-index │ │ ├── oci-layout │ │ ├── blobs │ │ │ └── sha256 │ │ │ │ ├── 07d9a868932bd092fa0a4c4df943785a7ba9cee12dbf446d02488319a5fbf336 │ │ │ │ ├── 816b20ea86474dcfb2906ffaf4410262dfcb0d49fdfb60698775f7bc10aad7fb │ │ │ │ ├── bb0ed50656ccdb2eb114407de579554426777d6dc0e4206a6f746afb4ee5237e │ │ │ │ ├── 363133d587b90ff7a21f7b32a96be8422c6799683f0e1e6d71de5c03a82ab35e │ │ │ │ ├── c0bd7799c46e00830b4d7cb8c1f622d14aae81643a90be5ec38c9be4bdd70f6c │ │ │ │ ├── 52f7a760b9322aa1af76d998763868b7d1bfec2331a2574a438ef44c92c0c46d │ │ │ │ ├── 7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390 │ │ │ │ ├── 059eea09507d0f904b8892ee59fcd3ddec1a637fc40fb7c83c432c6ff27e2f91 │ │ │ │ ├── 0b1ee0f360b073d2f76ceed15a63e291659fbcc6c3caf3be39e437d8344b520e │ │ │ │ ├── 618f1e2f903648dde23cc38dc0ed7eed83d5394a6902bb7bfae8fa707c2e5c33 │ │ │ │ ├── f0dac65dd0ff6a656c419c654ac672c38029a3f1a4b4acce062bd2f5a923ffae │ │ │ │ └── 1e3839ac14fba8c5e4db574df2046ce21a9e012e4030305cea97ad3f07f81a4a │ │ └── index.json │ ├── containerd-subject-layout │ │ ├── oci-layout │ │ ├── blobs │ │ │ └── sha256 │ │ │ │ ├── 5049b0fd8de6fc8937065a0b26214e5a1e620e98488de6bac72c0284b1a5242f │ │ │ │ ├── cbeaa84fb2fdfc8fd5e437555e94a323ac8acc69e68278d127cb4adf595f9d46 │ │ │ │ ├── e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec │ │ │ │ ├── a051db630f91aae4fc649b455724f2c5c60ae0c508e87d88937e862524f488b5 │ │ │ │ ├── e2c3b7df754e062b0c6b17c5262ea237fc86d68432e86e68724c57f04be3d064 │ │ │ │ └── bba371330d0124ce45f669c5d73092a3f2078ed1491e2bc52189a82e279074a1 │ │ └── index.json │ ├── tuf │ │ ├── test-repo-oci │ │ │ ├── metadata │ │ │ │ ├── oci-layout │ │ │ │ ├── test-role │ │ │ │ │ ├── oci-layout │ │ │ │ │ ├── blobs │ │ │ │ │ │ └── sha256 │ │ │ │ │ │ │ ├── 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a │ │ │ │ │ │ │ ├── 84fd82cab3086626411db7936836bca343f3f2cb7a9b41846cbc42d6ff64da98 │ │ │ │ │ │ │ ├── e4f3fbc9692b9f500fecd97d33c58bd00e120cecbcdff2279f864dd2832c10e3 │ │ │ │ │ │ │ └── ad7b6cdc3c7c0af0f8f05459471074adb6353ff72e65e2ec2629fafcce1603b1 │ │ │ │ │ └── index.json │ │ │ │ ├── testing │ │ │ │ │ ├── oci-layout │ │ │ │ │ ├── blobs │ │ │ │ │ │ └── sha256 │ │ │ │ │ │ │ ├── 243420d72b0472394a29ad86a06a05f9b1f6270000ccabfeeba7680e8d27840b │ │ │ │ │ │ │ ├── a70a4b054774f728a66a22b05008b505573d850cc942552276a1faec79a6d6a5 │ │ │ │ │ │ │ └── f06ffb8527f121fa950570349ed57f77498ca4ac9a590fb15a0ec97a67a70ea6 │ │ │ │ │ └── index.json │ │ │ │ ├── blobs │ │ │ │ │ └── sha256 │ │ │ │ │ │ ├── 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a │ │ │ │ │ │ ├── 5556a0398a04564261ccc7b548d670792f2086c496322c4e95d898686e8b4811 │ │ │ │ │ │ ├── 640c0d21bbc7c99717feee6c74ff65e7099e4dc21a30f985f18d6e5bd205502d │ │ │ │ │ │ ├── e83d550964be04addfc119b45b8dd80914babd5e5f0529b3106d6f18f74afc3a │ │ │ │ │ │ ├── a00c1b266ea6b992a8b6fa87ab8a67232f4319d9e3dd0e63365e73114a2c7869 │ │ │ │ │ │ └── 5a9f60b64b708d05e4e4da0354529fc7fe5015807b79f0bf7b136207bf952bd7 │ │ │ │ └── index.json │ │ │ └── targets │ │ │ │ ├── testing │ │ │ │ ├── oci-layout │ │ │ │ ├── blobs │ │ │ │ │ └── sha256 │ │ │ │ │ │ ├── 0ee29b1aba4bf2259b76066c170aeeb923b2d96db3c46a8fe3d1475e9ccf320b │ │ │ │ │ │ ├── 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a │ │ │ │ │ │ ├── 9ecff174eabe9768063a2686be1ef45185c5932916e4e108f4f9fde20f6d3f97 │ │ │ │ │ │ ├── b3ed84cbb194e472b365c914d6551e2420167022e156409e10701c0ec9418b10 │ │ │ │ │ │ ├── f7c51ae1304af943cc66cf88fb043c1e463fe245793752c463312581dd4a1f9a │ │ │ │ │ │ ├── d3b20bd505b925e6b4b73dd875e9c5839e1797061049e243bdb0d70d62f6d090 │ │ │ │ │ │ ├── e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac │ │ │ │ │ │ ├── b846de84908dbf583e3b7e7fbd95cf2c5ffc3c0c92e19ef7be6859df3c5397a3 │ │ │ │ │ │ ├── 0d9f576776df40330e2f646eca34a51f4a092bd23409b19824ed36c1e8ed70ac │ │ │ │ │ │ ├── db3d6f0ce76f0fa388b83f4928620a7d532ab386a954dd997bdf9318aa5d0b79 │ │ │ │ │ │ ├── 39be48096573b49cb30ce5479d25c49a3405e8495daa9066e813e96338a17f48 │ │ │ │ │ │ └── 93a0c6a57652e182f3e04fed6e3bd0eedeb98c624af12668bc9e2741c7443374 │ │ │ │ └── index.json │ │ │ │ ├── test-role │ │ │ │ ├── oci-layout │ │ │ │ ├── blobs │ │ │ │ │ └── sha256 │ │ │ │ │ │ ├── 1691cdc848fa42fceb9f97f195c4e2372fba2cbe2984801f5296d26032d822b0 │ │ │ │ │ │ ├── 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a │ │ │ │ │ │ ├── d9941355ca037d7e878e04c1bc7cbf9c71a5d8035b6e27be0d9e5d9087599055 │ │ │ │ │ │ ├── d1bb6181284970ae43fbbc88b5e72f9a5942ebac20588aa0c4bf78ba621e1ee2 │ │ │ │ │ │ ├── bb8fcf06f6c067dcbcb394d7d9ced788316fc02b715fe679097281108a4bd465 │ │ │ │ │ │ ├── 46ad77c669b6b5b015e4b164ad66624d0c7704dfae8752e7844a632d8e3df640 │ │ │ │ │ │ └── f1558403107419b9a79ce371bba1425c123daf3f77437ba42c77b9dd0f26d6f8 │ │ │ │ └── index.json │ │ │ │ ├── 02119a076ec3878c736c3a95e20794f5a8d5bce3d7ecc264681bb7334ca2e24b.test.txt │ │ │ │ ├── oci-layout │ │ │ │ ├── blobs │ │ │ │ │ └── sha256 │ │ │ │ │ │ ├── 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a │ │ │ │ │ │ ├── cf0c754e6415fab25e2f59fb6b010dcf0c2369f7a59a45ff29c693c844163ca7 │ │ │ │ │ │ ├── 02119a076ec3878c736c3a95e20794f5a8d5bce3d7ecc264681bb7334ca2e24b │ │ │ │ │ │ └── 4b0cc6119d25a34299b24d86095f21f667378aadf3c493c2d92f134869fd2c73 │ │ │ │ └── index.json │ │ │ │ ├── baad1a9d61afa5d6f8717f576b57b9749e5549da4b826746fd73a5a914ac5be1.mapping.yaml │ │ │ │ ├── oci-layout │ │ │ │ ├── blobs │ │ │ │ │ └── sha256 │ │ │ │ │ │ ├── 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a │ │ │ │ │ │ ├── 518931eb24f93aa58c711c77e59d63171462133141ba9c6f8b6bc99a8daaab4d │ │ │ │ │ │ ├── baad1a9d61afa5d6f8717f576b57b9749e5549da4b826746fd73a5a914ac5be1 │ │ │ │ │ │ └── f6c752a7909493c7aaee73c51f174a2ca9b2edd2dc3868c8306b80b0e7f489e1 │ │ │ │ └── index.json │ │ │ │ ├── bc46e8c31646f166a9efbd14fef154dd84cf07efc95c96be3a201c84470dcbc1.jonnystoten2.rego │ │ │ │ ├── oci-layout │ │ │ │ ├── blobs │ │ │ │ │ └── sha256 │ │ │ │ │ │ ├── 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a │ │ │ │ │ │ ├── b3ed84cbb194e472b365c914d6551e2420167022e156409e10701c0ec9418b10 │ │ │ │ │ │ └── 39be48096573b49cb30ce5479d25c49a3405e8495daa9066e813e96338a17f48 │ │ │ │ └── index.json │ │ │ │ ├── e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac.always-fail.rego │ │ │ │ ├── oci-layout │ │ │ │ ├── blobs │ │ │ │ │ └── sha256 │ │ │ │ │ │ ├── 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a │ │ │ │ │ │ ├── 9ecff174eabe9768063a2686be1ef45185c5932916e4e108f4f9fde20f6d3f97 │ │ │ │ │ │ ├── e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac │ │ │ │ │ │ └── 0d9f576776df40330e2f646eca34a51f4a092bd23409b19824ed36c1e8ed70ac │ │ │ │ └── index.json │ │ │ │ └── bd6394a08afc1edfe5512fc14e63025a337e25ca0013c1068ec879742fc3a3c3.version-constraints │ │ │ │ ├── oci-layout │ │ │ │ ├── blobs │ │ │ │ └── sha256 │ │ │ │ │ ├── 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a │ │ │ │ │ ├── d8be98f75d88fafaf2195e64474570f79d918741cf0e90603304b4035e86200a │ │ │ │ │ ├── bd6394a08afc1edfe5512fc14e63025a337e25ca0013c1068ec879742fc3a3c3 │ │ │ │ │ └── b197e563dc2e6961628f2d9543da7555b50fdd78877ef34917d642a60e6bd73f │ │ │ │ └── index.json │ │ └── test-repo │ │ │ ├── targets │ │ │ ├── bd6394a08afc1edfe5512fc14e63025a337e25ca0013c1068ec879742fc3a3c3.version-constraints │ │ │ ├── 02119a076ec3878c736c3a95e20794f5a8d5bce3d7ecc264681bb7334ca2e24b.test.txt │ │ │ ├── test-role │ │ │ │ ├── d1bb6181284970ae43fbbc88b5e72f9a5942ebac20588aa0c4bf78ba621e1ee2.test.txt │ │ │ │ └── dir1 │ │ │ │ │ └── dir2 │ │ │ │ │ └── dir3 │ │ │ │ │ └── bb8fcf06f6c067dcbcb394d7d9ced788316fc02b715fe679097281108a4bd465.test.txt │ │ │ ├── baad1a9d61afa5d6f8717f576b57b9749e5549da4b826746fd73a5a914ac5be1.mapping.yaml │ │ │ ├── testing │ │ │ │ ├── d3b20bd505b925e6b4b73dd875e9c5839e1797061049e243bdb0d70d62f6d090.mapping.yaml │ │ │ │ ├── e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac.always-fail.rego │ │ │ │ └── 93a0c6a57652e182f3e04fed6e3bd0eedeb98c624af12668bc9e2741c7443374.test-only.rego │ │ │ └── e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac.always-fail.rego │ │ │ └── metadata │ │ │ ├── timestamp.json │ │ │ ├── 11.snapshot.json │ │ │ ├── 2.test-role.json │ │ │ ├── 2.testing.json │ │ │ └── 1.root.json │ ├── local-policy-no-policies │ │ └── mapping.yaml │ ├── local-policy-fail │ │ ├── mapping.yaml │ │ └── policy.rego │ ├── local-policy-no-tl │ │ ├── mapping.yaml │ │ └── policy.rego │ ├── local-policy-pass │ │ ├── mapping.yaml │ │ └── policy.rego │ ├── local-policy-mirror │ │ ├── mapping.yaml │ │ └── policy.rego │ ├── local-policy │ │ ├── mapping.yaml │ │ └── doi │ │ │ └── policy.rego │ └── local-policy-inputs │ │ ├── mapping.yaml │ │ └── doi │ │ └── policy.rego ├── README.md └── Dockerfile ├── tlog ├── README.md └── keys │ └── c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d.pem ├── policy ├── testdata │ └── policies │ │ ├── no-rego │ │ ├── doi │ │ │ └── policy.yaml │ │ └── mapping.yaml │ │ ├── allow │ │ ├── doi │ │ │ └── policy.rego │ │ └── mapping.yaml │ │ ├── deny │ │ ├── doi │ │ │ └── policy.rego │ │ └── mapping.yaml │ │ ├── test │ │ ├── fetch │ │ │ ├── fetch.rego │ │ │ └── fetch_test.rego │ │ ├── def_parse │ │ │ └── def_parse_test.rego │ │ └── git_checksum │ │ │ └── git_checksum_test.rego │ │ ├── allow-canonical │ │ ├── doi │ │ │ └── policy.rego │ │ └── mapping.yaml │ │ ├── verify-sig │ │ ├── mapping.yaml │ │ └── doi │ │ │ └── policy.rego │ │ ├── wrong-key │ │ ├── mapping.yaml │ │ └── doi │ │ │ └── policy.rego │ │ └── no-policy │ │ └── mapping.yaml ├── README.md ├── evaluator.go └── mock.go ├── tuf ├── README.md ├── example_registry_test.go ├── mock.go └── version_test.go ├── codecov.yml ├── mirror ├── README.md ├── mirror.go └── types.go ├── signerverifier ├── README.md ├── keyid.go ├── aws.go ├── gcp.go ├── parse.go ├── gcp_test.go └── common.go ├── oci ├── README.md ├── resolver.go ├── types_test.go ├── authn.go ├── authn_test.go └── registry.go ├── scripts ├── README.md └── gen-testdata.sh ├── attestation ├── README.md ├── resolver.go ├── attestation_test.go ├── types_test.go ├── verify_test.go ├── verifier_test.go ├── mock.go ├── registry_test.go └── vsa.go ├── internal ├── test │ └── test-signing-key.pem ├── util │ └── crypto.go ├── git │ └── git_test.go └── embed │ ├── root.go │ └── embedded-roots │ └── 1.root-dev.json ├── mapping ├── testdata │ └── mappings │ │ ├── doi │ │ └── mapping.yaml │ │ ├── local │ │ └── mapping.yaml │ │ ├── doi-platform-broken │ │ └── mapping.yaml │ │ ├── simple-rewrite │ │ └── mapping.yaml │ │ ├── rewrite-loop │ │ └── mapping.yaml │ │ ├── rewrite-to-local │ │ └── mapping.yaml │ │ ├── rewrite-to-no-match │ │ └── mapping.yaml │ │ ├── rewrite-invalid │ │ └── mapping.yaml │ │ ├── rewrite-multiple │ │ └── mapping.yaml │ │ └── doi-platform │ │ └── mapping.yaml └── types.go ├── .gitignore ├── NOTICE ├── template ├── bash.txt ├── makefile.txt ├── dockerfile.txt └── go.txt ├── .golangci.yaml ├── useragent ├── useragent_test.go └── useragent.go ├── sign.go ├── types.go ├── version └── version.go └── example_verify_test.go /.github/dco.yml: -------------------------------------------------------------------------------- 1 | require: 2 | members: false 3 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @docker/supply-chain-security 2 | -------------------------------------------------------------------------------- /test/testdata/local-policy-real/.gitignore: -------------------------------------------------------------------------------- 1 | config.yaml 2 | -------------------------------------------------------------------------------- /test/testdata/local-policy-param/.gitignore: -------------------------------------------------------------------------------- 1 | config.yaml 2 | -------------------------------------------------------------------------------- /test/testdata/unsigned-image/oci-layout: -------------------------------------------------------------------------------- 1 | {"imageLayoutVersion":"1.0.0"} -------------------------------------------------------------------------------- /test/testdata/unsigned-index/oci-layout: -------------------------------------------------------------------------------- 1 | {"imageLayoutVersion":"1.0.0"} -------------------------------------------------------------------------------- /tlog/README.md: -------------------------------------------------------------------------------- 1 | ## tlog 2 | This package implements transparency logging. -------------------------------------------------------------------------------- /test/testdata/missing-subject-layout/oci-layout: -------------------------------------------------------------------------------- 1 | {"imageLayoutVersion":"1.0.0"} -------------------------------------------------------------------------------- /test/testdata/no-provenance-index/oci-layout: -------------------------------------------------------------------------------- 1 | {"imageLayoutVersion":"1.0.0"} -------------------------------------------------------------------------------- /policy/testdata/policies/no-rego/doi/policy.yaml: -------------------------------------------------------------------------------- 1 | policy: "this is not rego" 2 | -------------------------------------------------------------------------------- /test/testdata/containerd-subject-layout/oci-layout: -------------------------------------------------------------------------------- 1 | {"imageLayoutVersion":"1.0.0"} -------------------------------------------------------------------------------- /policy/README.md: -------------------------------------------------------------------------------- 1 | ## policy 2 | This package is for attestation policy mapping and evaluation. -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | ## test 2 | This directory contains static `testdata` used to run go tests. -------------------------------------------------------------------------------- /tuf/README.md: -------------------------------------------------------------------------------- 1 | ## tuf 2 | This package implements TUF clients for http and oci data sources. -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "internal/test" 3 | coverage: 4 | status: 5 | patch: false 6 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/oci-layout: -------------------------------------------------------------------------------- 1 | { 2 | "imageLayoutVersion": "1.0.0" 3 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/testing/oci-layout: -------------------------------------------------------------------------------- 1 | { 2 | "imageLayoutVersion": "1.0.0" 3 | } -------------------------------------------------------------------------------- /mirror/README.md: -------------------------------------------------------------------------------- 1 | ## mirror 2 | This package contains components to mirror TUF metadata and targets to OCI. -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/test-role/oci-layout: -------------------------------------------------------------------------------- 1 | { 2 | "imageLayoutVersion": "1.0.0" 3 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/testing/oci-layout: -------------------------------------------------------------------------------- 1 | { 2 | "imageLayoutVersion": "1.0.0" 3 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/test-role/oci-layout: -------------------------------------------------------------------------------- 1 | { 2 | "imageLayoutVersion": "1.0.0" 3 | } -------------------------------------------------------------------------------- /test/testdata/local-policy-no-policies/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | rules: 5 | -------------------------------------------------------------------------------- /signerverifier/README.md: -------------------------------------------------------------------------------- 1 | ## signerverifier 2 | This package implements methods to sign and verify attestation envelopes. -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/testing/blobs/sha256/243420d72b0472394a29ad86a06a05f9b1f6270000ccabfeeba7680e8d27840b: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/testing/blobs/sha256/0ee29b1aba4bf2259b76066c170aeeb923b2d96db3c46a8fe3d1475e9ccf320b: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/testing/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/testing/blobs/sha256/9ecff174eabe9768063a2686be1ef45185c5932916e4e108f4f9fde20f6d3f97: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/testing/blobs/sha256/b3ed84cbb194e472b365c914d6551e2420167022e156409e10701c0ec9418b10: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/testing/blobs/sha256/f7c51ae1304af943cc66cf88fb043c1e463fe245793752c463312581dd4a1f9a: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/test-role/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/test-role/blobs/sha256/84fd82cab3086626411db7936836bca343f3f2cb7a9b41846cbc42d6ff64da98: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/test-role/blobs/sha256/1691cdc848fa42fceb9f97f195c4e2372fba2cbe2984801f5296d26032d822b0: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/test-role/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/test-role/blobs/sha256/d9941355ca037d7e878e04c1bc7cbf9c71a5d8035b6e27be0d9e5d9087599055: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /oci/README.md: -------------------------------------------------------------------------------- 1 | ## oci 2 | This package is for generic OCI components. For attestation specific components see the `attestation` package. -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo/targets/bd6394a08afc1edfe5512fc14e63025a337e25ca0013c1068ec879742fc3a3c3.version-constraints: -------------------------------------------------------------------------------- 1 | >= v0.1.4-0 2 | -------------------------------------------------------------------------------- /policy/testdata/policies/allow/doi/policy.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | result := { 6 | "success": true, 7 | } 8 | -------------------------------------------------------------------------------- /policy/testdata/policies/deny/doi/policy.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | result := { 6 | "success": false, 7 | } 8 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo/targets/02119a076ec3878c736c3a95e20794f5a8d5bce3d7ecc264681bb7334ca2e24b.test.txt: -------------------------------------------------------------------------------- 1 | this is a top-level target file -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo/targets/test-role/d1bb6181284970ae43fbbc88b5e72f9a5942ebac20588aa0c4bf78ba621e1ee2.test.txt: -------------------------------------------------------------------------------- 1 | this is a delegated targets file -------------------------------------------------------------------------------- /policy/testdata/policies/test/fetch/fetch.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | success if { 6 | some env in attest.fetch("foo") 7 | } 8 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | ## scripts 2 | This directory contains project scripts. 3 | 4 | `gen-testdata.sh` - used to generate static test data saved in `/test/testdata/` -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/test-role/blobs/sha256/d1bb6181284970ae43fbbc88b5e72f9a5942ebac20588aa0c4bf78ba621e1ee2: -------------------------------------------------------------------------------- 1 | this is a delegated targets file -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/02119a076ec3878c736c3a95e20794f5a8d5bce3d7ecc264681bb7334ca2e24b.test.txt/oci-layout: -------------------------------------------------------------------------------- 1 | { 2 | "imageLayoutVersion": "1.0.0" 3 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/baad1a9d61afa5d6f8717f576b57b9749e5549da4b826746fd73a5a914ac5be1.mapping.yaml/oci-layout: -------------------------------------------------------------------------------- 1 | { 2 | "imageLayoutVersion": "1.0.0" 3 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/test-role/blobs/sha256/bb8fcf06f6c067dcbcb394d7d9ced788316fc02b715fe679097281108a4bd465: -------------------------------------------------------------------------------- 1 | this is a deeply nested delegated targets file -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/bc46e8c31646f166a9efbd14fef154dd84cf07efc95c96be3a201c84470dcbc1.jonnystoten2.rego/oci-layout: -------------------------------------------------------------------------------- 1 | { 2 | "imageLayoutVersion": "1.0.0" 3 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac.always-fail.rego/oci-layout: -------------------------------------------------------------------------------- 1 | { 2 | "imageLayoutVersion": "1.0.0" 3 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/bd6394a08afc1edfe5512fc14e63025a337e25ca0013c1068ec879742fc3a3c3.version-constraints/oci-layout: -------------------------------------------------------------------------------- 1 | { 2 | "imageLayoutVersion": "1.0.0" 3 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo/targets/test-role/dir1/dir2/dir3/bb8fcf06f6c067dcbcb394d7d9ced788316fc02b715fe679097281108a4bd465.test.txt: -------------------------------------------------------------------------------- 1 | this is a deeply nested delegated targets file -------------------------------------------------------------------------------- /policy/testdata/policies/test/fetch/fetch_test.rego: -------------------------------------------------------------------------------- 1 | package attest_test 2 | 3 | import rego.v1 4 | 5 | import data.attest 6 | 7 | test_sucess if { 8 | attest.success 9 | } 10 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/02119a076ec3878c736c3a95e20794f5a8d5bce3d7ecc264681bb7334ca2e24b.test.txt/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/02119a076ec3878c736c3a95e20794f5a8d5bce3d7ecc264681bb7334ca2e24b.test.txt/blobs/sha256/cf0c754e6415fab25e2f59fb6b010dcf0c2369f7a59a45ff29c693c844163ca7: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/baad1a9d61afa5d6f8717f576b57b9749e5549da4b826746fd73a5a914ac5be1.mapping.yaml/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/baad1a9d61afa5d6f8717f576b57b9749e5549da4b826746fd73a5a914ac5be1.mapping.yaml/blobs/sha256/518931eb24f93aa58c711c77e59d63171462133141ba9c6f8b6bc99a8daaab4d: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/bc46e8c31646f166a9efbd14fef154dd84cf07efc95c96be3a201c84470dcbc1.jonnystoten2.rego/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/bc46e8c31646f166a9efbd14fef154dd84cf07efc95c96be3a201c84470dcbc1.jonnystoten2.rego/blobs/sha256/b3ed84cbb194e472b365c914d6551e2420167022e156409e10701c0ec9418b10: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac.always-fail.rego/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac.always-fail.rego/blobs/sha256/9ecff174eabe9768063a2686be1ef45185c5932916e4e108f4f9fde20f6d3f97: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/bd6394a08afc1edfe5512fc14e63025a337e25ca0013c1068ec879742fc3a3c3.version-constraints/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/bd6394a08afc1edfe5512fc14e63025a337e25ca0013c1068ec879742fc3a3c3.version-constraints/blobs/sha256/d8be98f75d88fafaf2195e64474570f79d918741cf0e90603304b4035e86200a: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/bd6394a08afc1edfe5512fc14e63025a337e25ca0013c1068ec879742fc3a3c3.version-constraints/blobs/sha256/bd6394a08afc1edfe5512fc14e63025a337e25ca0013c1068ec879742fc3a3c3: -------------------------------------------------------------------------------- 1 | >= v0.1.4-0 2 | -------------------------------------------------------------------------------- /attestation/README.md: -------------------------------------------------------------------------------- 1 | ## attestations 2 | This package is for components that deal with the creation, storage, and retrieval of signed attestions using OCI. 3 | 4 | For more generic OCI components see the `oci` package. -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/02119a076ec3878c736c3a95e20794f5a8d5bce3d7ecc264681bb7334ca2e24b.test.txt/blobs/sha256/02119a076ec3878c736c3a95e20794f5a8d5bce3d7ecc264681bb7334ca2e24b: -------------------------------------------------------------------------------- 1 | this is a top-level target file -------------------------------------------------------------------------------- /policy/testdata/policies/allow-canonical/doi/policy.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | default canonical = false 6 | 7 | canonical if { 8 | not input.tag 9 | } 10 | 11 | result := {"success": canonical} 12 | -------------------------------------------------------------------------------- /test/testdata/unsigned-image/blobs/sha256/07d9a868932bd092fa0a4c4df943785a7ba9cee12dbf446d02488319a5fbf336: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/attest/main/test/testdata/unsigned-image/blobs/sha256/07d9a868932bd092fa0a4c4df943785a7ba9cee12dbf446d02488319a5fbf336 -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/attest/main/test/testdata/unsigned-index/blobs/sha256/97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad -------------------------------------------------------------------------------- /test/testdata/no-provenance-index/blobs/sha256/07d9a868932bd092fa0a4c4df943785a7ba9cee12dbf446d02488319a5fbf336: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker/attest/main/test/testdata/no-provenance-index/blobs/sha256/07d9a868932bd092fa0a4c4df943785a7ba9cee12dbf446d02488319a5fbf336 -------------------------------------------------------------------------------- /tlog/keys/c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwr 3 | kBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw== 4 | -----END PUBLIC KEY----- 5 | -------------------------------------------------------------------------------- /test/testdata/no-provenance-index/blobs/sha256/816b20ea86474dcfb2906ffaf4410262dfcb0d49fdfb60698775f7bc10aad7fb: -------------------------------------------------------------------------------- 1 | {"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:f0dac65dd0ff6a656c419c654ac672c38029a3f1a4b4acce062bd2f5a923ffae"]}} -------------------------------------------------------------------------------- /test/testdata/no-provenance-index/blobs/sha256/bb0ed50656ccdb2eb114407de579554426777d6dc0e4206a6f746afb4ee5237e: -------------------------------------------------------------------------------- 1 | {"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:618f1e2f903648dde23cc38dc0ed7eed83d5394a6902bb7bfae8fa707c2e5c33"]}} -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | 3 | 4 | 5 | ### Tests 6 | 7 | 8 | 9 | ### Issue 10 | 11 | 12 | -------------------------------------------------------------------------------- /internal/test/test-signing-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIKZEqmmd++eAY3bmPoBdY6nC2wLy4da2yeVZNKCp6Oj2oAoGCCqGSM49 3 | AwEHoUQDQgAEZmicqYSY38DprGr42jU0V3ND0ROjzSRH1+yjsxhh0bi52Hh/DuOh 4 | rSq2KJ5a09lW3ybnDjljowbkof0Y1i9Oow== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /test/testdata/local-policy-fail/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: test-images 5 | description: Local test images 6 | files: 7 | - path: policy.rego 8 | rules: 9 | - pattern: ".*" 10 | policy-id: test-images 11 | -------------------------------------------------------------------------------- /test/testdata/local-policy-no-tl/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: test-images 5 | description: Local test images 6 | files: 7 | - path: policy.rego 8 | rules: 9 | - pattern: ".*" 10 | policy-id: test-images 11 | -------------------------------------------------------------------------------- /test/testdata/local-policy-pass/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: test-images 5 | description: Local test images 6 | files: 7 | - path: policy.rego 8 | rules: 9 | - pattern: ".*" 10 | policy-id: test-images 11 | -------------------------------------------------------------------------------- /test/testdata/missing-subject-layout/blobs/sha256/5049b0fd8de6fc8937065a0b26214e5a1e620e98488de6bac72c0284b1a5242f: -------------------------------------------------------------------------------- 1 | {"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:e2c3b7df754e062b0c6b17c5262ea237fc86d68432e86e68724c57f04be3d064"]}} -------------------------------------------------------------------------------- /test/testdata/containerd-subject-layout/blobs/sha256/5049b0fd8de6fc8937065a0b26214e5a1e620e98488de6bac72c0284b1a5242f: -------------------------------------------------------------------------------- 1 | {"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:e2c3b7df754e062b0c6b17c5262ea237fc86d68432e86e68724c57f04be3d064"]}} -------------------------------------------------------------------------------- /mapping/testdata/mappings/doi/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: docker-official-images 5 | description: Docker Official Images 6 | files: 7 | - path: doi/policy.rego 8 | rules: 9 | - pattern: "^docker[.]io/library/(.*)$" 10 | policy-id: docker-official-images 11 | -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/2953164d6cc6c8bb8271f78f9fb2003318350a8026ea082b63a249cfa60918a3: -------------------------------------------------------------------------------- 1 | {"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850","sha256:c6dd08ccc92ab60a87648a6b61fbf88d9287a936b285a8b4dde8893a1f4ffedf"]}} -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/2a9b671f3fc9bc5ca967b616d96cbbdb6493e32d4f6abd8f7a191990e8efb289: -------------------------------------------------------------------------------- 1 | {"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850","sha256:8f94b6e2a8be82e2e5b562d73212578bb3a02e8c0da7fc175c79045e73519375"]}} -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/3e64f9d2888ed9211fbf2c6b5853ea559248fdb4ab711bcea34b65c62f0e026b: -------------------------------------------------------------------------------- 1 | {"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850","sha256:8f2f55fc493890c2482a1220844157f4b0c8a6445d220af741e9fee8099bf532"]}} -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/4e5988d06eee647cb901d4435830fbf13cf3ab1ae27ec91246b280514e6a7b33: -------------------------------------------------------------------------------- 1 | {"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850","sha256:c01e5307ec84299048d76f162abec6f8bee4c463103161ab772c774e7ae9dd6d"]}} -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/a4cf4b24f3fa8cd49a59e8fd4ef5ce285f0aa928d2651f7ec3d5a78276249dec: -------------------------------------------------------------------------------- 1 | {"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e","sha256:371954672cfaa92735d6fbd70a787aac618a41d4c8ec8d6e12bd12d0cc601706"]}} -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/c9f436179969b60ec0bbd406b1340c501e59376a658b14b53c1828924c0ac668: -------------------------------------------------------------------------------- 1 | {"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e","sha256:5171425b78a2aedb43eb4e95083e64d3764c798507596ceded776c4ab038c224"]}} -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/f634c4c53b03bf8ff917b61165631fda0cfe691a383e7b333269a53bf9a79c34: -------------------------------------------------------------------------------- 1 | {"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e","sha256:9fe102c03d71d47a24cd7fc7db8e7affc05fd9bf98eb027038b7daf176861e85"]}} -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/fed2c8841731e2cf1ceb53c49c6440fcd6d565a8658141914a8a07c127e00d7e: -------------------------------------------------------------------------------- 1 | {"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e","sha256:92d3311aa91737ff81e2a4c8e269e78c3c95df611b44580426c384d3f5057776"]}} -------------------------------------------------------------------------------- /mapping/testdata/mappings/local/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: local-policy 5 | description: Local Policy 6 | files: 7 | - path: local-policy.rego 8 | rules: 9 | - pattern: "^docker[.]io/library/(.*)$" 10 | policy-id: docker-official-images # note this policy does not exist in this file 11 | -------------------------------------------------------------------------------- /policy/testdata/policies/deny/mapping.yaml: -------------------------------------------------------------------------------- 1 | # map repos to policies 2 | version: v1 3 | kind: policy-mapping 4 | policies: 5 | - id: docker-official-images 6 | description: Docker Official Images 7 | files: 8 | - path: doi/policy.rego 9 | rules: 10 | - pattern: "^docker[.]io/library/(.*)$" 11 | policy-id: docker-official-images 12 | -------------------------------------------------------------------------------- /policy/testdata/policies/no-rego/mapping.yaml: -------------------------------------------------------------------------------- 1 | # map repos to policies 2 | version: v1 3 | kind: policy-mapping 4 | policies: 5 | - id: docker-official-images 6 | description: Docker Official Images 7 | files: 8 | - path: doi/policy.yaml 9 | rules: 10 | - pattern: "^docker[.]io/library/(.*)$" 11 | policy-id: docker-official-images 12 | -------------------------------------------------------------------------------- /policy/testdata/policies/verify-sig/mapping.yaml: -------------------------------------------------------------------------------- 1 | # map repos to policies 2 | version: v1 3 | kind: policy-mapping 4 | policies: 5 | - id: docker-official-images 6 | description: Docker Official Images 7 | files: 8 | - path: doi/policy.rego 9 | rules: 10 | - pattern: "^docker[.]io/library/(.*)$" 11 | policy-id: docker-official-images 12 | -------------------------------------------------------------------------------- /policy/testdata/policies/wrong-key/mapping.yaml: -------------------------------------------------------------------------------- 1 | # map repos to policies 2 | version: v1 3 | kind: policy-mapping 4 | policies: 5 | - id: docker-official-images 6 | description: Docker Official Images 7 | files: 8 | - path: doi/policy.rego 9 | rules: 10 | - pattern: "^docker[.]io/library/(.*)$" 11 | policy-id: docker-official-images 12 | -------------------------------------------------------------------------------- /test/testdata/no-provenance-index/index.json: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:1e3839ac14fba8c5e4db574df2046ce21a9e012e4030305cea97ad3f07f81a4a","size":1607,"annotations":{"org.opencontainers.image.created":"2024-09-27T20:22:06Z","org.opencontainers.image.ref.name":"docker.io/library/test-image:test"}}]} -------------------------------------------------------------------------------- /policy/testdata/policies/no-policy/mapping.yaml: -------------------------------------------------------------------------------- 1 | # map repos to policies 2 | version: v1 3 | kind: policy-mapping 4 | rules: 5 | - pattern: "^docker[.]io/library/(.*)$" 6 | policy-id: docker-official-images 7 | - pattern: ^localhost:5001/(.*)$ 8 | rewrite: docker.io/library/$1 9 | - pattern: ^registry[.]local:5000/(.*)$ 10 | rewrite: docker.io/library/$1 11 | -------------------------------------------------------------------------------- /test/testdata/local-policy-mirror/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: test-images 5 | description: Local test images 6 | files: 7 | - path: policy.rego 8 | rules: 9 | - pattern: "^docker[.]io/library/test-image$" 10 | policy-id: test-images 11 | - pattern: "^mirror[.]org/library/(.*)$" 12 | rewrite: docker.io/library/$1 13 | -------------------------------------------------------------------------------- /mapping/testdata/mappings/doi-platform-broken/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: docker-official-images 5 | description: Docker Official Images 6 | files: 7 | - path: doi/policy.rego 8 | rules: 9 | - pattern: "^docker[.]io/library/(.*)$" 10 | platforms: ["linux/amd64/broken/platform/spec/1.0:foobar"] 11 | policy-id: docker-official-images 12 | -------------------------------------------------------------------------------- /test/testdata/missing-subject-layout/blobs/sha256/cbeaa84fb2fdfc8fd5e437555e94a323ac8acc69e68278d127cb4adf595f9d46: -------------------------------------------------------------------------------- 1 | {"architecture":"arm64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/","ArgsEscaped":true},"created":null,"history":[{"created_by":"CMD []","comment":"buildkit.dockerfile.v0","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":null}} -------------------------------------------------------------------------------- /test/testdata/unsigned-image/index.json: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390","size":476,"annotations":{"org.opencontainers.image.created":"2024-09-27T20:22:07Z","org.opencontainers.image.ref.name":"docker.io/library/test-image:test"},"platform":{"architecture":"amd64","os":"linux"}}]} -------------------------------------------------------------------------------- /test/testdata/containerd-subject-layout/blobs/sha256/cbeaa84fb2fdfc8fd5e437555e94a323ac8acc69e68278d127cb4adf595f9d46: -------------------------------------------------------------------------------- 1 | {"architecture":"arm64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/","ArgsEscaped":true},"created":null,"history":[{"created_by":"CMD []","comment":"buildkit.dockerfile.v0","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":null}} -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: enhancement 6 | assignees: "" 7 | --- 8 | 9 | **Describe the solution you'd like** 10 | [A clear and concise description of what you want to happen.] 11 | 12 | **Anything else you would like to add:** 13 | [Miscellaneous information that will assist in solving the issue.] 14 | -------------------------------------------------------------------------------- /mapping/testdata/mappings/simple-rewrite/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: docker-official-images 5 | description: Docker Official Images 6 | files: 7 | - path: doi/policy.rego 8 | rules: 9 | - pattern: "^docker[.]io/library/(.*)$" 10 | policy-id: docker-official-images 11 | - pattern: "^mycoolmirror[.]org/library/(.*)$" 12 | rewrite: "docker.io/library/$1" 13 | -------------------------------------------------------------------------------- /test/testdata/missing-subject-layout/blobs/sha256/e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:cbeaa84fb2fdfc8fd5e437555e94a323ac8acc69e68278d127cb4adf595f9d46", 7 | "size": 308 8 | }, 9 | "layers": null 10 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo/targets/baad1a9d61afa5d6f8717f576b57b9749e5549da4b826746fd73a5a914ac5be1.mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - origin: 5 | domain: docker.io 6 | prefix: jonnystoten2/ 7 | id: jonnystoten2 8 | description: jonnystoten2 personal images for testing 9 | attestations: 10 | style: "referrers" 11 | files: 12 | - path: jonnystoten2.rego 13 | -------------------------------------------------------------------------------- /test/testdata/containerd-subject-layout/blobs/sha256/e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:cbeaa84fb2fdfc8fd5e437555e94a323ac8acc69e68278d127cb4adf595f9d46", 7 | "size": 308 8 | }, 9 | "layers": null 10 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo/targets/testing/d3b20bd505b925e6b4b73dd875e9c5839e1797061049e243bdb0d70d62f6d090.mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - origin: 5 | domain: docker.io 6 | prefix: jonnystoten2/ 7 | id: jonnystoten2 8 | description: jonnystoten2 personal images for testing 9 | attestations: 10 | style: "referrers" 11 | files: 12 | - path: test-only.rego 13 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/testing/blobs/sha256/d3b20bd505b925e6b4b73dd875e9c5839e1797061049e243bdb0d70d62f6d090: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - origin: 5 | domain: docker.io 6 | prefix: jonnystoten2/ 7 | id: jonnystoten2 8 | description: jonnystoten2 personal images for testing 9 | attestations: 10 | style: "referrers" 11 | files: 12 | - path: test-only.rego 13 | -------------------------------------------------------------------------------- /mapping/testdata/mappings/rewrite-loop/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: docker-official-images 5 | description: Docker Official Images 6 | files: 7 | - path: doi/policy.rego 8 | rules: 9 | - pattern: "^docker[.]io/library/(.*)$" 10 | policy-id: docker-official-images 11 | - pattern: "^yin/(.*)$" 12 | rewrite: "yang/$1" 13 | - pattern: "^yang/(.*)$" 14 | rewrite: "yin/$1" 15 | -------------------------------------------------------------------------------- /mapping/testdata/mappings/rewrite-to-local/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: local-policy 5 | description: Local Policy 6 | files: 7 | - path: local-policy.rego 8 | rules: 9 | - pattern: "^docker[.]io/library/(.*)$" 10 | policy-id: docker-official-images # note this policy does not exist in this file 11 | - pattern: "^mycoolmirror[.]org/library/(.*)$" 12 | rewrite: "docker.io/library/$1" 13 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 7 | "size": 1608, 8 | "digest": "sha256:e83d550964be04addfc119b45b8dd80914babd5e5f0529b3106d6f18f74afc3a", 9 | "artifactType": "application/vnd.oci.empty.v1+json" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /mapping/testdata/mappings/rewrite-to-no-match/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: docker-official-images 5 | description: Docker Official Images 6 | files: 7 | - path: doi/policy.rego 8 | rules: 9 | - pattern: "^docker[.]io/library/(.*)$" 10 | policy-id: docker-official-images 11 | - pattern: "^mycoolmirror[.]org/library/(.*)$" 12 | rewrite: "badredirect.org/$1" # no matching rule for this rewrite 13 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/test-role/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 7 | "size": 444, 8 | "digest": "sha256:e4f3fbc9692b9f500fecd97d33c58bd00e120cecbcdff2279f864dd2832c10e3", 9 | "artifactType": "application/vnd.oci.empty.v1+json" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/testing/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 7 | "size": 442, 8 | "digest": "sha256:a70a4b054774f728a66a22b05008b505573d850cc942552276a1faec79a6d6a5", 9 | "artifactType": "application/vnd.oci.empty.v1+json" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /test/testdata/missing-subject-layout/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.index.v1+json", 7 | "digest": "sha256:bba371330d0124ce45f669c5d73092a3f2078ed1491e2bc52189a82e279074a1", 8 | "size": 855, 9 | "annotations": { 10 | "org.opencontainers.image.ref.name": "test" 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /test/testdata/local-policy-param/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: test-images 5 | description: Local test images 6 | files: 7 | - path: policy.rego 8 | - path: config.yaml #auto generated 9 | attestations: 10 | style: attached 11 | rules: 12 | - pattern: "^docker[.]io/library/test-image$" 13 | policy-id: test-images 14 | - pattern: "^mirror[.]org/library/(.*)$" 15 | rewrite: docker.io/library/$1 16 | -------------------------------------------------------------------------------- /test/testdata/local-policy-real/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: test-images 5 | description: Local test images 6 | files: 7 | - path: policy.rego 8 | - path: config.yaml #auto generated 9 | attestations: 10 | style: attached 11 | rules: 12 | - pattern: "^docker[.]io/library/test-image$" 13 | policy-id: test-images 14 | - pattern: "^mirror[.]org/library/(.*)$" 15 | rewrite: docker.io/library/$1 16 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/blobs/sha256/5556a0398a04564261ccc7b548d670792f2086c496322c4e95d898686e8b4811: -------------------------------------------------------------------------------- 1 | {"signatures":[{"keyid":"bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5","sig":"3045022042bb3075239d8d3676fe0990b9cfbb6c1629204d599d61e8805b5057cfecd20c022100da3e16fe5c2259c8a4847f3be8b5d8686f444cdffb2d94da83d71c9707b1cad3"}],"signed":{"_type":"timestamp","expires":"2034-09-07T14:41:18Z","meta":{"snapshot.json":{"version":11}},"spec_version":"1.0.31","version":11}} -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: bug 6 | assignees: "" 7 | --- 8 | 9 | **What steps did you take and what happened:** 10 | [A clear and concise description of what the bug is.] 11 | 12 | **What did you expect to happen:** 13 | 14 | **Anything else you would like to add:** 15 | [Miscellaneous information that will assist in solving the issue.] 16 | 17 | **Environment:** 18 | 19 | - Attest version: 20 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/baad1a9d61afa5d6f8717f576b57b9749e5549da4b826746fd73a5a914ac5be1.mapping.yaml/blobs/sha256/baad1a9d61afa5d6f8717f576b57b9749e5549da4b826746fd73a5a914ac5be1: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - origin: 5 | domain: docker.io 6 | prefix: jonnystoten2/ 7 | id: jonnystoten2 8 | description: jonnystoten2 personal images for testing 9 | attestations: 10 | style: "referrers" 11 | files: 12 | - path: jonnystoten2.rego 13 | -------------------------------------------------------------------------------- /mapping/testdata/mappings/rewrite-invalid/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: docker-official-images 5 | description: Docker Official Images 6 | files: 7 | - path: doi/policy.rego 8 | rules: 9 | - pattern: "^docker[.]io/library/(.*)$" 10 | policy-id: docker-official-images 11 | - pattern: "^mycoolmirror[.]org/library/(.*)$" 12 | rewrite: "docker.io/library/$1" 13 | policy-id: docker-official-images # invalid to specify both rewrite and policy-id 14 | -------------------------------------------------------------------------------- /test/testdata/unsigned-image/blobs/sha256/363133d587b90ff7a21f7b32a96be8422c6799683f0e1e6d71de5c03a82ab35e: -------------------------------------------------------------------------------- 1 | {"architecture":"amd64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/"},"created":"2024-09-27T16:10:13.292759474Z","history":[{"created":"2024-09-27T16:10:13.292759474Z","created_by":"COPY /tmp/hello.txt / # buildkit","comment":"buildkit.dockerfile.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:81a78ab7aa0b72d665a9c203b4c30f0423e434b789ed95b2d418e60a1b726470"]}} -------------------------------------------------------------------------------- /mapping/testdata/mappings/rewrite-multiple/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: docker-official-images 5 | description: Docker Official Images 6 | files: 7 | - path: doi/policy.rego 8 | rules: 9 | - pattern: "^docker[.]io/library/(.*)$" 10 | policy-id: docker-official-images 11 | - pattern: "^mycoolmirror[.]org/library/(.*)$" 12 | rewrite: "docker.io/library/$1" 13 | - pattern: "^myevencoolermirror[.]org/library/(.*)$" 14 | rewrite: "mycoolmirror.org/library/$1" 15 | -------------------------------------------------------------------------------- /test/testdata/no-provenance-index/blobs/sha256/363133d587b90ff7a21f7b32a96be8422c6799683f0e1e6d71de5c03a82ab35e: -------------------------------------------------------------------------------- 1 | {"architecture":"amd64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/"},"created":"2024-09-27T16:10:13.292759474Z","history":[{"created":"2024-09-27T16:10:13.292759474Z","created_by":"COPY /tmp/hello.txt / # buildkit","comment":"buildkit.dockerfile.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:81a78ab7aa0b72d665a9c203b4c30f0423e434b789ed95b2d418e60a1b726470"]}} -------------------------------------------------------------------------------- /test/testdata/no-provenance-index/blobs/sha256/c0bd7799c46e00830b4d7cb8c1f622d14aae81643a90be5ec38c9be4bdd70f6c: -------------------------------------------------------------------------------- 1 | {"architecture":"arm64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/"},"created":"2024-09-27T16:10:13.292759474Z","history":[{"created":"2024-09-27T16:10:13.292759474Z","created_by":"COPY /tmp/hello.txt / # buildkit","comment":"buildkit.dockerfile.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:81a78ab7aa0b72d665a9c203b4c30f0423e434b789ed95b2d418e60a1b726470"]}} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/02119a076ec3878c736c3a95e20794f5a8d5bce3d7ecc264681bb7334ca2e24b.test.txt/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 7 | "size": 493, 8 | "digest": "sha256:4b0cc6119d25a34299b24d86095f21f667378aadf3c493c2d92f134869fd2c73", 9 | "artifactType": "application/vnd.oci.empty.v1+json" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/baad1a9d61afa5d6f8717f576b57b9749e5549da4b826746fd73a5a914ac5be1.mapping.yaml/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 7 | "size": 498, 8 | "digest": "sha256:f6c752a7909493c7aaee73c51f174a2ca9b2edd2dc3868c8306b80b0e7f489e1", 9 | "artifactType": "application/vnd.oci.empty.v1+json" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/bc46e8c31646f166a9efbd14fef154dd84cf07efc95c96be3a201c84470dcbc1.jonnystoten2.rego/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 7 | "size": 504, 8 | "digest": "sha256:39be48096573b49cb30ce5479d25c49a3405e8495daa9066e813e96338a17f48", 9 | "artifactType": "application/vnd.oci.empty.v1+json" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac.always-fail.rego/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 7 | "size": 502, 8 | "digest": "sha256:0d9f576776df40330e2f646eca34a51f4a092bd23409b19824ed36c1e8ed70ac", 9 | "artifactType": "application/vnd.oci.empty.v1+json" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/bd6394a08afc1edfe5512fc14e63025a337e25ca0013c1068ec879742fc3a3c3.version-constraints/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 7 | "size": 504, 8 | "digest": "sha256:b197e563dc2e6961628f2d9543da7555b50fdd78877ef34917d642a60e6bd73f", 9 | "artifactType": "application/vnd.oci.empty.v1+json" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/1c70b3e7c3a57801501ec127aa6c918c390c373294ec4fc48f2c6fe703fcc6fe: -------------------------------------------------------------------------------- 1 | {"architecture":"amd64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/","OnBuild":null},"created":"2024-03-08T16:42:30.065465358Z","history":[{"created":"2024-03-08T16:42:30.065465358Z","created_by":"COPY /tmp/hello.txt / # buildkit","comment":"buildkit.dockerfile.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:b842af8c2f1451ffc802ae4139819eaea8441223357642548d8a25ab5c52cff7"]}} -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/d85d624a324422194b43cccd975b5752cf0acaedd668bb525fcd40c3587cc460: -------------------------------------------------------------------------------- 1 | {"architecture":"arm64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/","OnBuild":null},"created":"2024-03-08T16:42:30.065465358Z","history":[{"created":"2024-03-08T16:42:30.065465358Z","created_by":"COPY /tmp/hello.txt / # buildkit","comment":"buildkit.dockerfile.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:b842af8c2f1451ffc802ae4139819eaea8441223357642548d8a25ab5c52cff7"]}} -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint code 2 | on: 3 | pull_request: 4 | workflow_dispatch: 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Set git to use LF 10 | run: git config --global core.autocrlf false 11 | - uses: actions/checkout@v4 12 | - uses: actions/setup-go@v5 13 | with: 14 | go-version: 1.22.x 15 | - name: golangci-lint 16 | uses: golangci/golangci-lint-action@v6 17 | with: 18 | version: v1.59 19 | only-new-issues: true 20 | -------------------------------------------------------------------------------- /test/testdata/containerd-subject-layout/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.index.v1+json", 7 | "digest": "sha256:bba371330d0124ce45f669c5d73092a3f2078ed1491e2bc52189a82e279074a1", 8 | "size": 855, 9 | "annotations": { 10 | "io.containerd.image.name": "docker.io/library/test-image:test", 11 | "org.opencontainers.image.ref.name": "test" 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/testing/blobs/sha256/a70a4b054774f728a66a22b05008b505573d850cc942552276a1faec79a6d6a5: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","data":"e30="},"layers":[{"mediaType":"application/vnd.tuf.metadata+json","size":930,"digest":"sha256:f06ffb8527f121fa950570349ed57f77498ca4ac9a590fb15a0ec97a67a70ea6","annotations":{"tuf.io/filename":"2.testing.json"}}]} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/test-role/blobs/sha256/e4f3fbc9692b9f500fecd97d33c58bd00e120cecbcdff2279f864dd2832c10e3: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","data":"e30="},"layers":[{"mediaType":"application/vnd.tuf.metadata+json","size":742,"digest":"sha256:ad7b6cdc3c7c0af0f8f05459471074adb6353ff72e65e2ec2629fafcce1603b1","annotations":{"tuf.io/filename":"2.test-role.json"}}]} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo/targets/e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac.always-fail.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | violations contains { 6 | "type": "always_fail", 7 | "description": "This policy always fails", 8 | } 9 | 10 | result := { 11 | "success": false, 12 | "violations": violations, 13 | "summary": { 14 | "subjects": set(), 15 | "slsa_levels": ["SLSA_BUILD_LEVEL_3"], 16 | "verifier": "docker-official-images", 17 | "policy_uri": "https://docker.com/official/policy/v0.1", 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /test/testdata/unsigned-index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "manifests": [ 4 | { 5 | "mediaType": "application/vnd.oci.image.index.v1+json", 6 | "digest": "sha256:db8f2a6e112ea6396f57d073269ecfac61e8dcdad3a4a643dcb577522492f898", 7 | "size": 1607, 8 | "annotations": { 9 | "org.opencontainers.image.created": "2024-04-29T10:23:46Z", 10 | "org.opencontainers.image.ref.name": "docker.io/library/test-image:test" 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/testing/blobs/sha256/e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | violations contains { 6 | "type": "always_fail", 7 | "description": "This policy always fails", 8 | } 9 | 10 | result := { 11 | "success": false, 12 | "violations": violations, 13 | "summary": { 14 | "subjects": set(), 15 | "slsa_levels": ["SLSA_BUILD_LEVEL_3"], 16 | "verifier": "docker-official-images", 17 | "policy_uri": "https://docker.com/official/policy/v0.1", 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo/targets/testing/e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac.always-fail.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | violations contains { 6 | "type": "always_fail", 7 | "description": "This policy always fails", 8 | } 9 | 10 | result := { 11 | "success": false, 12 | "violations": violations, 13 | "summary": { 14 | "subjects": set(), 15 | "slsa_levels": ["SLSA_BUILD_LEVEL_3"], 16 | "verifier": "docker-official-images", 17 | "policy_uri": "https://docker.com/official/policy/v0.1", 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/blobs/sha256/640c0d21bbc7c99717feee6c74ff65e7099e4dc21a30f985f18d6e5bd205502d: -------------------------------------------------------------------------------- 1 | {"signatures":[{"keyid":"bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5","sig":"3046022100aeac20924d8a674836e298773a4bb728559cf0acfbae5b6bf1b9c8e29b1a1d1c022100a00c2d981a6ae8b530d213433946216604bcab34bb85435beed63a0e8b0f837c"}],"signed":{"_type":"snapshot","expires":"2034-09-07T14:41:18Z","meta":{"policy.json":{"version":1},"targets.json":{"version":11},"test-role.json":{"version":2},"testing.json":{"version":2}},"spec_version":"1.0.31","version":11}} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo/metadata/timestamp.json: -------------------------------------------------------------------------------- 1 | { 2 | "signatures": [ 3 | { 4 | "keyid": "bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5", 5 | "sig": "3045022042bb3075239d8d3676fe0990b9cfbb6c1629204d599d61e8805b5057cfecd20c022100da3e16fe5c2259c8a4847f3be8b5d8686f444cdffb2d94da83d71c9707b1cad3" 6 | } 7 | ], 8 | "signed": { 9 | "_type": "timestamp", 10 | "expires": "2034-09-07T14:41:18Z", 11 | "meta": { 12 | "snapshot.json": { 13 | "version": 11 14 | } 15 | }, 16 | "spec_version": "1.0.31", 17 | "version": 11 18 | } 19 | } -------------------------------------------------------------------------------- /policy/testdata/policies/allow/mapping.yaml: -------------------------------------------------------------------------------- 1 | # map repos to policies 2 | version: v1 3 | kind: policy-mapping 4 | policies: 5 | - id: docker-official-images 6 | description: Docker Official Images 7 | attestations: 8 | repo: "localhost:5001/library-refs" 9 | files: 10 | - path: doi/policy.rego 11 | rules: 12 | - pattern: "^docker[.]io/library/(.*)$" 13 | policy-id: docker-official-images 14 | - pattern: ^localhost:5001/(.*)$ 15 | rewrite: docker.io/library/$1 16 | - pattern: ^registry[.]local:5000/(.*)$ 17 | rewrite: docker.io/library/$1 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | .aider* 23 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Docker attest 2 | Copyright Docker attest authors 3 | 4 | This product includes software developed at Docker, Inc. (https://www.docker.com). 5 | 6 | The following is courtesy of our legal counsel: 7 | 8 | Use and transfer of Docker may be subject to certain restrictions by the 9 | United States and other governments. 10 | It is your responsibility to ensure that your use and/or transfer does not 11 | violate applicable laws. 12 | 13 | For more information, please see https://www.bis.doc.gov 14 | 15 | See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. 16 | -------------------------------------------------------------------------------- /policy/testdata/policies/allow-canonical/mapping.yaml: -------------------------------------------------------------------------------- 1 | # map repos to policies 2 | version: v1 3 | kind: policy-mapping 4 | policies: 5 | - id: docker-official-images 6 | description: Docker Official Images 7 | attestations: 8 | repo: "localhost:5001/library-refs" 9 | files: 10 | - path: doi/policy.rego 11 | rules: 12 | - pattern: "^docker[.]io/library/(.*)$" 13 | policy-id: docker-official-images 14 | - pattern: ^localhost:5001/(.*)$ 15 | rewrite: docker.io/library/$1 16 | - pattern: ^registry[.]local:5000/(.*)$ 17 | rewrite: docker.io/library/$1 18 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/test-role/blobs/sha256/46ad77c669b6b5b015e4b164ad66624d0c7704dfae8752e7844a632d8e3df640: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","data":"e30="},"layers":[{"mediaType":"application/vnd.tuf.target","size":46,"digest":"sha256:bb8fcf06f6c067dcbcb394d7d9ced788316fc02b715fe679097281108a4bd465","annotations":{"tuf.io/filename":"bb8fcf06f6c067dcbcb394d7d9ced788316fc02b715fe679097281108a4bd465.test.txt"}}]} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/test-role/blobs/sha256/f1558403107419b9a79ce371bba1425c123daf3f77437ba42c77b9dd0f26d6f8: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","data":"e30="},"layers":[{"mediaType":"application/vnd.tuf.target","size":32,"digest":"sha256:d1bb6181284970ae43fbbc88b5e72f9a5942ebac20588aa0c4bf78ba621e1ee2","annotations":{"tuf.io/filename":"d1bb6181284970ae43fbbc88b5e72f9a5942ebac20588aa0c4bf78ba621e1ee2.test.txt"}}]} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/testing/blobs/sha256/b846de84908dbf583e3b7e7fbd95cf2c5ffc3c0c92e19ef7be6859df3c5397a3: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","data":"e30="},"layers":[{"mediaType":"application/vnd.tuf.target","size":269,"digest":"sha256:d3b20bd505b925e6b4b73dd875e9c5839e1797061049e243bdb0d70d62f6d090","annotations":{"tuf.io/filename":"d3b20bd505b925e6b4b73dd875e9c5839e1797061049e243bdb0d70d62f6d090.mapping.yaml"}}]} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/testing/blobs/sha256/0d9f576776df40330e2f646eca34a51f4a092bd23409b19824ed36c1e8ed70ac: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","data":"e30="},"layers":[{"mediaType":"application/vnd.tuf.target","size":364,"digest":"sha256:e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac","annotations":{"tuf.io/filename":"e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac.always-fail.rego"}}]} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/testing/blobs/sha256/db3d6f0ce76f0fa388b83f4928620a7d532ab386a954dd997bdf9318aa5d0b79: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","data":"e30="},"layers":[{"mediaType":"application/vnd.tuf.target","size":467,"digest":"sha256:93a0c6a57652e182f3e04fed6e3bd0eedeb98c624af12668bc9e2741c7443374","annotations":{"tuf.io/filename":"93a0c6a57652e182f3e04fed6e3bd0eedeb98c624af12668bc9e2741c7443374.test-only.rego"}}]} -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | types: [opened, reopened, synchronize] 8 | permissions: 9 | contents: read 10 | jobs: 11 | update_release_draft: 12 | permissions: 13 | contents: write 14 | pull-requests: write 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: release-drafter/release-drafter@v6 18 | with: 19 | config-name: release-drafter-config.yml 20 | publish: false 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/testing/blobs/sha256/39be48096573b49cb30ce5479d25c49a3405e8495daa9066e813e96338a17f48: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","data":"e30="},"layers":[{"mediaType":"application/vnd.tuf.target","size":5857,"digest":"sha256:bc46e8c31646f166a9efbd14fef154dd84cf07efc95c96be3a201c84470dcbc1","annotations":{"tuf.io/filename":"bc46e8c31646f166a9efbd14fef154dd84cf07efc95c96be3a201c84470dcbc1.jonnystoten2.rego"}}]} -------------------------------------------------------------------------------- /test/testdata/local-policy/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: docker-official-images 5 | description: Docker Official Images 6 | files: 7 | - path: doi/policy.rego 8 | rules: 9 | - pattern: "^docker[.]io/library/(.*)$" 10 | policy-id: docker-official-images 11 | - pattern: "repo$" 12 | policy-id: docker-official-images 13 | - pattern: "test-image$" 14 | policy-id: docker-official-images 15 | - pattern: "image-signer-verifier-test$" 16 | policy-id: docker-official-images 17 | - pattern: "library/(.*)$" 18 | rewrite: docker.io/library/$1 19 | -------------------------------------------------------------------------------- /test/testdata/local-policy-inputs/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: docker-official-images 5 | description: Docker Official Images 6 | files: 7 | - path: doi/policy.rego 8 | rules: 9 | - pattern: "^docker[.]io/library/(.*)$" 10 | policy-id: docker-official-images 11 | - pattern: "repo$" 12 | policy-id: docker-official-images 13 | - pattern: "test-image$" 14 | policy-id: docker-official-images 15 | - pattern: "image-signer-verifier-test$" 16 | policy-id: docker-official-images 17 | - pattern: "library/(.*)$" 18 | rewrite: docker.io/library/$1 19 | -------------------------------------------------------------------------------- /mapping/testdata/mappings/doi-platform/mapping.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | kind: policy-mapping 3 | policies: 4 | - id: docker-official-images 5 | description: Docker Official Images 6 | files: 7 | - path: doi/policy.rego 8 | rules: 9 | - pattern: "^docker[.]io/library/(.*)$" 10 | platforms: ["linux/amd64"] 11 | policy-id: docker-official-images 12 | - pattern: "^docker.io/mozilla/(.*)$" 13 | platforms: ["linux/amd64", "linux/arm64"] 14 | policy-id: docker-official-images 15 | - pattern: "^mycoolmirror[.]org/library/(.*)$" 16 | platforms: ["linux/amd64"] 17 | rewrite: "docker.io/library/$1" 18 | -------------------------------------------------------------------------------- /policy/testdata/policies/verify-sig/doi/policy.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | keys := [{ 6 | "id": "a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4", 7 | "key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgH23D1i2+ZIOtVjmfB7iFvX8AhVN\n9CPJ4ie9axw+WRHozGnRy99U2dRge3zueBBg2MweF0zrToXGig2v3YOrdw==\n-----END PUBLIC KEY-----", 8 | "from": "2023-12-15T14:00:00Z", 9 | "to": null, 10 | }] 11 | 12 | opts := {"keys": keys} 13 | 14 | success if { 15 | some env in attest.fetch("foo") 16 | statement := attest.verify(env, opts) 17 | } 18 | 19 | result := {"success": success} 20 | -------------------------------------------------------------------------------- /test/testdata/unsigned-image/blobs/sha256/7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:363133d587b90ff7a21f7b32a96be8422c6799683f0e1e6d71de5c03a82ab35e", 7 | "size": 438 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", 12 | "digest": "sha256:07d9a868932bd092fa0a4c4df943785a7ba9cee12dbf446d02488319a5fbf336", 13 | "size": 116 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:d85d624a324422194b43cccd975b5752cf0acaedd668bb525fcd40c3587cc460", 7 | "size": 453 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", 12 | "digest": "sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad", 13 | "size": 116 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:1c70b3e7c3a57801501ec127aa6c918c390c373294ec4fc48f2c6fe703fcc6fe", 7 | "size": 453 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", 12 | "digest": "sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad", 13 | "size": 116 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /test/testdata/no-provenance-index/blobs/sha256/52f7a760b9322aa1af76d998763868b7d1bfec2331a2574a438ef44c92c0c46d: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:c0bd7799c46e00830b4d7cb8c1f622d14aae81643a90be5ec38c9be4bdd70f6c", 7 | "size": 438 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", 12 | "digest": "sha256:07d9a868932bd092fa0a4c4df943785a7ba9cee12dbf446d02488319a5fbf336", 13 | "size": 116 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /test/testdata/no-provenance-index/blobs/sha256/7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:363133d587b90ff7a21f7b32a96be8422c6799683f0e1e6d71de5c03a82ab35e", 7 | "size": 438 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", 12 | "digest": "sha256:07d9a868932bd092fa0a4c4df943785a7ba9cee12dbf446d02488319a5fbf336", 13 | "size": 116 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac.always-fail.rego/blobs/sha256/e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | violations contains { 6 | "type": "always_fail", 7 | "description": "This policy always fails", 8 | } 9 | 10 | result := { 11 | "success": false, 12 | "violations": violations, 13 | "summary": { 14 | "subjects": set(), 15 | "slsa_levels": ["SLSA_BUILD_LEVEL_3"], 16 | "verifier": "docker-official-images", 17 | "policy_uri": "https://docker.com/official/policy/v0.1", 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /policy/testdata/policies/test/def_parse/def_parse_test.rego: -------------------------------------------------------------------------------- 1 | package def_parse_test 2 | 3 | import rego.v1 4 | 5 | test_parse_library_definition if { 6 | def := `Maintainers: me (@me) 7 | GitRepo: blah 8 | 9 | Tags: 1, 2, 3 10 | GitCommit: fa105cb3c26c8f0e87d7dbb1bf5293691ac2f688 11 | File: Dockerfile.foo` 12 | result := attest.internals.parse_library_definition(def) 13 | definition := result.value 14 | definition.Entries[0].GitRepo == "blah" 15 | definition.Entries[0].GitCommit == "fa105cb3c26c8f0e87d7dbb1bf5293691ac2f688" 16 | definition.Entries[0].Tags == ["1", "2", "3"] 17 | definition.Entries[0].File == "Dockerfile.foo" 18 | } 19 | -------------------------------------------------------------------------------- /template/bash.txt: -------------------------------------------------------------------------------- 1 | # Copyright Docker attest authors 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /template/makefile.txt: -------------------------------------------------------------------------------- 1 | # Copyright Docker attest authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /template/dockerfile.txt: -------------------------------------------------------------------------------- 1 | # Copyright Docker attest authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /template/go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/02119a076ec3878c736c3a95e20794f5a8d5bce3d7ecc264681bb7334ca2e24b.test.txt/blobs/sha256/4b0cc6119d25a34299b24d86095f21f667378aadf3c493c2d92f134869fd2c73: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","data":"e30="},"layers":[{"mediaType":"application/vnd.tuf.target","size":31,"digest":"sha256:02119a076ec3878c736c3a95e20794f5a8d5bce3d7ecc264681bb7334ca2e24b","annotations":{"tuf.io/filename":"02119a076ec3878c736c3a95e20794f5a8d5bce3d7ecc264681bb7334ca2e24b.test.txt"}}]} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/baad1a9d61afa5d6f8717f576b57b9749e5549da4b826746fd73a5a914ac5be1.mapping.yaml/blobs/sha256/f6c752a7909493c7aaee73c51f174a2ca9b2edd2dc3868c8306b80b0e7f489e1: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","data":"e30="},"layers":[{"mediaType":"application/vnd.tuf.target","size":272,"digest":"sha256:baad1a9d61afa5d6f8717f576b57b9749e5549da4b826746fd73a5a914ac5be1","annotations":{"tuf.io/filename":"baad1a9d61afa5d6f8717f576b57b9749e5549da4b826746fd73a5a914ac5be1.mapping.yaml"}}]} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/testing/blobs/sha256/93a0c6a57652e182f3e04fed6e3bd0eedeb98c624af12668bc9e2741c7443374: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | # this file only exists in the testing delegation 6 | 7 | violations contains { 8 | "type": "testing_delegation", 9 | "description": "This policy always fails. We'd better not promote this to production.", 10 | } 11 | 12 | result := { 13 | "success": false, 14 | "violations": violations, 15 | "summary": { 16 | "subjects": set(), 17 | "slsa_levels": ["SLSA_BUILD_LEVEL_3"], 18 | "verifier": "docker-official-images", 19 | "policy_uri": "https://docker.com/official/policy/v0.1", 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo/targets/testing/93a0c6a57652e182f3e04fed6e3bd0eedeb98c624af12668bc9e2741c7443374.test-only.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | # this file only exists in the testing delegation 6 | 7 | violations contains { 8 | "type": "testing_delegation", 9 | "description": "This policy always fails. We'd better not promote this to production.", 10 | } 11 | 12 | result := { 13 | "success": false, 14 | "violations": violations, 15 | "summary": { 16 | "subjects": set(), 17 | "slsa_levels": ["SLSA_BUILD_LEVEL_3"], 18 | "verifier": "docker-official-images", 19 | "policy_uri": "https://docker.com/official/policy/v0.1", 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | run: 2 | timeout: 5m 3 | go: "1.22" 4 | 5 | linters-settings: 6 | gocritic: 7 | enabled-tags: 8 | - performance 9 | lll: 10 | line-length: 200 11 | 12 | misspell: 13 | locale: US 14 | 15 | linters: 16 | disable-all: true 17 | enable: 18 | - errcheck 19 | - forcetypeassert 20 | - gocritic 21 | - goconst 22 | - godot 23 | - gofmt 24 | - gofumpt 25 | - goimports 26 | - gosec 27 | - gosimple 28 | - govet 29 | - importas 30 | - ineffassign 31 | - misspell 32 | - revive # replacement for golint 33 | - staticcheck 34 | - typecheck 35 | - unused 36 | - whitespace 37 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac.always-fail.rego/blobs/sha256/0d9f576776df40330e2f646eca34a51f4a092bd23409b19824ed36c1e8ed70ac: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","data":"e30="},"layers":[{"mediaType":"application/vnd.tuf.target","size":364,"digest":"sha256:e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac","annotations":{"tuf.io/filename":"e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac.always-fail.rego"}}]} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/bc46e8c31646f166a9efbd14fef154dd84cf07efc95c96be3a201c84470dcbc1.jonnystoten2.rego/blobs/sha256/39be48096573b49cb30ce5479d25c49a3405e8495daa9066e813e96338a17f48: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","data":"e30="},"layers":[{"mediaType":"application/vnd.tuf.target","size":5857,"digest":"sha256:bc46e8c31646f166a9efbd14fef154dd84cf07efc95c96be3a201c84470dcbc1","annotations":{"tuf.io/filename":"bc46e8c31646f166a9efbd14fef154dd84cf07efc95c96be3a201c84470dcbc1.jonnystoten2.rego"}}]} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/bd6394a08afc1edfe5512fc14e63025a337e25ca0013c1068ec879742fc3a3c3.version-constraints/blobs/sha256/b197e563dc2e6961628f2d9543da7555b50fdd78877ef34917d642a60e6bd73f: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","data":"e30="},"layers":[{"mediaType":"application/vnd.tuf.target","size":12,"digest":"sha256:bd6394a08afc1edfe5512fc14e63025a337e25ca0013c1068ec879742fc3a3c3","annotations":{"tuf.io/filename":"bd6394a08afc1edfe5512fc14e63025a337e25ca0013c1068ec879742fc3a3c3.version-constraints"}}]} -------------------------------------------------------------------------------- /test/testdata/missing-subject-layout/blobs/sha256/a051db630f91aae4fc649b455724f2c5c60ae0c508e87d88937e862524f488b5: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:5049b0fd8de6fc8937065a0b26214e5a1e620e98488de6bac72c0284b1a5242f", 7 | "size": 167 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.in-toto+json", 12 | "digest": "sha256:e2c3b7df754e062b0c6b17c5262ea237fc86d68432e86e68724c57f04be3d064", 13 | "size": 917, 14 | "annotations": { 15 | "in-toto.io/predicate-type": "https://spdx.dev/Document" 16 | } 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /test/testdata/no-provenance-index/blobs/sha256/059eea09507d0f904b8892ee59fcd3ddec1a637fc40fb7c83c432c6ff27e2f91: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:bb0ed50656ccdb2eb114407de579554426777d6dc0e4206a6f746afb4ee5237e", 7 | "size": 167 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.in-toto+json", 12 | "digest": "sha256:618f1e2f903648dde23cc38dc0ed7eed83d5394a6902bb7bfae8fa707c2e5c33", 13 | "size": 946, 14 | "annotations": { 15 | "in-toto.io/predicate-type": "https://spdx.dev/Document" 16 | } 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /test/testdata/no-provenance-index/blobs/sha256/0b1ee0f360b073d2f76ceed15a63e291659fbcc6c3caf3be39e437d8344b520e: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:816b20ea86474dcfb2906ffaf4410262dfcb0d49fdfb60698775f7bc10aad7fb", 7 | "size": 167 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.in-toto+json", 12 | "digest": "sha256:f0dac65dd0ff6a656c419c654ac672c38029a3f1a4b4acce062bd2f5a923ffae", 13 | "size": 946, 14 | "annotations": { 15 | "in-toto.io/predicate-type": "https://spdx.dev/Document" 16 | } 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /test/testdata/containerd-subject-layout/blobs/sha256/a051db630f91aae4fc649b455724f2c5c60ae0c508e87d88937e862524f488b5: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:5049b0fd8de6fc8937065a0b26214e5a1e620e98488de6bac72c0284b1a5242f", 7 | "size": 167 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.in-toto+json", 12 | "digest": "sha256:e2c3b7df754e062b0c6b17c5262ea237fc86d68432e86e68724c57f04be3d064", 13 | "size": 917, 14 | "annotations": { 15 | "in-toto.io/predicate-type": "https://spdx.dev/Document" 16 | } 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo/metadata/11.snapshot.json: -------------------------------------------------------------------------------- 1 | { 2 | "signatures": [ 3 | { 4 | "keyid": "bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5", 5 | "sig": "3046022100aeac20924d8a674836e298773a4bb728559cf0acfbae5b6bf1b9c8e29b1a1d1c022100a00c2d981a6ae8b530d213433946216604bcab34bb85435beed63a0e8b0f837c" 6 | } 7 | ], 8 | "signed": { 9 | "_type": "snapshot", 10 | "expires": "2034-09-07T14:41:18Z", 11 | "meta": { 12 | "policy.json": { 13 | "version": 1 14 | }, 15 | "targets.json": { 16 | "version": 11 17 | }, 18 | "test-role.json": { 19 | "version": 2 20 | }, 21 | "testing.json": { 22 | "version": 2 23 | } 24 | }, 25 | "spec_version": "1.0.31", 26 | "version": 11 27 | } 28 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/test-role/index.json: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.index.v1+json","manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","size":493,"digest":"sha256:46ad77c669b6b5b015e4b164ad66624d0c7704dfae8752e7844a632d8e3df640","annotations":{"tuf.io/filename":"test-role/dir1/dir2/dir3/bb8fcf06f6c067dcbcb394d7d9ced788316fc02b715fe679097281108a4bd465.test.txt"},"artifactType":"application/vnd.oci.empty.v1+json"},{"mediaType":"application/vnd.oci.image.manifest.v1+json","size":493,"digest":"sha256:f1558403107419b9a79ce371bba1425c123daf3f77437ba42c77b9dd0f26d6f8","annotations":{"tuf.io/filename":"test-role/d1bb6181284970ae43fbbc88b5e72f9a5942ebac20588aa0c4bf78ba621e1ee2.test.txt"},"artifactType":"application/vnd.oci.empty.v1+json"}]} -------------------------------------------------------------------------------- /test/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright Docker attest authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | FROM alpine AS build 15 | RUN echo "hello world" > /tmp/hello.txt 16 | 17 | FROM scratch 18 | COPY --from=build /tmp/hello.txt / 19 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | time: "08:00" 8 | labels: 9 | - "dependencies" 10 | commit-message: 11 | prefix: "feat" 12 | include: "scope" 13 | - package-ecosystem: "github-actions" 14 | directory: "/" 15 | schedule: 16 | interval: "daily" 17 | time: "08:00" 18 | labels: 19 | - "dependencies" 20 | commit-message: 21 | prefix: "chore" 22 | include: "scope" 23 | - package-ecosystem: "docker" 24 | directory: "/" 25 | schedule: 26 | interval: "daily" 27 | time: "08:00" 28 | labels: 29 | - "dependencies" 30 | commit-message: 31 | prefix: "feat" 32 | include: "scope" 33 | -------------------------------------------------------------------------------- /policy/testdata/policies/wrong-key/doi/policy.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | keys := [{ 6 | "id": "a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4", 7 | "key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHyZpSgzvqFqNv7f3x7865OS38rAb\nQMcff55zM2UH/KR3Pr84a8QsGDNgaNGzJQJWjtMSgfV8WnNoffNK+svFNg==\n-----END PUBLIC KEY-----", 8 | "from": "2023-12-15T14:00:00Z", 9 | "to": null, 10 | }] 11 | 12 | default success := false 13 | 14 | provs(pred) := p if { 15 | res := attest.fetch(pred) 16 | not res.error 17 | p := res.value 18 | } 19 | 20 | atts := union({provs("foo")}) 21 | 22 | opts := {"keys": keys} 23 | 24 | success if { 25 | some env in atts 26 | res := attest.verify(env, opts) 27 | not res.error 28 | } 29 | 30 | result := {"success": success} 31 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/test-role/blobs/sha256/ad7b6cdc3c7c0af0f8f05459471074adb6353ff72e65e2ec2629fafcce1603b1: -------------------------------------------------------------------------------- 1 | {"signatures":[{"keyid":"76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221","sig":"3065023100c37572d6e0608e0501026d99238ee37d26856d93074227410b0748e56775f8369cf7c44553b73d8a30aa94a388148ca602305b46acbb0e8818657725024a39d02589538845ad9fa0c2b6eb18f431f560096045fd825586dce81688c9574b11b975da"}],"signed":{"_type":"targets","expires":"2034-05-29T20:25:01Z","spec_version":"1.0.31","targets":{"test-role/dir1/dir2/dir3/test.txt":{"hashes":{"sha256":"bb8fcf06f6c067dcbcb394d7d9ced788316fc02b715fe679097281108a4bd465"},"length":46},"test-role/test.txt":{"hashes":{"sha256":"d1bb6181284970ae43fbbc88b5e72f9a5942ebac20588aa0c4bf78ba621e1ee2"},"length":32}},"version":2,"x-tuf-on-ci-expiry-period":3650,"x-tuf-on-ci-signing-period":60}} -------------------------------------------------------------------------------- /policy/evaluator.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package policy 18 | 19 | import ( 20 | "context" 21 | 22 | "github.com/docker/attest/attestation" 23 | ) 24 | 25 | type Evaluator interface { 26 | Evaluate(ctx context.Context, resolver attestation.Resolver, pctx *Policy, input *Input) (*Result, error) 27 | } 28 | -------------------------------------------------------------------------------- /attestation/resolver.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package attestation 18 | 19 | import ( 20 | "context" 21 | 22 | "github.com/docker/attest/oci" 23 | ) 24 | 25 | type Resolver interface { 26 | oci.ImageDetailsResolver 27 | Attestations(ctx context.Context, mediaType string) ([]*EnvelopeReference, error) 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | release: 4 | types: [published] 5 | jobs: 6 | trigger_attest_update: 7 | name: Update attest lib - ALL 8 | runs-on: ubuntu-latest 9 | timeout-minutes: 5 10 | steps: 11 | - name: Generate GitHub App Token 12 | id: app-token 13 | uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0 14 | with: 15 | app-id: ${{ vars.ATTEST_RELEASE_APP_ID }} 16 | private-key: ${{ secrets.ATTEST_RELEASE_APP_PRIVATE_KEY }} 17 | repositories: "attest-actions" 18 | - name: Send repository_dispatch event 19 | uses: peter-evans/repository-dispatch@v3.0.0 20 | with: 21 | token: ${{ steps.app-token.outputs.token }} 22 | event-type: update_attest_all 23 | repository: docker/attest-actions 24 | client-payload: '{"attest_version": "${{ github.ref_name }}"}' 25 | -------------------------------------------------------------------------------- /internal/util/crypto.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package util 18 | 19 | import ( 20 | "crypto/sha256" 21 | "encoding/hex" 22 | ) 23 | 24 | func SHA256Hex(input []byte) string { 25 | return hex.EncodeToString(SHA256(input)) 26 | } 27 | 28 | func SHA256(data []byte) []byte { 29 | h := sha256.Sum256(data) 30 | return h[:] 31 | } 32 | -------------------------------------------------------------------------------- /test/testdata/missing-subject-layout/blobs/sha256/e2c3b7df754e062b0c6b17c5262ea237fc86d68432e86e68724c57f04be3d064: -------------------------------------------------------------------------------- 1 | {"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://spdx.dev/Document","subject":[{"name":"pkg:docker/test@latest?platform=linux%2Farm64","digest":{"sha256":"e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec"}}],"predicate":{"SPDXID":"SPDXRef-DOCUMENT","creationInfo":{"created":"2024-09-19T20:28:48Z","creators":["Organization: Anchore, Inc","Tool: syft-v0.105.0"],"licenseListVersion":"3.23"},"dataLicense":"CC0-1.0","documentNamespace":"https://anchore.com/syft/dir/sbom-4d662591-02b0-4448-8cdc-c8b539bbe1a0","name":"sbom","packages":[{"SPDXID":"SPDXRef-DocumentRoot-Directory-sbom","downloadLocation":"NOASSERTION","filesAnalyzed":false,"name":"sbom","primaryPackagePurpose":"FILE","supplier":"NOASSERTION"}],"relationships":[{"relatedSpdxElement":"SPDXRef-DocumentRoot-Directory-sbom","relationshipType":"DESCRIBES","spdxElementId":"SPDXRef-DOCUMENT"}],"spdxVersion":"SPDX-2.3"}} -------------------------------------------------------------------------------- /test/testdata/containerd-subject-layout/blobs/sha256/e2c3b7df754e062b0c6b17c5262ea237fc86d68432e86e68724c57f04be3d064: -------------------------------------------------------------------------------- 1 | {"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://spdx.dev/Document","subject":[{"name":"pkg:docker/test@latest?platform=linux%2Farm64","digest":{"sha256":"e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec"}}],"predicate":{"SPDXID":"SPDXRef-DOCUMENT","creationInfo":{"created":"2024-09-19T20:28:48Z","creators":["Organization: Anchore, Inc","Tool: syft-v0.105.0"],"licenseListVersion":"3.23"},"dataLicense":"CC0-1.0","documentNamespace":"https://anchore.com/syft/dir/sbom-4d662591-02b0-4448-8cdc-c8b539bbe1a0","name":"sbom","packages":[{"SPDXID":"SPDXRef-DocumentRoot-Directory-sbom","downloadLocation":"NOASSERTION","filesAnalyzed":false,"name":"sbom","primaryPackagePurpose":"FILE","supplier":"NOASSERTION"}],"relationships":[{"relatedSpdxElement":"SPDXRef-DocumentRoot-Directory-sbom","relationshipType":"DESCRIBES","spdxElementId":"SPDXRef-DOCUMENT"}],"spdxVersion":"SPDX-2.3"}} -------------------------------------------------------------------------------- /oci/resolver.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package oci 18 | 19 | import ( 20 | "context" 21 | 22 | v1 "github.com/google/go-containerregistry/pkg/v1" 23 | ) 24 | 25 | type ImageDetailsResolver interface { 26 | ImageName(ctx context.Context) (string, error) 27 | ImagePlatform(ctx context.Context) (*v1.Platform, error) 28 | ImageDescriptor(ctx context.Context) (*v1.Descriptor, error) 29 | } 30 | -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850: -------------------------------------------------------------------------------- 1 | {"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://spdx.dev/Document","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Famd64","digest":{"sha256":"da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620"}}],"predicate":{"spdxVersion":"SPDX-2.3","dataLicense":"CC0-1.0","SPDXID":"SPDXRef-DOCUMENT","name":"sbom","documentNamespace":"https://anchore.com/syft/dir/sbom-6d900ae6-587d-4695-9c01-511801a85b65","creationInfo":{"licenseListVersion":"3.23","creators":["Organization: Anchore, Inc","Tool: syft-v0.105.0","Tool: buildkit-v0.12.4"],"created":"2024-03-08T16:42:30Z"},"packages":[{"name":"sbom","SPDXID":"SPDXRef-DocumentRoot-Directory-sbom","supplier":"NOASSERTION","downloadLocation":"NOASSERTION","filesAnalyzed":false,"primaryPackagePurpose":"FILE"}],"relationships":[{"spdxElementId":"SPDXRef-DOCUMENT","relatedSpdxElement":"SPDXRef-DocumentRoot-Directory-sbom","relationshipType":"DESCRIBES"}]}} -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e: -------------------------------------------------------------------------------- 1 | {"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://spdx.dev/Document","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Farm64","digest":{"sha256":"7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e"}}],"predicate":{"spdxVersion":"SPDX-2.3","dataLicense":"CC0-1.0","SPDXID":"SPDXRef-DOCUMENT","name":"sbom","documentNamespace":"https://anchore.com/syft/dir/sbom-6d900ae6-587d-4695-9c01-511801a85b65","creationInfo":{"licenseListVersion":"3.23","creators":["Organization: Anchore, Inc","Tool: syft-v0.105.0","Tool: buildkit-v0.12.4"],"created":"2024-03-08T16:42:30Z"},"packages":[{"name":"sbom","SPDXID":"SPDXRef-DocumentRoot-Directory-sbom","supplier":"NOASSERTION","downloadLocation":"NOASSERTION","filesAnalyzed":false,"primaryPackagePurpose":"FILE"}],"relationships":[{"spdxElementId":"SPDXRef-DOCUMENT","relatedSpdxElement":"SPDXRef-DocumentRoot-Directory-sbom","relationshipType":"DESCRIBES"}]}} -------------------------------------------------------------------------------- /test/testdata/no-provenance-index/blobs/sha256/618f1e2f903648dde23cc38dc0ed7eed83d5394a6902bb7bfae8fa707c2e5c33: -------------------------------------------------------------------------------- 1 | {"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://spdx.dev/Document","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Famd64","digest":{"sha256":"7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390"}}],"predicate":{"spdxVersion":"SPDX-2.3","dataLicense":"CC0-1.0","SPDXID":"SPDXRef-DOCUMENT","name":"sbom","documentNamespace":"https://anchore.com/syft/dir/sbom-b92d7d2e-7ffe-4d0a-8194-9af68e80e169","creationInfo":{"licenseListVersion":"3.23","creators":["Organization: Anchore, Inc","Tool: syft-v0.105.0","Tool: buildkit-v0.15.2"],"created":"2024-09-27T16:10:21Z"},"packages":[{"name":"sbom","SPDXID":"SPDXRef-DocumentRoot-Directory-sbom","supplier":"NOASSERTION","downloadLocation":"NOASSERTION","filesAnalyzed":false,"primaryPackagePurpose":"FILE"}],"relationships":[{"spdxElementId":"SPDXRef-DOCUMENT","relatedSpdxElement":"SPDXRef-DocumentRoot-Directory-sbom","relationshipType":"DESCRIBES"}]}} -------------------------------------------------------------------------------- /test/testdata/no-provenance-index/blobs/sha256/f0dac65dd0ff6a656c419c654ac672c38029a3f1a4b4acce062bd2f5a923ffae: -------------------------------------------------------------------------------- 1 | {"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://spdx.dev/Document","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Farm64","digest":{"sha256":"52f7a760b9322aa1af76d998763868b7d1bfec2331a2574a438ef44c92c0c46d"}}],"predicate":{"spdxVersion":"SPDX-2.3","dataLicense":"CC0-1.0","SPDXID":"SPDXRef-DOCUMENT","name":"sbom","documentNamespace":"https://anchore.com/syft/dir/sbom-b92d7d2e-7ffe-4d0a-8194-9af68e80e169","creationInfo":{"licenseListVersion":"3.23","creators":["Organization: Anchore, Inc","Tool: syft-v0.105.0","Tool: buildkit-v0.15.2"],"created":"2024-09-27T16:10:21Z"},"packages":[{"name":"sbom","SPDXID":"SPDXRef-DocumentRoot-Directory-sbom","supplier":"NOASSERTION","downloadLocation":"NOASSERTION","filesAnalyzed":false,"primaryPackagePurpose":"FILE"}],"relationships":[{"spdxElementId":"SPDXRef-DOCUMENT","relatedSpdxElement":"SPDXRef-DocumentRoot-Directory-sbom","relationshipType":"DESCRIBES"}]}} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/testing/blobs/sha256/f06ffb8527f121fa950570349ed57f77498ca4ac9a590fb15a0ec97a67a70ea6: -------------------------------------------------------------------------------- 1 | {"signatures":[{"keyid":"beac53949c4cf075824edede7d41715941f524db247d1b455a2389d7490ecd72","sig":"304502207ffc26ed83118f9aa0e0c7d6cad1cbcca7ffedc1cdfa7d1c5d6bc589ee1586c502210091bf85dfbe58b300af02922e28878a135767a07a7ed93e3f169d418e5b03dcd0"}],"signed":{"_type":"targets","expires":"2025-09-09T14:38:32Z","spec_version":"1.0.31","targets":{"testing/always-fail.rego":{"hashes":{"sha256":"e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac"},"length":364},"testing/jonnystoten2.rego":{"hashes":{"sha256":"bc46e8c31646f166a9efbd14fef154dd84cf07efc95c96be3a201c84470dcbc1"},"length":5857},"testing/mapping.yaml":{"hashes":{"sha256":"d3b20bd505b925e6b4b73dd875e9c5839e1797061049e243bdb0d70d62f6d090"},"length":269},"testing/test-only.rego":{"hashes":{"sha256":"93a0c6a57652e182f3e04fed6e3bd0eedeb98c624af12668bc9e2741c7443374"},"length":467}},"version":2,"x-tuf-on-ci-expiry-period":365,"x-tuf-on-ci-signing-period":60}} -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo/metadata/2.test-role.json: -------------------------------------------------------------------------------- 1 | { 2 | "signatures": [ 3 | { 4 | "keyid": "76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221", 5 | "sig": "3065023100c37572d6e0608e0501026d99238ee37d26856d93074227410b0748e56775f8369cf7c44553b73d8a30aa94a388148ca602305b46acbb0e8818657725024a39d02589538845ad9fa0c2b6eb18f431f560096045fd825586dce81688c9574b11b975da" 6 | } 7 | ], 8 | "signed": { 9 | "_type": "targets", 10 | "expires": "2034-05-29T20:25:01Z", 11 | "spec_version": "1.0.31", 12 | "targets": { 13 | "test-role/dir1/dir2/dir3/test.txt": { 14 | "hashes": { 15 | "sha256": "bb8fcf06f6c067dcbcb394d7d9ced788316fc02b715fe679097281108a4bd465" 16 | }, 17 | "length": 46 18 | }, 19 | "test-role/test.txt": { 20 | "hashes": { 21 | "sha256": "d1bb6181284970ae43fbbc88b5e72f9a5942ebac20588aa0c4bf78ba621e1ee2" 22 | }, 23 | "length": 32 24 | } 25 | }, 26 | "version": 2, 27 | "x-tuf-on-ci-expiry-period": 3650, 28 | "x-tuf-on-ci-signing-period": 60 29 | } 30 | } -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/26da286bbc886aa14d191808db8fcbbd5d8ec68cf0047f954133e76d8e73d71c: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:2953164d6cc6c8bb8271f78f9fb2003318350a8026ea082b63a249cfa60918a3", 7 | "size": 241 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.in-toto+json", 12 | "digest": "sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850", 13 | "size": 946, 14 | "annotations": { 15 | "in-toto.io/predicate-type": "https://spdx.dev/Document" 16 | } 17 | }, 18 | { 19 | "mediaType": "application/vnd.in-toto+json", 20 | "digest": "sha256:c6dd08ccc92ab60a87648a6b61fbf88d9287a936b285a8b4dde8893a1f4ffedf", 21 | "size": 3944, 22 | "annotations": { 23 | "in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2" 24 | } 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/3883faf6acc3cae029364ed17ec2ce917fc9a500aab72f813d26fed8404e7162: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:2a9b671f3fc9bc5ca967b616d96cbbdb6493e32d4f6abd8f7a191990e8efb289", 7 | "size": 241 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.in-toto+json", 12 | "digest": "sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850", 13 | "size": 946, 14 | "annotations": { 15 | "in-toto.io/predicate-type": "https://spdx.dev/Document" 16 | } 17 | }, 18 | { 19 | "mediaType": "application/vnd.in-toto+json", 20 | "digest": "sha256:8f94b6e2a8be82e2e5b562d73212578bb3a02e8c0da7fc175c79045e73519375", 21 | "size": 3944, 22 | "annotations": { 23 | "in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2" 24 | } 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/6658b8ba1e1221a6288bf50cd7813f814e2baad70141a3e315b7c3476b0f476f: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:fed2c8841731e2cf1ceb53c49c6440fcd6d565a8658141914a8a07c127e00d7e", 7 | "size": 241 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.in-toto+json", 12 | "digest": "sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e", 13 | "size": 946, 14 | "annotations": { 15 | "in-toto.io/predicate-type": "https://spdx.dev/Document" 16 | } 17 | }, 18 | { 19 | "mediaType": "application/vnd.in-toto+json", 20 | "digest": "sha256:92d3311aa91737ff81e2a4c8e269e78c3c95df611b44580426c384d3f5057776", 21 | "size": 3944, 22 | "annotations": { 23 | "in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2" 24 | } 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/8049aa9ad3479085066b31d02b74310803129c3eb1e22d2e62279f8c72340b55: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:f634c4c53b03bf8ff917b61165631fda0cfe691a383e7b333269a53bf9a79c34", 7 | "size": 241 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.in-toto+json", 12 | "digest": "sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e", 13 | "size": 946, 14 | "annotations": { 15 | "in-toto.io/predicate-type": "https://spdx.dev/Document" 16 | } 17 | }, 18 | { 19 | "mediaType": "application/vnd.in-toto+json", 20 | "digest": "sha256:9fe102c03d71d47a24cd7fc7db8e7affc05fd9bf98eb027038b7daf176861e85", 21 | "size": 3943, 22 | "annotations": { 23 | "in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2" 24 | } 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/9638ca53d2795806cf51b7461575c51e4a626a091dc2842b35cac18c787ff80f: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:3e64f9d2888ed9211fbf2c6b5853ea559248fdb4ab711bcea34b65c62f0e026b", 7 | "size": 241 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.in-toto+json", 12 | "digest": "sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850", 13 | "size": 946, 14 | "annotations": { 15 | "in-toto.io/predicate-type": "https://spdx.dev/Document" 16 | } 17 | }, 18 | { 19 | "mediaType": "application/vnd.in-toto+json", 20 | "digest": "sha256:8f2f55fc493890c2482a1220844157f4b0c8a6445d220af741e9fee8099bf532", 21 | "size": 3943, 22 | "annotations": { 23 | "in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2" 24 | } 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/aeca14119e3242c51633a899438518217417e01414d18189a3cf71c07f2a02c3: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:4e5988d06eee647cb901d4435830fbf13cf3ab1ae27ec91246b280514e6a7b33", 7 | "size": 241 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.in-toto+json", 12 | "digest": "sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850", 13 | "size": 946, 14 | "annotations": { 15 | "in-toto.io/predicate-type": "https://spdx.dev/Document" 16 | } 17 | }, 18 | { 19 | "mediaType": "application/vnd.in-toto+json", 20 | "digest": "sha256:c01e5307ec84299048d76f162abec6f8bee4c463103161ab772c774e7ae9dd6d", 21 | "size": 3944, 22 | "annotations": { 23 | "in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2" 24 | } 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/e0a9b9404ac2691b9b1c9ef217f22bb1e106efd5ee791640411764e1cf39ea2c: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:a4cf4b24f3fa8cd49a59e8fd4ef5ce285f0aa928d2651f7ec3d5a78276249dec", 7 | "size": 241 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.in-toto+json", 12 | "digest": "sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e", 13 | "size": 946, 14 | "annotations": { 15 | "in-toto.io/predicate-type": "https://spdx.dev/Document" 16 | } 17 | }, 18 | { 19 | "mediaType": "application/vnd.in-toto+json", 20 | "digest": "sha256:371954672cfaa92735d6fbd70a787aac618a41d4c8ec8d6e12bd12d0cc601706", 21 | "size": 3944, 22 | "annotations": { 23 | "in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2" 24 | } 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/f2b95cecafef9c22a5d059fac8f20e3645a45370e52abf9581dd4eedd152fce0: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 4 | "config": { 5 | "mediaType": "application/vnd.oci.image.config.v1+json", 6 | "digest": "sha256:c9f436179969b60ec0bbd406b1340c501e59376a658b14b53c1828924c0ac668", 7 | "size": 241 8 | }, 9 | "layers": [ 10 | { 11 | "mediaType": "application/vnd.in-toto+json", 12 | "digest": "sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e", 13 | "size": 946, 14 | "annotations": { 15 | "in-toto.io/predicate-type": "https://spdx.dev/Document" 16 | } 17 | }, 18 | { 19 | "mediaType": "application/vnd.in-toto+json", 20 | "digest": "sha256:5171425b78a2aedb43eb4e95083e64d3764c798507596ceded776c4ab038c224", 21 | "size": 3944, 22 | "annotations": { 23 | "in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2" 24 | } 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /test/testdata/containerd-subject-layout/blobs/sha256/bba371330d0124ce45f669c5d73092a3f2078ed1491e2bc52189a82e279074a1: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 7 | "digest": "sha256:e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec", 8 | "size": 288, 9 | "platform": { 10 | "architecture": "arm64", 11 | "os": "linux" 12 | } 13 | }, 14 | { 15 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 16 | "digest": "sha256:a051db630f91aae4fc649b455724f2c5c60ae0c508e87d88937e862524f488b5", 17 | "size": 558, 18 | "annotations": { 19 | "vnd.docker.reference.digest": "sha256:e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec", 20 | "vnd.docker.reference.type": "attestation-manifest" 21 | }, 22 | "platform": { 23 | "architecture": "unknown", 24 | "os": "unknown" 25 | } 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /test/testdata/missing-subject-layout/blobs/sha256/bba371330d0124ce45f669c5d73092a3f2078ed1491e2bc52189a82e279074a1: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 7 | "digest": "sha256:e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec", 8 | "size": 288, 9 | "platform": { 10 | "architecture": "arm64", 11 | "os": "linux" 12 | } 13 | }, 14 | { 15 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 16 | "digest": "sha256:a051db630f91aae4fc649b455724f2c5c60ae0c508e87d88937e862524f488b5", 17 | "size": 558, 18 | "annotations": { 19 | "vnd.docker.reference.digest": "sha256:e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec", 20 | "vnd.docker.reference.type": "attestation-manifest" 21 | }, 22 | "platform": { 23 | "architecture": "unknown", 24 | "os": "unknown" 25 | } 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /signerverifier/keyid.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package signerverifier 18 | 19 | import ( 20 | "crypto" 21 | "crypto/x509" 22 | "fmt" 23 | 24 | "github.com/docker/attest/internal/util" 25 | ) 26 | 27 | func KeyID(pubKey crypto.PublicKey) (string, error) { 28 | pub, err := x509.MarshalPKIXPublicKey(pubKey) 29 | if err != nil { 30 | return "", fmt.Errorf("error marshaling public key: %w", err) 31 | } 32 | return util.SHA256Hex(pub), nil 33 | } 34 | -------------------------------------------------------------------------------- /useragent/useragent_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package useragent 18 | 19 | import ( 20 | "context" 21 | "testing" 22 | ) 23 | 24 | // test the user agent setting and getting. 25 | func TestSetUserAgent(t *testing.T) { 26 | ctx := context.Background() 27 | if Get(ctx) != defaultUserAgent { 28 | t.Errorf("expected user agent to be '%s', got %q", defaultUserAgent, Get(ctx)) 29 | } 30 | 31 | ctx = Set(ctx, "test-agent") 32 | if Get(ctx) != "test-agent" { 33 | t.Errorf("expected user agent to be 'test-agent', got %q", Get(ctx)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /oci/types_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package oci 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/docker/attest/internal/util" 23 | "github.com/google/go-containerregistry/pkg/v1/empty" 24 | "github.com/stretchr/testify/assert" 25 | "github.com/stretchr/testify/require" 26 | ) 27 | 28 | func TestEmptyConfigImageDigest(t *testing.T) { 29 | empty := empty.Image 30 | img := EmptyConfigImage{Image: empty} 31 | mf, err := img.RawManifest() 32 | require.NoError(t, err) 33 | hash := util.SHA256Hex(mf) 34 | digest, err := img.Digest() 35 | require.NoError(t, err) 36 | assert.Equal(t, digest.Hex, hash) 37 | } 38 | -------------------------------------------------------------------------------- /test/testdata/local-policy-inputs/doi/policy.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | keys := [{ 6 | "id": "a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4", 7 | "key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgH23D1i2+ZIOtVjmfB7iFvX8AhVN\n9CPJ4ie9axw+WRHozGnRy99U2dRge3zueBBg2MweF0zrToXGig2v3YOrdw==\n-----END PUBLIC KEY-----", 8 | "from": "2023-12-15T14:00:00Z", 9 | "to": null, 10 | "status": "active", 11 | "signing-format": "dssev1", 12 | }] 13 | 14 | provs(pred) := p if { 15 | res := attest.fetch(pred) 16 | not res.error 17 | p := res.value 18 | } 19 | 20 | atts := union({ 21 | provs("https://slsa.dev/provenance/v0.2"), 22 | provs("https://spdx.dev/Document"), 23 | }) 24 | 25 | success if { 26 | input.domain == "docker.io" 27 | input.familiar_name == "test-image" 28 | input.normalized_name == "library/test-image" 29 | input.platform == "linux/amd64" 30 | input.tag == "test" 31 | } 32 | 33 | result := { 34 | "success": success, 35 | "violations": set(), 36 | "attestations": set(), 37 | "summary": { 38 | "subjects": set(), 39 | "slsa_level": "SLSA_BUILD_LEVEL_3", 40 | "verifier": "docker-official-images", 41 | "policy_uri": "https://docker.com/official/policy/v0.1", 42 | }, 43 | } 44 | -------------------------------------------------------------------------------- /attestation/attestation_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package attestation_test 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/docker/attest/attestation" 23 | "github.com/docker/attest/internal/test" 24 | intoto "github.com/in-toto/in-toto-golang/in_toto" 25 | "github.com/stretchr/testify/assert" 26 | ) 27 | 28 | const ExpectedStatements = 4 29 | 30 | func TestExtractAnnotatedStatements(t *testing.T) { 31 | statements, err := attestation.ExtractAnnotatedStatements(test.UnsignedTestIndex(".."), intoto.PayloadType) 32 | assert.NoError(t, err) 33 | assert.Equalf(t, len(statements), ExpectedStatements, "expected %d statement, got %d", ExpectedStatements, len(statements)) 34 | } 35 | -------------------------------------------------------------------------------- /internal/git/git_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package git 18 | 19 | import ( 20 | "context" 21 | "os" 22 | "os/exec" 23 | "testing" 24 | 25 | "github.com/stretchr/testify/require" 26 | ) 27 | 28 | func TestArchiveWithInvalidGitCommand(t *testing.T) { 29 | originalGitCommand := GitCommand 30 | GitCommand = "invalid-git-command" 31 | defer func() { GitCommand = originalGitCommand }() 32 | 33 | tempDir, err := os.MkdirTemp("", "gitrepo") 34 | if err != nil { 35 | t.Fatalf("failed to create temp dir: %v", err) 36 | } 37 | defer os.RemoveAll(tempDir) 38 | 39 | ctx := context.Background() 40 | _, err = Archive(ctx, tempDir, "") 41 | require.ErrorIs(t, err, exec.ErrNotFound) 42 | } 43 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo/metadata/2.testing.json: -------------------------------------------------------------------------------- 1 | { 2 | "signatures": [ 3 | { 4 | "keyid": "beac53949c4cf075824edede7d41715941f524db247d1b455a2389d7490ecd72", 5 | "sig": "304502207ffc26ed83118f9aa0e0c7d6cad1cbcca7ffedc1cdfa7d1c5d6bc589ee1586c502210091bf85dfbe58b300af02922e28878a135767a07a7ed93e3f169d418e5b03dcd0" 6 | } 7 | ], 8 | "signed": { 9 | "_type": "targets", 10 | "expires": "2025-09-09T14:38:32Z", 11 | "spec_version": "1.0.31", 12 | "targets": { 13 | "testing/always-fail.rego": { 14 | "hashes": { 15 | "sha256": "e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac" 16 | }, 17 | "length": 364 18 | }, 19 | "testing/jonnystoten2.rego": { 20 | "hashes": { 21 | "sha256": "bc46e8c31646f166a9efbd14fef154dd84cf07efc95c96be3a201c84470dcbc1" 22 | }, 23 | "length": 5857 24 | }, 25 | "testing/mapping.yaml": { 26 | "hashes": { 27 | "sha256": "d3b20bd505b925e6b4b73dd875e9c5839e1797061049e243bdb0d70d62f6d090" 28 | }, 29 | "length": 269 30 | }, 31 | "testing/test-only.rego": { 32 | "hashes": { 33 | "sha256": "93a0c6a57652e182f3e04fed6e3bd0eedeb98c624af12668bc9e2741c7443374" 34 | }, 35 | "length": 467 36 | } 37 | }, 38 | "version": 2, 39 | "x-tuf-on-ci-expiry-period": 365, 40 | "x-tuf-on-ci-signing-period": 60 41 | } 42 | } -------------------------------------------------------------------------------- /test/testdata/local-policy-no-tl/policy.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | keys := [{ 6 | "id": "6b241993defaba26558c64f94a94303ce860e7ad9163d801495c91cf57197c75", 7 | "key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZmicqYSY38DprGr42jU0V3ND0ROj\nzSRH1+yjsxhh0bi52Hh/DuOhrSq2KJ5a09lW3ybnDjljowbkof0Y1i9Oow==\n-----END PUBLIC KEY-----", 8 | "from": "2023-12-15T14:00:00Z", 9 | "to": null, 10 | # this key is still active 11 | "status": "active", 12 | "signing-format": "dssev1", 13 | }] 14 | 15 | provs(pred) := p if { 16 | res := attest.fetch(pred) 17 | not res.error 18 | p := res.value 19 | } 20 | 21 | atts := union({ 22 | provs("https://slsa.dev/provenance/v0.2"), 23 | provs("https://spdx.dev/Document"), 24 | }) 25 | 26 | opts := {"keys": keys, "skip_tl": true} 27 | 28 | statements contains s if { 29 | some att in atts 30 | res := attest.verify(att, opts) 31 | not res.error 32 | s := res.value 33 | } 34 | 35 | subjects contains subject if { 36 | some statement in statements 37 | some subject in statement.subject 38 | } 39 | 40 | result := { 41 | "success": true, 42 | "violations": set(), 43 | "summary": { 44 | "subjects": subjects, 45 | "slsa_levels": ["SLSA_BUILD_LEVEL_3"], 46 | "verifier": "docker-official-images", 47 | "policy_uri": "https://docker.com/official/policy/v0.1", 48 | }, 49 | } 50 | -------------------------------------------------------------------------------- /test/testdata/local-policy/doi/policy.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | keys := [{ 6 | "id": "a0c296026645799b2a297913878e81b0aefff2a0c301e97232f717e14402f3e4", 7 | "key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgH23D1i2+ZIOtVjmfB7iFvX8AhVN\n9CPJ4ie9axw+WRHozGnRy99U2dRge3zueBBg2MweF0zrToXGig2v3YOrdw==\n-----END PUBLIC KEY-----", 8 | "from": "2023-12-15T14:00:00Z", 9 | "to": null, 10 | "status": "active", 11 | "signing-format": "dssev1", 12 | }] 13 | 14 | provs(pred) := p if { 15 | res := attest.fetch(pred) 16 | not res.error 17 | p := res.value 18 | } 19 | 20 | atts := union({ 21 | provs("https://slsa.dev/provenance/v0.2"), 22 | provs("https://spdx.dev/Document"), 23 | }) 24 | 25 | opts := {"keys": keys, "skip_tl": false} 26 | 27 | statements contains s if { 28 | some att in atts 29 | res := attest.verify(att, opts) 30 | not res.error 31 | s := res.value 32 | } 33 | 34 | subjects contains subject if { 35 | some statement in statements 36 | some subject in statement.subject 37 | } 38 | 39 | result := { 40 | "success": count(atts) > 0, 41 | "violations": set(), 42 | "attestations": statements, 43 | "summary": { 44 | "subjects": subjects, 45 | "slsa_level": "SLSA_BUILD_LEVEL_3", 46 | "verifier": "docker-official-images", 47 | "policy_uri": "https://docker.com/official/policy/v0.1", 48 | }, 49 | } 50 | -------------------------------------------------------------------------------- /.github/release-drafter-config.yml: -------------------------------------------------------------------------------- 1 | name-template: "v$RESOLVED_VERSION" 2 | tag-template: "v$RESOLVED_VERSION" 3 | categories: 4 | - title: "🚀 Features" 5 | labels: 6 | - "feat" 7 | - "feature" 8 | - "enhancement" 9 | - title: "🐛 Bug Fixes" 10 | labels: 11 | - "fix" 12 | - "bugfix" 13 | - "bug" 14 | - title: "🧰 Maintenance" 15 | labels: 16 | - "chore" 17 | - title: "💥 Breaking Changes" 18 | labels: 19 | - "breaking" 20 | 21 | change-template: "- $TITLE @$AUTHOR (#$NUMBER)" 22 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. 23 | version-resolver: 24 | major: 25 | labels: 26 | - "major" 27 | - "breaking" 28 | minor: 29 | labels: 30 | - "minor" 31 | patch: 32 | labels: 33 | - "patch" 34 | default: patch 35 | template: | 36 | ## Changes 37 | 38 | $CHANGES 39 | 40 | autolabeler: 41 | - label: "chore" 42 | files: 43 | - "*.md" 44 | title: 45 | - "/^docs!?:/i" 46 | - "/^test!?:/i" 47 | - "/^chore!?:/i" 48 | - "/^refactor!?:/i" 49 | - label: "bug" 50 | title: 51 | - "/^fix!?:/i" 52 | - "/^revert!?:/i" 53 | - label: "feature" 54 | title: 55 | - "/^feat!?:/i" 56 | - "/^add!?:/i" 57 | - label: "breaking" 58 | title: 59 | - "/^[a-zA-Z]+!:/i" 60 | -------------------------------------------------------------------------------- /policy/testdata/policies/test/git_checksum/git_checksum_test.rego: -------------------------------------------------------------------------------- 1 | package git_checksum_test 2 | 3 | import rego.v1 4 | 5 | test_reproducible_git_checksum if { 6 | # test case from https://github.com/docker-library/meta/blob/5c3af85f2c735ea2b689271cb64ff38bcca28bec/builds.json 7 | # build id: e1dc43214da28419a105a665f994080e83093c6849fe2851344350b8c264afd1 8 | # grab with `curl https://raw.githubusercontent.com/docker-library/meta/5c3af85f2c735ea2b689271cb64ff38bcca28bec/builds.json | jq '."e1dc43214da28419a105a665f994080e83093c6849fe2851344350b8c264afd1"'` 9 | 10 | repo := "https://github.com/docker-library/busybox.git" 11 | commit := "91f9975d4bb91d7c916ef74de77911d961ac9b75" 12 | dir := "latest/glibc/amd64" 13 | expected_checksum := "48d47b7ee1617a53291a76942cd240773fbb59daaa874007c6d16cb3125d63c2" 14 | 15 | result := attest.internals.reproducible_git_checksum(repo, commit, dir) 16 | actual_checksum := result.value 17 | actual_checksum == expected_checksum 18 | 19 | invalid_commit := "0000000000000000000000000000000000000000" 20 | bad_commit_result := attest.internals.reproducible_git_checksum(repo, invalid_commit, dir) 21 | contains(bad_commit_result.error, "failed to fetch") 22 | 23 | invalid_dir := "not_a_real_dir" 24 | bad_dir_result := attest.internals.reproducible_git_checksum(repo, commit, invalid_dir) 25 | contains(bad_dir_result.error, "not a valid object name") 26 | } 27 | -------------------------------------------------------------------------------- /oci/authn.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package oci 18 | 19 | import ( 20 | "io" 21 | 22 | ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login" 23 | "github.com/google/go-containerregistry/pkg/authn" 24 | "github.com/google/go-containerregistry/pkg/v1/google" 25 | "github.com/google/go-containerregistry/pkg/v1/remote" 26 | ) 27 | 28 | func MultiKeychainOption() remote.Option { 29 | return remote.WithAuthFromKeychain(MultiKeychainAll()) 30 | } 31 | 32 | func MultiKeychainAll() authn.Keychain { 33 | // Create a multi-keychain that will use the default Docker, Google, or ECR keychain 34 | return authn.NewMultiKeychain( 35 | authn.DefaultKeychain, 36 | google.Keychain, 37 | authn.NewKeychainFromHelper(ecr.NewECRHelper(ecr.WithLogger(io.Discard))), 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /test/testdata/local-policy-mirror/policy.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | keys := [{ 6 | "id": "6b241993defaba26558c64f94a94303ce860e7ad9163d801495c91cf57197c75", 7 | "key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZmicqYSY38DprGr42jU0V3ND0ROj\nzSRH1+yjsxhh0bi52Hh/DuOhrSq2KJ5a09lW3ybnDjljowbkof0Y1i9Oow==\n-----END PUBLIC KEY-----", 8 | "from": "2023-12-15T14:00:00Z", 9 | "to": null, 10 | # this key is still active 11 | "status": "active", 12 | "signing-format": "dssev1", 13 | }] 14 | 15 | provs(pred) := p if { 16 | res := attest.fetch(pred) 17 | not res.error 18 | p := res.value 19 | } 20 | 21 | atts := union({ 22 | provs("https://slsa.dev/provenance/v0.2"), 23 | provs("https://spdx.dev/Document"), 24 | }) 25 | 26 | opts := {"keys": keys} 27 | 28 | statements contains s if { 29 | some att in atts 30 | res := attest.verify(att, opts) 31 | not res.error 32 | s := res.value 33 | } 34 | 35 | subjects contains subject if { 36 | some statement in statements 37 | some subject in statement.subject 38 | } 39 | 40 | success if { 41 | # print("input:",input) 42 | true 43 | } 44 | 45 | result := { 46 | "success": success, 47 | "violations": set(), 48 | "summary": { 49 | "subjects": subjects, 50 | "slsa_levels": ["SLSA_BUILD_LEVEL_3"], 51 | "verifier": "docker-official-images", 52 | "policy_uri": "https://docker.com/official/policy/v0.1", 53 | }, 54 | } 55 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/targets/testing/index.json: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.index.v1+json","manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","size":502,"digest":"sha256:0d9f576776df40330e2f646eca34a51f4a092bd23409b19824ed36c1e8ed70ac","annotations":{"tuf.io/filename":"testing/e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac.always-fail.rego"},"artifactType":"application/vnd.oci.empty.v1+json"},{"mediaType":"application/vnd.oci.image.manifest.v1+json","size":504,"digest":"sha256:39be48096573b49cb30ce5479d25c49a3405e8495daa9066e813e96338a17f48","annotations":{"tuf.io/filename":"testing/bc46e8c31646f166a9efbd14fef154dd84cf07efc95c96be3a201c84470dcbc1.jonnystoten2.rego"},"artifactType":"application/vnd.oci.empty.v1+json"},{"mediaType":"application/vnd.oci.image.manifest.v1+json","size":498,"digest":"sha256:b846de84908dbf583e3b7e7fbd95cf2c5ffc3c0c92e19ef7be6859df3c5397a3","annotations":{"tuf.io/filename":"testing/d3b20bd505b925e6b4b73dd875e9c5839e1797061049e243bdb0d70d62f6d090.mapping.yaml"},"artifactType":"application/vnd.oci.empty.v1+json"},{"mediaType":"application/vnd.oci.image.manifest.v1+json","size":500,"digest":"sha256:db3d6f0ce76f0fa388b83f4928620a7d532ab386a954dd997bdf9318aa5d0b79","annotations":{"tuf.io/filename":"testing/93a0c6a57652e182f3e04fed6e3bd0eedeb98c624af12668bc9e2741c7443374.test-only.rego"},"artifactType":"application/vnd.oci.empty.v1+json"}]} -------------------------------------------------------------------------------- /mirror/mirror.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mirror 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/docker/attest/tuf" 24 | ) 25 | 26 | func NewTUFMirror(ctx context.Context, root []byte, tufPath, metadataURL, targetsURL string, versionChecker tuf.VersionChecker) (*TUFMirror, error) { 27 | if root == nil { 28 | root = tuf.DockerTUFRootDefault.Data 29 | } 30 | tufClient, err := tuf.NewClient(ctx, &tuf.ClientOptions{InitialRoot: root, LocalStorageDir: tufPath, MetadataSource: metadataURL, TargetsSource: targetsURL, VersionChecker: versionChecker}) 31 | if err != nil { 32 | return nil, fmt.Errorf("failed to create TUF client: %w", err) 33 | } 34 | return &TUFMirror{TUFClient: tufClient, tufPath: tufPath, metadataURL: metadataURL, targetsURL: targetsURL}, nil 35 | } 36 | -------------------------------------------------------------------------------- /test/testdata/local-policy-pass/policy.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | keys := [{ 6 | "id": "6b241993defaba26558c64f94a94303ce860e7ad9163d801495c91cf57197c75", 7 | "key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZmicqYSY38DprGr42jU0V3ND0ROj\nzSRH1+yjsxhh0bi52Hh/DuOhrSq2KJ5a09lW3ybnDjljowbkof0Y1i9Oow==\n-----END PUBLIC KEY-----", 8 | "from": "2023-12-15T14:00:00Z", 9 | "to": null, 10 | # this key is still active 11 | "status": "active", 12 | "signing-format": "dssev1", 13 | }] 14 | 15 | provs(pred) := p if { 16 | res := attest.fetch(pred) 17 | not res.error 18 | p := res.value 19 | } 20 | 21 | atts := union({ 22 | provs("https://slsa.dev/provenance/v0.2"), 23 | provs("https://spdx.dev/Document"), 24 | }) 25 | 26 | opts := {"keys": keys, "skip_tl": true} 27 | 28 | statements contains s if { 29 | some att in atts 30 | res := attest.verify(att, opts) 31 | not res.error 32 | s := res.value 33 | } 34 | 35 | subjects contains subject if { 36 | some statement in statements 37 | some subject in statement.subject 38 | } 39 | 40 | inputs contains desc if { 41 | some att in atts 42 | desc := att.resourceDescriptor 43 | } 44 | 45 | result := { 46 | "success": true, 47 | "violations": set(), 48 | "summary": { 49 | "subjects": subjects, 50 | "input_attestations": inputs, 51 | "slsa_levels": ["SLSA_BUILD_LEVEL_3"], 52 | "verifier": "docker-official-images", 53 | "policy_uri": "https://docker.com/official/policy/v0.1", 54 | }, 55 | } 56 | -------------------------------------------------------------------------------- /test/testdata/local-policy-real/policy.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | import data.keys 6 | 7 | provs(pred) := p if { 8 | res := attest.fetch(pred) 9 | not res.error 10 | p := res.value 11 | } 12 | 13 | atts := union({ 14 | provs("https://slsa.dev/provenance/v0.2"), 15 | provs("https://spdx.dev/Document"), 16 | }) 17 | 18 | opts := {"keys": keys, "skip_tl": true} 19 | 20 | statements contains s if { 21 | some att in atts 22 | res := attest.verify(att, opts) 23 | not res.error 24 | s := res.value 25 | } 26 | 27 | subjects contains subject if { 28 | some statement in statements 29 | some subject in statement.subject 30 | } 31 | 32 | unsafe_statement_from_attestation(att) := statement if { 33 | payload := att.payload 34 | statement := json.unmarshal(base64.decode(payload)) 35 | } 36 | 37 | violations contains violation if { 38 | some att in atts 39 | statement := unsafe_statement_from_attestation(att) 40 | res := attest.verify(att, opts) 41 | err := res.error 42 | violation := { 43 | "type": "unsigned_statement", 44 | "description": sprintf("Statement is not correctly signed: %v", [err]), 45 | "attestation": statement, 46 | "details": {"error": err}, 47 | } 48 | } 49 | 50 | result := { 51 | "success": count(statements) > 0, 52 | "violations": violations, 53 | "summary": { 54 | "subjects": subjects, 55 | "slsa_level": "SLSA_BUILD_LEVEL_3", 56 | "verifier": "docker-official-images", 57 | "policy_uri": "https://docker.com/official/policy/v0.1", 58 | }, 59 | } 60 | -------------------------------------------------------------------------------- /signerverifier/aws.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package signerverifier 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/aws/aws-sdk-go-v2/config" 24 | "github.com/secure-systems-lab/go-securesystemslib/dsse" 25 | awssigner "github.com/sigstore/sigstore/pkg/signature/kms/aws" 26 | ) 27 | 28 | // using AWS KMS. 29 | func GetAWSSigner(ctx context.Context, keyARN string, region string) (dsse.SignerVerifier, error) { 30 | keyPath := fmt.Sprintf("awskms:///%s", keyARN) 31 | sv, err := awssigner.LoadSignerVerifier(ctx, keyPath, config.WithRegion(region)) 32 | if err != nil { 33 | return nil, fmt.Errorf("error loading aws signer verifier: %w", err) 34 | } 35 | cs, _, err := sv.CryptoSigner(context.Background(), func(_ error) {}) 36 | if err != nil { 37 | return nil, fmt.Errorf("error getting aws crypto signer: %w", err) 38 | } 39 | return NewECDSASignerVerifier(cs) 40 | } 41 | -------------------------------------------------------------------------------- /useragent/useragent.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package useragent 18 | 19 | import ( 20 | "context" 21 | 22 | "github.com/docker/attest/version" 23 | ) 24 | 25 | type userAgentKeyType string 26 | 27 | const ( 28 | userAgentKey userAgentKeyType = "attest-user-agent" 29 | defaultUserAgent string = "attest/unknown (docker)" 30 | ) 31 | 32 | func Set(ctx context.Context, userAgent string) context.Context { 33 | return context.WithValue(ctx, userAgentKey, userAgent) 34 | } 35 | 36 | // Get retrieves the HTTP user agent from the context. 37 | func Get(ctx context.Context) string { 38 | fetcher := version.NewGoVersionFetcher() 39 | if ua, ok := ctx.Value(userAgentKey).(string); ok { 40 | return ua 41 | } 42 | version, err := fetcher.Get() 43 | if err != nil || version == nil { 44 | return defaultUserAgent 45 | } 46 | 47 | return "attest/" + version.String() + " (docker)" 48 | } 49 | -------------------------------------------------------------------------------- /policy/mock.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package policy 18 | 19 | import ( 20 | "context" 21 | 22 | "github.com/docker/attest/attestation" 23 | ) 24 | 25 | type MockPolicyEvaluator struct { 26 | EvaluateFunc func(ctx context.Context, resolver attestation.Resolver, pctx *Policy, input *Input) (*Result, error) 27 | } 28 | 29 | func (pe *MockPolicyEvaluator) Evaluate(ctx context.Context, resolver attestation.Resolver, pctx *Policy, input *Input) (*Result, error) { 30 | if pe.EvaluateFunc != nil { 31 | return pe.EvaluateFunc(ctx, resolver, pctx, input) 32 | } 33 | return AllowedResult(), nil 34 | } 35 | 36 | func GetMockPolicy() Evaluator { 37 | return &MockPolicyEvaluator{ 38 | EvaluateFunc: func(_ context.Context, _ attestation.Resolver, _ *Policy, _ *Input) (*Result, error) { 39 | return AllowedResult(), nil 40 | }, 41 | } 42 | } 43 | 44 | func AllowedResult() *Result { 45 | return &Result{ 46 | Success: true, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/testdata/local-policy-fail/policy.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | keys := [{ 6 | "id": "6b241993defaba26558c64f94a94303ce860e7ad9163d801495c91cf57197c75", 7 | "key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZmicqYSY38DprGr42jU0V3ND0ROj\nzSRH1+yjsxhh0bi52Hh/DuOhrSq2KJ5a09lW3ybnDjljowbkof0Y1i9Oow==\n-----END PUBLIC KEY-----", 8 | "from": "2023-12-15T14:00:00Z", 9 | "to": null, 10 | # this key is still active 11 | "status": "active", 12 | "signing-format": "dssev1", 13 | }] 14 | 15 | provs(pred) := p if { 16 | res := attest.fetch(pred) 17 | not res.error 18 | p := res.value 19 | } 20 | 21 | atts := union({ 22 | provs("https://slsa.dev/provenance/v0.2"), 23 | provs("https://spdx.dev/Document"), 24 | }) 25 | 26 | opts := {"keys": keys, "skip_tl": true} 27 | 28 | statements contains s if { 29 | some att in atts 30 | res := attest.verify(att, opts) 31 | not res.error 32 | s := res.value 33 | } 34 | 35 | subjects contains subject if { 36 | some statement in statements 37 | some subject in statement.subject 38 | } 39 | 40 | violations contains v if { 41 | v := { 42 | "type": "missing_attestation", 43 | "description": "Attestation missing for subject", 44 | "attestation": null, 45 | "details": {}, 46 | } 47 | } 48 | 49 | result := { 50 | "success": false, 51 | "violations": violations, 52 | "summary": { 53 | "subjects": subjects, 54 | "slsa_levels": ["SLSA_BUILD_LEVEL_3"], 55 | "verifier": "docker-official-images", 56 | "policy_uri": "https://docker.com/official/policy/v0.1", 57 | }, 58 | } 59 | -------------------------------------------------------------------------------- /test/testdata/local-policy-param/policy.rego: -------------------------------------------------------------------------------- 1 | package attest 2 | 3 | import rego.v1 4 | 5 | import data.keys 6 | import input.parameters 7 | 8 | provs(pred) := p if { 9 | res := attest.fetch(pred) 10 | not res.error 11 | p := res.value 12 | } 13 | 14 | atts := union({ 15 | provs("https://slsa.dev/provenance/v0.2"), 16 | provs("https://spdx.dev/Document"), 17 | }) 18 | 19 | opts := {"keys": keys, "skip_tl": true} 20 | 21 | statements contains s if { 22 | parameters.foo == "bar" 23 | some att in atts 24 | res := attest.verify(att, opts) 25 | not res.error 26 | s := res.value 27 | } 28 | 29 | subjects contains subject if { 30 | some statement in statements 31 | some subject in statement.subject 32 | } 33 | 34 | unsafe_statement_from_attestation(att) := statement if { 35 | payload := att.payload 36 | statement := json.unmarshal(base64.decode(payload)) 37 | } 38 | 39 | violations contains violation if { 40 | some att in atts 41 | statement := unsafe_statement_from_attestation(att) 42 | res := attest.verify(att, opts) 43 | err := res.error 44 | violation := { 45 | "type": "unsigned_statement", 46 | "description": sprintf("Statement is not correctly signed: %v", [err]), 47 | "attestation": statement, 48 | "details": {"error": err}, 49 | } 50 | } 51 | 52 | result := { 53 | "success": count(statements) > 0, 54 | "violations": violations, 55 | "summary": { 56 | "subjects": subjects, 57 | "slsa_level": "SLSA_BUILD_LEVEL_3", 58 | "verifier": "docker-official-images", 59 | "policy_uri": "https://docker.com/official/policy/v0.1", 60 | }, 61 | } 62 | -------------------------------------------------------------------------------- /oci/authn_test.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | 3 | /* 4 | Copyright Docker attest authors 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package oci_test 20 | 21 | import ( 22 | "context" 23 | "testing" 24 | 25 | "github.com/docker/attest/internal/test" 26 | "github.com/docker/attest/oci" 27 | "github.com/stretchr/testify/require" 28 | ) 29 | 30 | func TestRegistryAuth(t *testing.T) { 31 | attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex("..")) 32 | require.NoError(t, err) 33 | // test cases for ecr, gcr and dockerhub 34 | testCases := []struct { 35 | Image string 36 | }{ 37 | {Image: "175142243308.dkr.ecr.us-east-1.amazonaws.com/e2e-test-image:latest"}, 38 | {Image: "docker/image-signer-verifier-test:latest"}, 39 | } 40 | ctx := context.Background() 41 | for _, tc := range testCases { 42 | t.Run(tc.Image, func(t *testing.T) { 43 | err := oci.PushIndexToRegistry(ctx, attIdx.Index, tc.Image) 44 | require.NoError(t, err) 45 | _, err = oci.IndexFromRemote(ctx, tc.Image) 46 | require.NoError(t, err) 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /signerverifier/gcp.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package signerverifier 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/secure-systems-lab/go-securesystemslib/dsse" 24 | gcpsigner "github.com/sigstore/sigstore/pkg/signature/kms/gcp" 25 | "google.golang.org/api/option" 26 | ) 27 | 28 | // using GCP KMS 29 | // reference should be in the format projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEY_RING]/cryptoKeys/[KEY]/cryptoKeyVersions/[VERSION]. 30 | func GetGCPSigner(ctx context.Context, reference string, opts ...option.ClientOption) (dsse.SignerVerifier, error) { 31 | reference = fmt.Sprintf("gcpkms://%s", reference) 32 | sv, err := gcpsigner.LoadSignerVerifier(ctx, reference, opts...) 33 | if err != nil { 34 | return nil, fmt.Errorf("error loading gcp signer verifier: %w", err) 35 | } 36 | cs, _, err := sv.CryptoSigner(ctx, func(_ error) {}) 37 | if err != nil { 38 | return nil, fmt.Errorf("error getting gcp crypto signer: %w", err) 39 | } 40 | return NewECDSASignerVerifier(cs) 41 | } 42 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/blobs/sha256/e83d550964be04addfc119b45b8dd80914babd5e5f0529b3106d6f18f74afc3a: -------------------------------------------------------------------------------- 1 | {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","data":"e30="},"layers":[{"mediaType":"application/vnd.tuf.metadata+json","size":2202,"digest":"sha256:5a9f60b64b708d05e4e4da0354529fc7fe5015807b79f0bf7b136207bf952bd7","annotations":{"tuf.io/filename":"1.root.json"}},{"mediaType":"application/vnd.tuf.metadata+json","size":2856,"digest":"sha256:832485119c0195acdcd2c7d555f55565be54e658c2e8de3adccf4e2d0c92e536","annotations":{"tuf.io/filename":"2.root.json"}},{"mediaType":"application/vnd.tuf.metadata+json","size":3506,"digest":"sha256:4f2b6b008a82518eace3f053d04bd5fbd2059453df992bfda9e5caa46e095502","annotations":{"tuf.io/filename":"3.root.json"}},{"mediaType":"application/vnd.tuf.metadata+json","size":3128,"digest":"sha256:3debf3f541b67760dc37ac1f82a7e0fc86cb5fc3d4f4f9c45ca7d38e55beca7b","annotations":{"tuf.io/filename":"4.root.json"}},{"mediaType":"application/vnd.tuf.metadata+json","size":473,"digest":"sha256:640c0d21bbc7c99717feee6c74ff65e7099e4dc21a30f985f18d6e5bd205502d","annotations":{"tuf.io/filename":"11.snapshot.json"}},{"mediaType":"application/vnd.tuf.metadata+json","size":2390,"digest":"sha256:a00c1b266ea6b992a8b6fa87ab8a67232f4319d9e3dd0e63365e73114a2c7869","annotations":{"tuf.io/filename":"11.targets.json"}},{"mediaType":"application/vnd.tuf.metadata+json","size":385,"digest":"sha256:5556a0398a04564261ccc7b548d670792f2086c496322c4e95d898686e8b4811","annotations":{"tuf.io/filename":"timestamp.json"}}]} -------------------------------------------------------------------------------- /tuf/example_registry_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package tuf_test 18 | 19 | import ( 20 | "context" 21 | "os" 22 | "path/filepath" 23 | 24 | "github.com/docker/attest/tuf" 25 | "github.com/theupdateframework/go-tuf/v2/metadata" 26 | ) 27 | 28 | func ExampleNewClient_registry() { 29 | // create a tuf client 30 | home, err := os.UserHomeDir() 31 | if err != nil { 32 | panic(err) 33 | } 34 | tufOutputPath := filepath.Join(home, ".docker", "tuf") 35 | 36 | opts := tuf.NewDockerDefaultClientOptions(tufOutputPath) 37 | registryClient, err := tuf.NewClient(context.Background(), opts) 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | // get trusted tuf metadata 43 | trustedMetadata := registryClient.GetMetadata() 44 | 45 | // top-level target files 46 | targets := trustedMetadata.Targets[metadata.TARGETS].Signed.Targets 47 | 48 | for _, t := range targets { 49 | // download target files 50 | _, err := registryClient.DownloadTarget(t.Path, filepath.Join(tufOutputPath, "download")) 51 | if err != nil { 52 | panic(err) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tuf/mock.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package tuf 18 | 19 | import ( 20 | "io" 21 | "os" 22 | "path/filepath" 23 | 24 | "github.com/docker/attest/internal/util" 25 | ) 26 | 27 | type MockTufClient struct { 28 | srcPath string 29 | } 30 | 31 | func NewMockTufClient(srcPath string) *MockTufClient { 32 | if srcPath == "" { 33 | panic("srcPath must be set") 34 | } 35 | return &MockTufClient{ 36 | srcPath: srcPath, 37 | } 38 | } 39 | 40 | func (dc *MockTufClient) DownloadTarget(target string, _ string) (file *TargetFile, err error) { 41 | targetPath := filepath.Join(dc.srcPath, target) 42 | src, err := os.Open(targetPath) 43 | if err != nil { 44 | return nil, err 45 | } 46 | defer src.Close() 47 | 48 | b, err := io.ReadAll(src) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | return &TargetFile{TargetURI: targetPath, Data: b, Digest: util.SHA256Hex(b)}, nil 54 | } 55 | 56 | type MockVersionChecker struct { 57 | err error 58 | } 59 | 60 | func NewMockVersionChecker() *MockVersionChecker { 61 | return &MockVersionChecker{} 62 | } 63 | 64 | func (vc *MockVersionChecker) CheckVersion(_ Downloader) error { 65 | return vc.err 66 | } 67 | -------------------------------------------------------------------------------- /attestation/types_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package attestation 18 | 19 | import ( 20 | "fmt" 21 | "testing" 22 | 23 | intoto "github.com/in-toto/in-toto-golang/in_toto" 24 | v02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2" 25 | slsav1 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" 26 | "github.com/stretchr/testify/assert" 27 | "github.com/stretchr/testify/require" 28 | ) 29 | 30 | func TestDSSEMediaType(t *testing.T) { 31 | testcases := []struct { 32 | name string 33 | expected string 34 | }{ 35 | { 36 | name: slsav1.PredicateSLSAProvenance, 37 | expected: "provenance", 38 | }, 39 | { 40 | name: v02.PredicateSLSAProvenance, 41 | expected: "provenance", 42 | }, 43 | { 44 | name: intoto.PredicateSPDX, 45 | expected: "spdx", 46 | }, 47 | { 48 | name: VSAPredicateType, 49 | expected: "verification_summary", 50 | }, 51 | } 52 | 53 | for _, tc := range testcases { 54 | t.Run(tc.name, func(t *testing.T) { 55 | mt, err := DSSEMediaType(tc.name) 56 | require.NoError(t, err) 57 | assert.Equal(t, fmt.Sprintf("application/vnd.in-toto.%s+dsse", tc.expected), mt) 58 | }) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /internal/embed/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package embed 18 | 19 | import ( 20 | _ "embed" 21 | "fmt" 22 | ) 23 | 24 | //go:embed embedded-roots/1.root-dev.json 25 | var devRoot []byte 26 | 27 | //go:embed embedded-roots/1.root-staging.json 28 | var stagingRoot []byte 29 | 30 | //go:embed embedded-roots/1.root.json 31 | var prodRoot []byte 32 | 33 | var defaultRoot = prodRoot 34 | 35 | type ( 36 | RootName string 37 | EmbeddedRoot struct { 38 | Data []byte 39 | Name RootName 40 | } 41 | ) 42 | 43 | var ( 44 | RootDev = EmbeddedRoot{Data: devRoot, Name: "dev"} 45 | RootStaging = EmbeddedRoot{Data: stagingRoot, Name: "staging"} 46 | RootProd = EmbeddedRoot{Data: prodRoot, Name: "prod"} 47 | RootDefault = EmbeddedRoot{Data: defaultRoot, Name: ""} 48 | ) 49 | 50 | func GetRootFromName(root string) (*EmbeddedRoot, error) { 51 | switch root { 52 | case string(RootDev.Name): 53 | return &RootDev, nil 54 | case string(RootStaging.Name): 55 | return &RootStaging, nil 56 | case string(RootProd.Name): 57 | return &RootProd, nil 58 | case string(RootDefault.Name): 59 | return &RootDefault, nil 60 | default: 61 | return nil, fmt.Errorf("invalid tuf root: %s", root) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /sign.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package attest 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/docker/attest/attestation" 24 | v1 "github.com/google/go-containerregistry/pkg/v1" 25 | "github.com/secure-systems-lab/go-securesystemslib/dsse" 26 | ) 27 | 28 | // this is only relevant if there are (unsigned) in-toto statements. 29 | func SignStatements(ctx context.Context, idx v1.ImageIndex, signer dsse.SignerVerifier, opts *attestation.SigningOptions) ([]*attestation.Manifest, error) { 30 | // extract attestation manifests from index 31 | attestationManifests, err := attestation.ManifestsFromIndex(idx) 32 | if err != nil { 33 | return nil, fmt.Errorf("failed to load attestation manifests from index: %w", err) 34 | } 35 | // sign every attestation layer in each manifest 36 | for _, manifest := range attestationManifests { 37 | for _, layer := range manifest.OriginalLayers { 38 | // skip layers without statements 39 | if layer.Statement != nil { 40 | err = manifest.Add(ctx, signer, layer.Statement, opts) 41 | if err != nil { 42 | return nil, fmt.Errorf("failed to sign attestation layer %w", err) 43 | } 44 | } 45 | } 46 | } 47 | return attestationManifests, nil 48 | } 49 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package attest 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/docker/attest/attestation" 24 | "github.com/docker/attest/policy" 25 | v1 "github.com/google/go-containerregistry/pkg/v1" 26 | intoto "github.com/in-toto/in-toto-golang/in_toto" 27 | ) 28 | 29 | type Outcome string 30 | 31 | const ( 32 | OutcomeSuccess Outcome = "success" 33 | OutcomeFailure Outcome = "failure" 34 | OutcomeNoPolicy Outcome = "no_policy" 35 | ) 36 | 37 | func (o Outcome) StringForVSA() (string, error) { 38 | switch o { 39 | case OutcomeSuccess: 40 | return "PASSED", nil 41 | case OutcomeFailure: 42 | return "FAILED", nil 43 | default: 44 | return "", fmt.Errorf("unknown outcome: %s", o) 45 | } 46 | } 47 | 48 | type VerificationResult struct { 49 | Outcome Outcome 50 | Policy *policy.Policy 51 | Input *policy.Input 52 | VSA *intoto.Statement 53 | Violations []policy.Violation 54 | SubjectDescriptor *v1.Descriptor 55 | } 56 | 57 | type wrappedResolver struct { 58 | imageName string 59 | attestation.Resolver 60 | } 61 | 62 | func (w *wrappedResolver) ImageName(_ context.Context) (string, error) { 63 | return w.imageName, nil 64 | } 65 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package version 18 | 19 | import ( 20 | "fmt" 21 | "runtime/debug" 22 | 23 | "github.com/Masterminds/semver/v3" 24 | ) 25 | 26 | const ThisModulePath = "github.com/docker/attest" 27 | 28 | type Fetcher interface { 29 | Get() (*semver.Version, error) 30 | } 31 | 32 | type GoModVersionFetcher struct{} 33 | 34 | func NewGoVersionFetcher() *GoModVersionFetcher { 35 | return &GoModVersionFetcher{} 36 | } 37 | 38 | // Get returns the version of the attest module. 39 | // this can return nil if the version can't be determined (without an error). 40 | func (*GoModVersionFetcher) Get() (*semver.Version, error) { 41 | var attestMod *debug.Module 42 | bi, ok := debug.ReadBuildInfo() 43 | if !ok { 44 | return nil, nil 45 | } 46 | if bi.Main.Path == ThisModulePath { 47 | attestMod = &bi.Main 48 | } else { 49 | for _, dep := range bi.Deps { 50 | if dep.Path == ThisModulePath { 51 | attestMod = dep 52 | break 53 | } 54 | } 55 | } 56 | if attestMod == nil { 57 | return nil, nil 58 | } 59 | 60 | attestVersion, err := semver.NewVersion(attestMod.Version) 61 | if err != nil { 62 | return nil, fmt.Errorf("failed to parse version %s: %w", attestMod.Version, err) 63 | } 64 | return attestVersion, nil 65 | } 66 | -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/0f2ee9a338149a5a1435a7383582e5ef981b8a6bb7415d07d8d70c90d8cfd326: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 7 | "digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620", 8 | "size": 476, 9 | "platform": { 10 | "architecture": "amd64", 11 | "os": "linux" 12 | } 13 | }, 14 | { 15 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 16 | "digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e", 17 | "size": 476, 18 | "platform": { 19 | "architecture": "arm64", 20 | "os": "linux" 21 | } 22 | }, 23 | { 24 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 25 | "digest": "sha256:aeca14119e3242c51633a899438518217417e01414d18189a3cf71c07f2a02c3", 26 | "size": 836, 27 | "annotations": { 28 | "vnd.docker.reference.digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620", 29 | "vnd.docker.reference.type": "attestation-manifest" 30 | }, 31 | "platform": { 32 | "architecture": "unknown", 33 | "os": "unknown" 34 | } 35 | }, 36 | { 37 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 38 | "digest": "sha256:6658b8ba1e1221a6288bf50cd7813f814e2baad70141a3e315b7c3476b0f476f", 39 | "size": 836, 40 | "annotations": { 41 | "vnd.docker.reference.digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e", 42 | "vnd.docker.reference.type": "attestation-manifest" 43 | }, 44 | "platform": { 45 | "architecture": "unknown", 46 | "os": "unknown" 47 | } 48 | } 49 | ] 50 | } -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/6c3da8eeaba64ce5acfcbaeb1f2c06af73879adba0fcb4743339c9a54b377635: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 7 | "digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620", 8 | "size": 476, 9 | "platform": { 10 | "architecture": "amd64", 11 | "os": "linux" 12 | } 13 | }, 14 | { 15 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 16 | "digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e", 17 | "size": 476, 18 | "platform": { 19 | "architecture": "arm64", 20 | "os": "linux" 21 | } 22 | }, 23 | { 24 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 25 | "digest": "sha256:9638ca53d2795806cf51b7461575c51e4a626a091dc2842b35cac18c787ff80f", 26 | "size": 836, 27 | "annotations": { 28 | "vnd.docker.reference.digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620", 29 | "vnd.docker.reference.type": "attestation-manifest" 30 | }, 31 | "platform": { 32 | "architecture": "unknown", 33 | "os": "unknown" 34 | } 35 | }, 36 | { 37 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 38 | "digest": "sha256:8049aa9ad3479085066b31d02b74310803129c3eb1e22d2e62279f8c72340b55", 39 | "size": 836, 40 | "annotations": { 41 | "vnd.docker.reference.digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e", 42 | "vnd.docker.reference.type": "attestation-manifest" 43 | }, 44 | "platform": { 45 | "architecture": "unknown", 46 | "os": "unknown" 47 | } 48 | } 49 | ] 50 | } -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/98e06f6b48edd74e21e8504c5538aec56315874a6db860fbf6874cd7a830e3c8: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 7 | "digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620", 8 | "size": 476, 9 | "platform": { 10 | "architecture": "amd64", 11 | "os": "linux" 12 | } 13 | }, 14 | { 15 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 16 | "digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e", 17 | "size": 476, 18 | "platform": { 19 | "architecture": "arm64", 20 | "os": "linux" 21 | } 22 | }, 23 | { 24 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 25 | "digest": "sha256:3883faf6acc3cae029364ed17ec2ce917fc9a500aab72f813d26fed8404e7162", 26 | "size": 836, 27 | "annotations": { 28 | "vnd.docker.reference.digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620", 29 | "vnd.docker.reference.type": "attestation-manifest" 30 | }, 31 | "platform": { 32 | "architecture": "unknown", 33 | "os": "unknown" 34 | } 35 | }, 36 | { 37 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 38 | "digest": "sha256:f2b95cecafef9c22a5d059fac8f20e3645a45370e52abf9581dd4eedd152fce0", 39 | "size": 836, 40 | "annotations": { 41 | "vnd.docker.reference.digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e", 42 | "vnd.docker.reference.type": "attestation-manifest" 43 | }, 44 | "platform": { 45 | "architecture": "unknown", 46 | "os": "unknown" 47 | } 48 | } 49 | ] 50 | } -------------------------------------------------------------------------------- /test/testdata/unsigned-index/blobs/sha256/db8f2a6e112ea6396f57d073269ecfac61e8dcdad3a4a643dcb577522492f898: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 7 | "digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620", 8 | "size": 476, 9 | "platform": { 10 | "architecture": "amd64", 11 | "os": "linux" 12 | } 13 | }, 14 | { 15 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 16 | "digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e", 17 | "size": 476, 18 | "platform": { 19 | "architecture": "arm64", 20 | "os": "linux" 21 | } 22 | }, 23 | { 24 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 25 | "digest": "sha256:26da286bbc886aa14d191808db8fcbbd5d8ec68cf0047f954133e76d8e73d71c", 26 | "size": 836, 27 | "annotations": { 28 | "vnd.docker.reference.digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620", 29 | "vnd.docker.reference.type": "attestation-manifest" 30 | }, 31 | "platform": { 32 | "architecture": "unknown", 33 | "os": "unknown" 34 | } 35 | }, 36 | { 37 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 38 | "digest": "sha256:e0a9b9404ac2691b9b1c9ef217f22bb1e106efd5ee791640411764e1cf39ea2c", 39 | "size": 836, 40 | "annotations": { 41 | "vnd.docker.reference.digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e", 42 | "vnd.docker.reference.type": "attestation-manifest" 43 | }, 44 | "platform": { 45 | "architecture": "unknown", 46 | "os": "unknown" 47 | } 48 | } 49 | ] 50 | } -------------------------------------------------------------------------------- /test/testdata/no-provenance-index/blobs/sha256/1e3839ac14fba8c5e4db574df2046ce21a9e012e4030305cea97ad3f07f81a4a: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 2, 3 | "mediaType": "application/vnd.oci.image.index.v1+json", 4 | "manifests": [ 5 | { 6 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 7 | "digest": "sha256:7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390", 8 | "size": 476, 9 | "platform": { 10 | "architecture": "amd64", 11 | "os": "linux" 12 | } 13 | }, 14 | { 15 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 16 | "digest": "sha256:52f7a760b9322aa1af76d998763868b7d1bfec2331a2574a438ef44c92c0c46d", 17 | "size": 476, 18 | "platform": { 19 | "architecture": "arm64", 20 | "os": "linux" 21 | } 22 | }, 23 | { 24 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 25 | "digest": "sha256:059eea09507d0f904b8892ee59fcd3ddec1a637fc40fb7c83c432c6ff27e2f91", 26 | "size": 558, 27 | "annotations": { 28 | "vnd.docker.reference.digest": "sha256:7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390", 29 | "vnd.docker.reference.type": "attestation-manifest" 30 | }, 31 | "platform": { 32 | "architecture": "unknown", 33 | "os": "unknown" 34 | } 35 | }, 36 | { 37 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 38 | "digest": "sha256:0b1ee0f360b073d2f76ceed15a63e291659fbcc6c3caf3be39e437d8344b520e", 39 | "size": 558, 40 | "annotations": { 41 | "vnd.docker.reference.digest": "sha256:52f7a760b9322aa1af76d998763868b7d1bfec2331a2574a438ef44c92c0c46d", 42 | "vnd.docker.reference.type": "attestation-manifest" 43 | }, 44 | "platform": { 45 | "architecture": "unknown", 46 | "os": "unknown" 47 | } 48 | } 49 | ] 50 | } -------------------------------------------------------------------------------- /signerverifier/parse.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package signerverifier 18 | 19 | import ( 20 | "crypto" 21 | "crypto/ecdsa" 22 | "crypto/x509" 23 | "encoding/pem" 24 | "fmt" 25 | ) 26 | 27 | const pemType = "PUBLIC KEY" 28 | 29 | func ParsePublicKey(pubkeyBytes []byte) (crypto.PublicKey, error) { 30 | p, _ := pem.Decode(pubkeyBytes) 31 | if p == nil { 32 | return nil, fmt.Errorf("pubkey file does not contain any PEM data") 33 | } 34 | if p.Type != pemType { 35 | return nil, fmt.Errorf("pubkey file does not contain a public key") 36 | } 37 | return x509.ParsePKIXPublicKey(p.Bytes) 38 | } 39 | 40 | func ParseECDSAPublicKey(pubkeyBytes []byte) (*ecdsa.PublicKey, error) { 41 | pk, err := ParsePublicKey(pubkeyBytes) 42 | if err != nil { 43 | return nil, err 44 | } 45 | ecdsaPubKey, ok := pk.(*ecdsa.PublicKey) 46 | if !ok { 47 | return nil, fmt.Errorf("error public key is not an ecdsa key: %w", err) 48 | } 49 | return ecdsaPubKey, nil 50 | } 51 | 52 | func ConvertToPEM(ecdsaPubKey *ecdsa.PublicKey) ([]byte, error) { 53 | pubKeyBytes, err := x509.MarshalPKIXPublicKey(ecdsaPubKey) 54 | if err != nil { 55 | return nil, fmt.Errorf("error failed to marshal public key: %w", err) 56 | } 57 | return pem.EncodeToMemory(&pem.Block{Type: pemType, Bytes: pubKeyBytes}), nil 58 | } 59 | -------------------------------------------------------------------------------- /mirror/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mirror 18 | 19 | import ( 20 | "github.com/docker/attest/oci" 21 | "github.com/docker/attest/tuf" 22 | v1 "github.com/google/go-containerregistry/pkg/v1" 23 | "github.com/theupdateframework/go-tuf/v2/metadata" 24 | ) 25 | 26 | const ( 27 | DefaultMetadataURL = "https://docker.github.io/tuf/metadata" 28 | DefaultTargetsURL = "https://docker.github.io/tuf/targets" 29 | tufMetadataMediaType = "application/vnd.tuf.metadata+json" 30 | tufTargetMediaType = "application/vnd.tuf.target" 31 | tufFileAnnotation = "tuf.io/filename" 32 | ) 33 | 34 | type TUFRole string 35 | 36 | var TUFRoles = []TUFRole{metadata.ROOT, metadata.SNAPSHOT, metadata.TARGETS, metadata.TIMESTAMP} 37 | 38 | type TUFMetadata struct { 39 | Root map[string][]byte 40 | Snapshot map[string][]byte 41 | Targets map[string][]byte 42 | Timestamp []byte 43 | } 44 | 45 | type DelegatedTargetMetadata struct { 46 | Name string 47 | Version string 48 | Data []byte 49 | } 50 | 51 | type Image struct { 52 | Image *oci.EmptyConfigImage 53 | Tag string 54 | } 55 | 56 | type Index struct { 57 | Index v1.ImageIndex 58 | Tag string 59 | } 60 | 61 | type TUFMirror struct { 62 | TUFClient *tuf.Client 63 | tufPath string 64 | metadataURL string 65 | targetsURL string 66 | } 67 | -------------------------------------------------------------------------------- /attestation/verify_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package attestation_test 18 | 19 | import ( 20 | "encoding/base64" 21 | "testing" 22 | 23 | "github.com/docker/attest/attestation" 24 | "github.com/docker/attest/internal/test" 25 | intoto "github.com/in-toto/in-toto-golang/in_toto" 26 | ociv1 "github.com/opencontainers/image-spec/specs-go/v1" 27 | "github.com/stretchr/testify/assert" 28 | ) 29 | 30 | func TestValidPayloadType(t *testing.T) { 31 | testCases := []struct { 32 | name string 33 | payloadType string 34 | expected bool 35 | }{ 36 | {"valid in-toto payload type", intoto.PayloadType, true}, 37 | {"valid oci descriptor payload type", ociv1.MediaTypeDescriptor, true}, 38 | {"invalid payload type", "application/vnd.test.fail", false}, 39 | } 40 | 41 | for _, tc := range testCases { 42 | t.Run(tc.name, func(t *testing.T) { 43 | assert.Equalf(t, tc.expected, attestation.ValidPayloadType(tc.payloadType), "expected %v for payload type %s", tc.expected, tc.payloadType) 44 | }) 45 | } 46 | } 47 | 48 | func TestVerifyUnsignedAttestation(t *testing.T) { 49 | ctx, _ := test.Setup(t) 50 | 51 | payload := []byte("payload") 52 | env := &attestation.Envelope{ 53 | // no signatures 54 | Signatures: []*attestation.Signature{}, 55 | Payload: base64.StdEncoding.EncodeToString(payload), 56 | PayloadType: intoto.PayloadType, 57 | } 58 | opts := &attestation.VerifyOptions{ 59 | Keys: attestation.Keys{}, 60 | } 61 | _, err := attestation.VerifyDSSE(ctx, nil, env, opts) 62 | assert.Error(t, err) 63 | assert.Contains(t, err.Error(), "no signatures") 64 | } 65 | -------------------------------------------------------------------------------- /mapping/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mapping 18 | 19 | import ( 20 | "regexp" 21 | 22 | v1 "github.com/google/go-containerregistry/pkg/v1" 23 | ) 24 | 25 | type policyMappingsFile struct { 26 | Version string `json:"version"` 27 | Kind string `json:"kind"` 28 | Policies []*PolicyMapping `json:"policies"` 29 | Rules []*policyRuleFile `json:"rules"` 30 | } 31 | 32 | type policyRuleFile struct { 33 | Pattern string `json:"pattern"` 34 | Platforms []string `json:"platforms"` 35 | PolicyID string `json:"policy-id"` 36 | Replacement string `json:"rewrite"` 37 | } 38 | 39 | type PolicyMappings struct { 40 | Version string 41 | Kind string 42 | Policies map[string]*PolicyMapping 43 | Rules []*PolicyRule 44 | } 45 | 46 | type AttestationStyle string 47 | 48 | const ( 49 | AttestationStyleAttached AttestationStyle = "attached" 50 | AttestationStyleReferrers AttestationStyle = "referrers" 51 | ) 52 | 53 | type PolicyMapping struct { 54 | ID string `json:"id"` 55 | Description string `json:"description"` 56 | Files []PolicyMappingFile `json:"files"` 57 | Attestations *AttestationConfig `json:"attestations"` 58 | } 59 | 60 | type AttestationConfig struct { 61 | Style AttestationStyle `json:"style"` 62 | Repo string `json:"repo"` 63 | } 64 | 65 | type PolicyMappingFile struct { 66 | Path string `json:"path"` 67 | } 68 | 69 | type PolicyRule struct { 70 | Pattern *regexp.Regexp 71 | PolicyID string 72 | Replacement string 73 | Platforms []*v1.Platform 74 | } 75 | -------------------------------------------------------------------------------- /signerverifier/gcp_test.go: -------------------------------------------------------------------------------- 1 | //go:build e2e 2 | 3 | /* 4 | Copyright Docker attest authors 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package signerverifier 20 | 21 | import ( 22 | "context" 23 | "crypto/ecdsa" 24 | "testing" 25 | 26 | "github.com/docker/attest/internal/util" 27 | "github.com/stretchr/testify/assert" 28 | "github.com/stretchr/testify/require" 29 | ) 30 | 31 | const publicKeyPEM = `-----BEGIN PUBLIC KEY----- 32 | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMswW3iu7PR/rWTQjlhVmUsPK7rF 33 | k2s4SO3XbQ2GG2alm289SUUpmBAuVxvT8muYQ8HC/QzixzyTACTXsBDjQg== 34 | -----END PUBLIC KEY-----` 35 | 36 | // to run locally, we need to impersonate the GCP service account 37 | // gcloud auth application-default login --impersonate-service-account attest-kms-test@attest-kms-test.iam.gserviceaccount.com 38 | 39 | func TestGCPKMS_Signer(t *testing.T) { 40 | // create a new signer 41 | ctx := context.TODO() 42 | ref := "projects/attest-kms-test/locations/us-west1/keyRings/attest-kms-test/cryptoKeys/test-signing-key/cryptoKeyVersions/1" 43 | signer, err := GetGCPSigner(ctx, ref) 44 | require.NoError(t, err) 45 | msg := []byte("hello world") 46 | hash := util.SHA256(msg) 47 | 48 | // sign message digest 49 | sig, err := signer.Sign(ctx, hash) 50 | require.NoError(t, err) 51 | assert.NotEmpty(t, sig) 52 | // get Key ID from signer 53 | keyId, err := signer.KeyID() 54 | require.NoError(t, err) 55 | assert.NotEmpty(t, keyId) 56 | publicKey, err := ParsePublicKey([]byte(publicKeyPEM)) 57 | require.NoError(t, err) 58 | // verify payload ecdsa signature 59 | 60 | ecdsaPublicKey, ok := publicKey.(*ecdsa.PublicKey) 61 | if !ok { 62 | t.Fatal("Failed to convert publicKey to *ecdsa.PublicKey") 63 | } 64 | ok = ecdsa.VerifyASN1(ecdsaPublicKey, hash, sig) 65 | assert.True(t, ok) 66 | 67 | err = signer.Verify(ctx, msg, sig) 68 | require.NoError(t, err) 69 | } 70 | -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/blobs/sha256/a00c1b266ea6b992a8b6fa87ab8a67232f4319d9e3dd0e63365e73114a2c7869: -------------------------------------------------------------------------------- 1 | {"signatures":[{"keyid":"76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221","sig":""},{"keyid":"beac53949c4cf075824edede7d41715941f524db247d1b455a2389d7490ecd72","sig":"304402200ea43fe1e416994188eb928b097a2cdf4760de5ce1a5803ccd7f032fb043d5f00220201b346fbe41c44422426a5715eff90b09dfcc8a2b791f3b0471376a43c22889"},{"keyid":"81cf5a78d6ea2cd904256b9d814b340289b765e6f75ec4397e4ebb7586cab664","sig":""}],"signed":{"_type":"targets","delegations":{"keys":{"76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221":{"keytype":"ecdsa","keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3+asmp2GD6UijwWvMezwVG/BwFLuQa3o\nT6eRxFvkILGpVDbZ92ZYWidHl9LZ/eJUjhIjuVEkNVKoenw5KjKl8veP3MthZrQA\nSkYytOIwkidZo9Rk2dczbDcFSJvLGsmd\n-----END PUBLIC KEY-----\n"},"scheme":"ecdsa-sha2-nistp384","x-tuf-on-ci-keyowner":"@mrjoelkamp"},"beac53949c4cf075824edede7d41715941f524db247d1b455a2389d7490ecd72":{"keytype":"ecdsa","keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEERet/8hs3WHIXyOXNzhLpTOz6DBx\n7zzHnenJgV/TB0dRMAx6j9UVRvlEkh5OcYuktNeqnLpHce1rLjLjpiRPVg==\n-----END PUBLIC KEY-----\n"},"scheme":"ecdsa-sha2-nistp256","x-tuf-on-ci-keyowner":"@jonnystoten"}},"roles":[{"keyids":["76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221"],"name":"test-role","paths":["test-role/*","test-role/*/*","test-role/*/*/*","test-role/*/*/*/*"],"terminating":true,"threshold":1},{"keyids":["beac53949c4cf075824edede7d41715941f524db247d1b455a2389d7490ecd72"],"name":"testing","paths":["testing/*","testing/*/*","testing/*/*/*","testing/*/*/*/*"],"terminating":true,"threshold":1}]},"expires":"2034-09-07T14:32:09Z","spec_version":"1.0.31","targets":{"always-fail.rego":{"hashes":{"sha256":"e8a5b75ac27a28056d2155ff63acc1ffd76c30ed8558011c54708f4832f073ac"},"length":364},"jonnystoten2.rego":{"hashes":{"sha256":"bc46e8c31646f166a9efbd14fef154dd84cf07efc95c96be3a201c84470dcbc1"},"length":5857},"mapping.yaml":{"hashes":{"sha256":"baad1a9d61afa5d6f8717f576b57b9749e5549da4b826746fd73a5a914ac5be1"},"length":272},"test.txt":{"hashes":{"sha256":"02119a076ec3878c736c3a95e20794f5a8d5bce3d7ecc264681bb7334ca2e24b"},"length":31},"version-constraints":{"hashes":{"sha256":"bd6394a08afc1edfe5512fc14e63025a337e25ca0013c1068ec879742fc3a3c3"},"length":12}},"version":11,"x-tuf-on-ci-expiry-period":3650,"x-tuf-on-ci-signing-period":60}} -------------------------------------------------------------------------------- /example_verify_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package attest_test 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "os" 23 | "path/filepath" 24 | 25 | "github.com/docker/attest" 26 | "github.com/docker/attest/oci" 27 | "github.com/docker/attest/policy" 28 | "github.com/docker/attest/tuf" 29 | ) 30 | 31 | func ExampleVerify_remote() { 32 | // create a tuf client 33 | home, err := os.UserHomeDir() 34 | if err != nil { 35 | panic(err) 36 | } 37 | tufOutputPath := filepath.Join(home, ".docker", "tuf") 38 | tufClientOpts := tuf.NewDockerDefaultClientOptions(tufOutputPath) 39 | 40 | // create a resolver for remote attestations 41 | image := "registry-1.docker.io/library/notary:server" 42 | platform := "linux/amd64" 43 | 44 | // configure policy options 45 | opts := &policy.Options{ 46 | TUFClientOptions: tufClientOpts, 47 | LocalTargetsDir: filepath.Join(home, ".docker", "policy"), // location to store policy files downloaded from TUF 48 | LocalPolicyDir: "", // overrides TUF policy for local policy files if set 49 | PolicyID: "", // set to ignore policy mapping and select a policy by id 50 | DisableTUF: false, // set to disable TUF and rely on local policy files 51 | } 52 | 53 | src, err := oci.ParseImageSpec(image, oci.WithPlatform(platform)) 54 | if err != nil { 55 | panic(err) 56 | } 57 | // verify attestations 58 | result, err := attest.Verify(context.Background(), src, opts) 59 | if err != nil { 60 | panic(err) 61 | } 62 | switch result.Outcome { 63 | case attest.OutcomeSuccess: 64 | fmt.Println("policy passed") 65 | case attest.OutcomeNoPolicy: 66 | fmt.Println("no policy for image") 67 | case attest.OutcomeFailure: 68 | fmt.Println("policy failed") 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /internal/embed/embedded-roots/1.root-dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "signatures": [ 3 | { 4 | "keyid": "76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221", 5 | "sig": "3065023000f7d0a866576e94eaabc173b9233d4c8fcfa495527088f9022dff5a553f7a457da1015a6d0fc714f84848ec627387360231009fa70b2eebbe15241a2ec9b96a094ebd28661e30b8c3d1eab8d694df2b340bda511c489393630c9a9dacde42c99e9fa1" 6 | } 7 | ], 8 | "signed": { 9 | "_type": "root", 10 | "consistent_snapshot": true, 11 | "expires": "2034-05-29T20:14:11Z", 12 | "keys": { 13 | "76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221": { 14 | "keytype": "ecdsa", 15 | "keyval": { 16 | "public": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3+asmp2GD6UijwWvMezwVG/BwFLuQa3o\nT6eRxFvkILGpVDbZ92ZYWidHl9LZ/eJUjhIjuVEkNVKoenw5KjKl8veP3MthZrQA\nSkYytOIwkidZo9Rk2dczbDcFSJvLGsmd\n-----END PUBLIC KEY-----\n" 17 | }, 18 | "scheme": "ecdsa-sha2-nistp384", 19 | "x-tuf-on-ci-keyowner": "@mrjoelkamp" 20 | }, 21 | "bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5": { 22 | "keytype": "ecdsa", 23 | "keyval": { 24 | "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgDpP6O0sEt2R+l84WlfmqPBsFSby\nxJsJ6YmeUVgDk/wk9++8IAR6YBYewaKye56gMnIYjTFbyOI8WomA2NQFBw==\n-----END PUBLIC KEY-----\n" 25 | }, 26 | "scheme": "ecdsa-sha2-nistp256", 27 | "x-tuf-on-ci-online-uri": "awskms:arn:aws:kms:us-east-1:175142243308:key/fbd8dab6-5677-4b57-87e6-8369c45b3b61" 28 | } 29 | }, 30 | "roles": { 31 | "root": { 32 | "keyids": [ 33 | "76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221" 34 | ], 35 | "threshold": 1 36 | }, 37 | "snapshot": { 38 | "keyids": [ 39 | "bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5" 40 | ], 41 | "threshold": 1, 42 | "x-tuf-on-ci-expiry-period": 3650, 43 | "x-tuf-on-ci-signing-period": 60 44 | }, 45 | "targets": { 46 | "keyids": [ 47 | "76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221" 48 | ], 49 | "threshold": 1 50 | }, 51 | "timestamp": { 52 | "keyids": [ 53 | "bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5" 54 | ], 55 | "threshold": 1, 56 | "x-tuf-on-ci-expiry-period": 3650, 57 | "x-tuf-on-ci-signing-period": 60 58 | } 59 | }, 60 | "spec_version": "1.0.31", 61 | "version": 1, 62 | "x-tuf-on-ci-expiry-period": 3650, 63 | "x-tuf-on-ci-signing-period": 60 64 | } 65 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo/metadata/1.root.json: -------------------------------------------------------------------------------- 1 | { 2 | "signatures": [ 3 | { 4 | "keyid": "76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221", 5 | "sig": "3065023000f7d0a866576e94eaabc173b9233d4c8fcfa495527088f9022dff5a553f7a457da1015a6d0fc714f84848ec627387360231009fa70b2eebbe15241a2ec9b96a094ebd28661e30b8c3d1eab8d694df2b340bda511c489393630c9a9dacde42c99e9fa1" 6 | } 7 | ], 8 | "signed": { 9 | "_type": "root", 10 | "consistent_snapshot": true, 11 | "expires": "2034-05-29T20:14:11Z", 12 | "keys": { 13 | "76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221": { 14 | "keytype": "ecdsa", 15 | "keyval": { 16 | "public": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3+asmp2GD6UijwWvMezwVG/BwFLuQa3o\nT6eRxFvkILGpVDbZ92ZYWidHl9LZ/eJUjhIjuVEkNVKoenw5KjKl8veP3MthZrQA\nSkYytOIwkidZo9Rk2dczbDcFSJvLGsmd\n-----END PUBLIC KEY-----\n" 17 | }, 18 | "scheme": "ecdsa-sha2-nistp384", 19 | "x-tuf-on-ci-keyowner": "@mrjoelkamp" 20 | }, 21 | "bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5": { 22 | "keytype": "ecdsa", 23 | "keyval": { 24 | "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgDpP6O0sEt2R+l84WlfmqPBsFSby\nxJsJ6YmeUVgDk/wk9++8IAR6YBYewaKye56gMnIYjTFbyOI8WomA2NQFBw==\n-----END PUBLIC KEY-----\n" 25 | }, 26 | "scheme": "ecdsa-sha2-nistp256", 27 | "x-tuf-on-ci-online-uri": "awskms:arn:aws:kms:us-east-1:175142243308:key/fbd8dab6-5677-4b57-87e6-8369c45b3b61" 28 | } 29 | }, 30 | "roles": { 31 | "root": { 32 | "keyids": [ 33 | "76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221" 34 | ], 35 | "threshold": 1 36 | }, 37 | "snapshot": { 38 | "keyids": [ 39 | "bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5" 40 | ], 41 | "threshold": 1, 42 | "x-tuf-on-ci-expiry-period": 3650, 43 | "x-tuf-on-ci-signing-period": 60 44 | }, 45 | "targets": { 46 | "keyids": [ 47 | "76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221" 48 | ], 49 | "threshold": 1 50 | }, 51 | "timestamp": { 52 | "keyids": [ 53 | "bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5" 54 | ], 55 | "threshold": 1, 56 | "x-tuf-on-ci-expiry-period": 3650, 57 | "x-tuf-on-ci-signing-period": 60 58 | } 59 | }, 60 | "spec_version": "1.0.31", 61 | "version": 1, 62 | "x-tuf-on-ci-expiry-period": 3650, 63 | "x-tuf-on-ci-signing-period": 60 64 | } 65 | } -------------------------------------------------------------------------------- /test/testdata/tuf/test-repo-oci/metadata/blobs/sha256/5a9f60b64b708d05e4e4da0354529fc7fe5015807b79f0bf7b136207bf952bd7: -------------------------------------------------------------------------------- 1 | { 2 | "signatures": [ 3 | { 4 | "keyid": "76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221", 5 | "sig": "3065023000f7d0a866576e94eaabc173b9233d4c8fcfa495527088f9022dff5a553f7a457da1015a6d0fc714f84848ec627387360231009fa70b2eebbe15241a2ec9b96a094ebd28661e30b8c3d1eab8d694df2b340bda511c489393630c9a9dacde42c99e9fa1" 6 | } 7 | ], 8 | "signed": { 9 | "_type": "root", 10 | "consistent_snapshot": true, 11 | "expires": "2034-05-29T20:14:11Z", 12 | "keys": { 13 | "76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221": { 14 | "keytype": "ecdsa", 15 | "keyval": { 16 | "public": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3+asmp2GD6UijwWvMezwVG/BwFLuQa3o\nT6eRxFvkILGpVDbZ92ZYWidHl9LZ/eJUjhIjuVEkNVKoenw5KjKl8veP3MthZrQA\nSkYytOIwkidZo9Rk2dczbDcFSJvLGsmd\n-----END PUBLIC KEY-----\n" 17 | }, 18 | "scheme": "ecdsa-sha2-nistp384", 19 | "x-tuf-on-ci-keyowner": "@mrjoelkamp" 20 | }, 21 | "bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5": { 22 | "keytype": "ecdsa", 23 | "keyval": { 24 | "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgDpP6O0sEt2R+l84WlfmqPBsFSby\nxJsJ6YmeUVgDk/wk9++8IAR6YBYewaKye56gMnIYjTFbyOI8WomA2NQFBw==\n-----END PUBLIC KEY-----\n" 25 | }, 26 | "scheme": "ecdsa-sha2-nistp256", 27 | "x-tuf-on-ci-online-uri": "awskms:arn:aws:kms:us-east-1:175142243308:key/fbd8dab6-5677-4b57-87e6-8369c45b3b61" 28 | } 29 | }, 30 | "roles": { 31 | "root": { 32 | "keyids": [ 33 | "76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221" 34 | ], 35 | "threshold": 1 36 | }, 37 | "snapshot": { 38 | "keyids": [ 39 | "bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5" 40 | ], 41 | "threshold": 1, 42 | "x-tuf-on-ci-expiry-period": 3650, 43 | "x-tuf-on-ci-signing-period": 60 44 | }, 45 | "targets": { 46 | "keyids": [ 47 | "76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221" 48 | ], 49 | "threshold": 1 50 | }, 51 | "timestamp": { 52 | "keyids": [ 53 | "bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5" 54 | ], 55 | "threshold": 1, 56 | "x-tuf-on-ci-expiry-period": 3650, 57 | "x-tuf-on-ci-signing-period": 60 58 | } 59 | }, 60 | "spec_version": "1.0.31", 61 | "version": 1, 62 | "x-tuf-on-ci-expiry-period": 3650, 63 | "x-tuf-on-ci-signing-period": 60 64 | } 65 | } -------------------------------------------------------------------------------- /signerverifier/common.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package signerverifier 18 | 19 | import ( 20 | "context" 21 | "crypto" 22 | "crypto/ecdsa" 23 | "crypto/elliptic" 24 | "crypto/rand" 25 | "crypto/x509" 26 | "encoding/pem" 27 | "fmt" 28 | "io" 29 | 30 | "github.com/secure-systems-lab/go-securesystemslib/dsse" 31 | ) 32 | 33 | func LoadKeyPair(priv []byte) (dsse.SignerVerifier, error) { 34 | privateKey, err := parsePriv(priv) 35 | if err != nil { 36 | return nil, err 37 | } 38 | return NewECDSASignerVerifier(privateKey) 39 | } 40 | 41 | func parsePriv(privkeyBytes []byte) (*ecdsa.PrivateKey, error) { 42 | p, _ := pem.Decode(privkeyBytes) 43 | if p == nil { 44 | return nil, fmt.Errorf("privkey file does not contain any PEM data") 45 | } 46 | if p.Type != "EC PRIVATE KEY" { 47 | return nil, fmt.Errorf("privkey file does not contain a priavte key") 48 | } 49 | privKey, err := x509.ParseECPrivateKey(p.Bytes) 50 | if err != nil { 51 | return nil, fmt.Errorf("error failed to parse public key: %w", err) 52 | } 53 | 54 | return privKey, nil 55 | } 56 | 57 | func GenKeyPair() (dsse.SignerVerifier, error) { 58 | signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 59 | if err != nil { 60 | return nil, err 61 | } 62 | return NewECDSASignerVerifier(signer) 63 | } 64 | 65 | // ensure it implements crypto.Signer. 66 | var _ crypto.Signer = (*cryptoSignerWrapper)(nil) 67 | 68 | type cryptoSignerWrapper struct { 69 | sv dsse.SignerVerifier 70 | } 71 | 72 | // Public implements crypto.Signer. 73 | func (c *cryptoSignerWrapper) Public() crypto.PublicKey { 74 | return c.sv.Public() 75 | } 76 | 77 | // Sign implements crypto.Signer. 78 | func (c *cryptoSignerWrapper) Sign(_ io.Reader, digest []byte, _ crypto.SignerOpts) (signature []byte, err error) { 79 | return c.sv.Sign(context.Background(), digest) 80 | } 81 | 82 | func AsCryptoSigner(signer dsse.SignerVerifier) (crypto.Signer, error) { 83 | return &cryptoSignerWrapper{sv: signer}, nil 84 | } 85 | -------------------------------------------------------------------------------- /tuf/version_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package tuf 18 | 19 | import ( 20 | "fmt" 21 | "os" 22 | "path/filepath" 23 | "testing" 24 | 25 | "github.com/Masterminds/semver/v3" 26 | "github.com/docker/attest/internal/test" 27 | "github.com/docker/attest/version" 28 | "github.com/stretchr/testify/assert" 29 | ) 30 | 31 | const ( 32 | invalidVersion = "0.0.1" 33 | validVersion = "v1.0.0-0" 34 | versionConstraint = ">=v1.0.0-0" 35 | ) 36 | 37 | func TestDefaultVersionChecker(t *testing.T) { 38 | testDir := test.CreateTempDir(t, "", "tuf_temp") 39 | versionConstraintsPath := filepath.Join(testDir, "version-constraints") 40 | err := os.WriteFile(versionConstraintsPath, []byte(versionConstraint), 0o600) 41 | assert.NoError(t, err) 42 | tufClient := NewMockTufClient(testDir) 43 | 44 | expectedError := fmt.Sprintf("%s version %s does not satisfy constraints %s: %s is less than %s", version.ThisModulePath, invalidVersion, versionConstraint, invalidVersion, validVersion) 45 | 46 | testCases := []struct { 47 | name string 48 | expectedError string 49 | version string 50 | }{ 51 | {name: "version is less than the minimum", expectedError: expectedError, version: "0.0.1"}, 52 | {name: "version is equal to the minimum", version: "1.0.0"}, 53 | {name: "version is greater than the minimum", version: "1.0.1"}, 54 | } 55 | 56 | for _, tc := range testCases { 57 | t.Run(tc.name, func(t *testing.T) { 58 | checker := NewDefaultVersionChecker() 59 | checker.VersionFetcher = &MockVersionFetcher{version: tc.version} 60 | err := checker.CheckVersion(tufClient) 61 | if tc.expectedError != "" { 62 | assert.Error(t, err) 63 | assert.Equal(t, tc.expectedError, err.Error()) 64 | return 65 | } 66 | assert.NoError(t, err) 67 | }) 68 | } 69 | } 70 | 71 | type MockVersionFetcher struct { 72 | version string 73 | } 74 | 75 | func (m *MockVersionFetcher) Get() (*semver.Version, error) { 76 | return semver.NewVersion(m.version) 77 | } 78 | -------------------------------------------------------------------------------- /attestation/verifier_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package attestation 18 | 19 | import ( 20 | "context" 21 | "reflect" 22 | "testing" 23 | 24 | "github.com/docker/attest/tlog" 25 | "github.com/docker/attest/tuf" 26 | "github.com/stretchr/testify/require" 27 | ) 28 | 29 | func Test_verifier_GetLogVerifier(t *testing.T) { 30 | type fields struct { 31 | tufDownloader tuf.Downloader 32 | signatureVerifierFactory SignatureVerifierFactory 33 | logVerifierFactory LogVerifierFactory 34 | } 35 | type args struct { 36 | ctx context.Context 37 | opts *VerifyOptions 38 | } 39 | rekor, err := tlog.NewRekorLog() 40 | require.NoError(t, err) 41 | tests := []struct { 42 | name string 43 | fields fields 44 | args args 45 | want tlog.TransparencyLog 46 | wantErr bool 47 | }{ 48 | {name: "skip_tl true", fields: fields{}, args: args{ctx: context.Background(), opts: &VerifyOptions{SkipTL: true}}}, 49 | {name: "skip_tl false", fields: fields{}, args: args{ctx: context.Background(), opts: &VerifyOptions{SkipTL: false}}, want: rekor}, 50 | {name: "tl: rekor", fields: fields{logVerifierFactory: func(_ context.Context, _ *VerifyOptions) (tlog.TransparencyLog, error) { 51 | return &tlog.Rekor{}, nil 52 | }}, args: args{ctx: context.Background(), opts: &VerifyOptions{}}, want: &tlog.Rekor{}}, 53 | } 54 | for _, tt := range tests { 55 | t.Run(tt.name, func(t *testing.T) { 56 | v := &verifier{ 57 | tufDownloader: tt.fields.tufDownloader, 58 | signatureVerifierFactory: tt.fields.signatureVerifierFactory, 59 | logVerifierFactory: tt.fields.logVerifierFactory, 60 | } 61 | got, err := v.GetLogVerifier(tt.args.ctx, tt.args.opts) 62 | if (err != nil) != tt.wantErr { 63 | t.Errorf("verifier.GetLogVerifier() error = %v, wantErr %v", err, tt.wantErr) 64 | return 65 | } 66 | if !reflect.DeepEqual(got, tt.want) { 67 | t.Errorf("verifier.GetLogVerifier() = %v, want %v", got, tt.want) 68 | } 69 | }) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /attestation/mock.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package attestation 18 | 19 | import ( 20 | "context" 21 | 22 | "github.com/docker/attest/internal/test" 23 | "github.com/docker/attest/oci" 24 | v1 "github.com/google/go-containerregistry/pkg/v1" 25 | ) 26 | 27 | // ensure MockResolver implements Resolver. 28 | var _ oci.ImageDetailsResolver = MockResolver{} 29 | 30 | type MockResolver struct { 31 | Envs []*EnvelopeReference 32 | Image string 33 | PlatformFn func() (*v1.Platform, error) 34 | DescriptorFn func() (*v1.Descriptor, error) 35 | ImangeNameFn func() (string, error) 36 | } 37 | 38 | func (r MockResolver) Attestations(_ context.Context, _ string) ([]*EnvelopeReference, error) { 39 | return r.Envs, nil 40 | } 41 | 42 | func (r MockResolver) ImageName(_ context.Context) (string, error) { 43 | if r.Image != "" { 44 | return r.Image, nil 45 | } 46 | if r.ImangeNameFn != nil { 47 | return r.ImangeNameFn() 48 | } 49 | return "library/alpine:latest", nil 50 | } 51 | 52 | func (r MockResolver) ImageDescriptor(_ context.Context) (*v1.Descriptor, error) { 53 | if r.DescriptorFn != nil { 54 | return r.DescriptorFn() 55 | } 56 | digest, err := v1.NewHash(test.UnsignedLinuxAMD64ImageDigest) 57 | if err != nil { 58 | return nil, err 59 | } 60 | return &v1.Descriptor{ 61 | Digest: digest, 62 | Size: 1234, 63 | MediaType: "application/vnd.oci.image.manifest.v1+json", 64 | }, nil 65 | } 66 | 67 | func (r MockResolver) ImagePlatform(_ context.Context) (*v1.Platform, error) { 68 | if r.PlatformFn != nil { 69 | return r.PlatformFn() 70 | } 71 | return oci.ParsePlatform("linux/amd64") 72 | } 73 | 74 | type MockRegistryResolver struct { 75 | Subject *v1.Descriptor 76 | ImageNameStr string 77 | *MockResolver 78 | } 79 | 80 | func (r *MockRegistryResolver) ImageDescriptor(_ context.Context) (*v1.Descriptor, error) { 81 | return r.Subject, nil 82 | } 83 | 84 | func (r *MockRegistryResolver) ImageName(_ context.Context) (string, error) { 85 | return r.ImageNameStr, nil 86 | } 87 | -------------------------------------------------------------------------------- /oci/registry.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package oci 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/google/go-containerregistry/pkg/name" 24 | v1 "github.com/google/go-containerregistry/pkg/v1" 25 | "github.com/google/go-containerregistry/pkg/v1/remote" 26 | ) 27 | 28 | // ensure RegistryImageDetailsResolver implements ImageDetailsResolver. 29 | var _ ImageDetailsResolver = &RegistryImageDetailsResolver{} 30 | 31 | type RegistryImageDetailsResolver struct { 32 | *ImageSpec 33 | descriptor *v1.Descriptor 34 | } 35 | 36 | func NewRegistryImageDetailsResolver(src *ImageSpec) (*RegistryImageDetailsResolver, error) { 37 | return &RegistryImageDetailsResolver{ 38 | ImageSpec: src, 39 | }, nil 40 | } 41 | 42 | func (r *RegistryImageDetailsResolver) ImageName(_ context.Context) (string, error) { 43 | return r.Identifier, nil 44 | } 45 | 46 | func (r *RegistryImageDetailsResolver) ImagePlatform(_ context.Context) (*v1.Platform, error) { 47 | return r.Platform, nil 48 | } 49 | 50 | func (r *RegistryImageDetailsResolver) ImageDescriptor(ctx context.Context) (*v1.Descriptor, error) { 51 | if r.descriptor == nil { 52 | subjectRef, err := name.ParseReference(r.Identifier) 53 | if err != nil { 54 | return nil, fmt.Errorf("failed to parse reference: %w", err) 55 | } 56 | options := WithOptions(ctx, r.Platform) 57 | image, err := remote.Image(subjectRef, options...) 58 | if err != nil { 59 | return nil, fmt.Errorf("failed to get image manifest: %w", err) 60 | } 61 | digest, err := image.Digest() 62 | if err != nil { 63 | return nil, fmt.Errorf("failed to get image digest: %w", err) 64 | } 65 | size, err := image.Size() 66 | if err != nil { 67 | return nil, fmt.Errorf("failed to get image size: %w", err) 68 | } 69 | mediaType, err := image.MediaType() 70 | if err != nil { 71 | return nil, fmt.Errorf("failed to get image media type: %w", err) 72 | } 73 | r.descriptor = &v1.Descriptor{ 74 | Digest: digest, 75 | Size: size, 76 | MediaType: mediaType, 77 | } 78 | } 79 | return r.descriptor, nil 80 | } 81 | -------------------------------------------------------------------------------- /attestation/registry_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package attestation_test 18 | 19 | import ( 20 | "fmt" 21 | "net/url" 22 | "strings" 23 | "testing" 24 | 25 | "github.com/docker/attest" 26 | "github.com/docker/attest/attestation" 27 | "github.com/docker/attest/internal/test" 28 | "github.com/docker/attest/oci" 29 | "github.com/docker/attest/policy" 30 | "github.com/google/go-containerregistry/pkg/registry" 31 | "github.com/stretchr/testify/assert" 32 | "github.com/stretchr/testify/require" 33 | ) 34 | 35 | func TestRegistry(t *testing.T) { 36 | ctx, signer := test.Setup(t) 37 | regServer := test.NewLocalRegistry(ctx, registry.WithReferrersSupport(false)) 38 | defer regServer.Close() 39 | u, err := url.Parse(regServer.URL) 40 | require.NoError(t, err) 41 | 42 | opts := &attestation.SigningOptions{} 43 | attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex("..")) 44 | require.NoError(t, err) 45 | signedManifests, err := attest.SignStatements(ctx, attIdx.Index, signer, opts) 46 | require.NoError(t, err) 47 | signedIndex := attIdx.Index 48 | signedIndex, err = attestation.UpdateIndexImages(signedIndex, signedManifests) 49 | require.NoError(t, err) 50 | 51 | indexName := fmt.Sprintf("%s/repo:root", u.Host) 52 | require.NoError(t, err) 53 | err = oci.PushIndexToRegistry(ctx, signedIndex, indexName) 54 | require.NoError(t, err) 55 | 56 | spec, err := oci.ParseImageSpec(indexName) 57 | require.NoError(t, err) 58 | 59 | resolver, err := policy.CreateImageDetailsResolver(spec) 60 | require.NoError(t, err) 61 | desc, err := resolver.ImageDescriptor(ctx) 62 | require.NoError(t, err) 63 | digest := desc.Digest.String() 64 | assert.True(t, strings.Contains(digest, "sha256:")) 65 | 66 | // resolver also works with platform specific digest 67 | spec, err = oci.ParseImageSpec(fmt.Sprintf("%s@%s", indexName, digest)) 68 | require.NoError(t, err) 69 | 70 | resolver, err = policy.CreateImageDetailsResolver(spec) 71 | require.NoError(t, err) 72 | desc, err = resolver.ImageDescriptor(ctx) 73 | require.NoError(t, err) 74 | assert.Equal(t, desc.Digest.String(), digest) 75 | } 76 | -------------------------------------------------------------------------------- /attestation/vsa.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Docker attest authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package attestation 18 | 19 | import ( 20 | "fmt" 21 | 22 | "github.com/docker/attest/version" 23 | intoto "github.com/in-toto/in-toto-golang/in_toto" 24 | "github.com/package-url/packageurl-go" 25 | ) 26 | 27 | const ( 28 | VSAPredicateType = "https://slsa.dev/verification_summary/v1" 29 | ) 30 | 31 | type VSAPredicate struct { 32 | Verifier VSAVerifier `json:"verifier"` 33 | TimeVerified string `json:"timeVerified"` 34 | ResourceURI string `json:"resourceUri"` 35 | Policy VSAPolicy `json:"policy"` 36 | InputAttestations []ResourceDescriptor `json:"inputAttestations,omitempty"` 37 | VerificationResult string `json:"verificationResult"` 38 | VerifiedLevels []string `json:"verifiedLevels"` 39 | } 40 | 41 | type VSAVerifier struct { 42 | ID string `json:"id"` 43 | Version VerifierVersion `json:"version"` 44 | } 45 | 46 | type VerifierVersion map[string]string 47 | 48 | type VSAPolicy struct { 49 | URI string `json:"uri,omitempty"` 50 | Digest map[string]string `json:"digest"` 51 | DownloadLocation string `json:"downloadLocation,omitempty"` 52 | } 53 | 54 | func ToVSAResourceURI(sub intoto.Subject) (string, error) { 55 | // parse purl 56 | purl, err := packageurl.FromString(sub.Name) 57 | if err != nil { 58 | return "", fmt.Errorf("failed to parse package url: %w", err) 59 | } 60 | quals := purl.Qualifiers.Map() 61 | if quals["digest"] == "" { 62 | quals["digest"] = "sha256:" + sub.Digest["sha256"] 63 | } 64 | purl.Qualifiers = packageurl.QualifiersFromMap(quals) 65 | return purl.String(), nil 66 | } 67 | 68 | func GetVerifierVersion(fetcher version.Fetcher) (VerifierVersion, error) { 69 | attestVersion, err := fetcher.Get() 70 | if err != nil { 71 | return nil, fmt.Errorf("failed to get attest version: %w", err) 72 | } 73 | if attestVersion == nil { 74 | return nil, nil 75 | } 76 | return VerifierVersion{ 77 | version.ThisModulePath: attestVersion.String(), 78 | }, nil 79 | } 80 | -------------------------------------------------------------------------------- /scripts/gen-testdata.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright Docker attest authors 4 | 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -eo pipefail 18 | 19 | echo "Starting the process to generate testdata..." 20 | 21 | # Define functions 22 | function check_command () { 23 | command -v "$1" >/dev/null 2>&1 || { echo >&2 "This script requires $1 but it's not installed. Aborting."; exit 1; } 24 | } 25 | 26 | function cleanup_testdata () { 27 | echo "Cleaning up existing testdata..." 28 | rm -rf "${TESTDATA_PATH:?}/${UNSIGNED_INDEX_DIR:?}" 29 | rm -rf "${TESTDATA_PATH:?}/${NO_PROVENANCE_INDEX_DIR:?}" 30 | rm -rf "${TESTDATA_PATH:?}/${UNSIGNED_IMAGE_DIR:?}" 31 | } 32 | 33 | function build_unsigned_index () { 34 | echo "Building $UNSIGNED_INDEX_DIR..." 35 | docker buildx build "$TEST_INDEX_DOCKERFILE_PATH" --sbom true --provenance true --platform linux/amd64,linux/arm64 \ 36 | --output type=oci,tar=false,name="$TEST_INDEX_REPO:$TEST_INDEX_TAG",dest="$TESTDATA_PATH/$UNSIGNED_INDEX_DIR" 37 | } 38 | 39 | function build_no_provenance_index () { 40 | echo "Building unsigned $NO_PROVENANCE_INDEX_DIR..." 41 | docker buildx build "$TEST_INDEX_DOCKERFILE_PATH" --sbom true --provenance false --platform linux/amd64,linux/arm64 \ 42 | --output type=oci,tar=false,name="$TEST_INDEX_REPO:$TEST_INDEX_TAG",dest="$TESTDATA_PATH/$NO_PROVENANCE_INDEX_DIR" 43 | } 44 | 45 | function build_image () { 46 | echo "Building $UNSIGNED_IMAGE_DIR..." 47 | docker buildx build "$TEST_INDEX_DOCKERFILE_PATH" --sbom false --provenance false --platform linux/amd64 \ 48 | --output type=oci,tar=false,name="$TEST_INDEX_REPO:$TEST_INDEX_TAG",dest="$TESTDATA_PATH/$UNSIGNED_IMAGE_DIR" 49 | } 50 | 51 | # Check required commands 52 | check_command docker 53 | 54 | TESTDATA_PATH="../test/testdata" 55 | TEST_INDEX_DOCKERFILE_PATH="../test" 56 | TEST_INDEX_REPO="test-image" 57 | TEST_INDEX_TAG="test" 58 | UNSIGNED_INDEX_DIR="unsigned-index" 59 | NO_PROVENANCE_INDEX_DIR="no-provenance-index" 60 | UNSIGNED_IMAGE_DIR="unsigned-image" 61 | ATTESTATION_PAYLOADTYPE="application/vnd.in-toto+json" 62 | 63 | # Run steps 64 | cleanup_testdata 65 | build_unsigned_index 66 | build_no_provenance_index 67 | build_image 68 | 69 | echo "Process completed successfully." 70 | --------------------------------------------------------------------------------