339 Commits
v0.1.2 ... main

Author SHA1 Message Date
dependabot[bot]
de602565c0 feat(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.28.4 to 1.28.5 (#223)
Some checks failed
Release Drafter / update_release_draft (push) Has been cancelled
test code / golang (1.22.x, macos-latest) (push) Has been cancelled
test code / golang (1.22.x, ubuntu-latest) (push) Has been cancelled
test code / golang (1.23.x, macos-latest) (push) Has been cancelled
test code / golang (1.23.x, ubuntu-latest) (push) Has been cancelled
Bumps
[github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2)
from 1.28.4 to 1.28.5.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="d125de3792"><code>d125de3</code></a>
Release 2024-11-18</li>
<li><a
href="fec51f3fff"><code>fec51f3</code></a>
Regenerated Clients</li>
<li><a
href="fba5997045"><code>fba5997</code></a>
Update API model</li>
<li><a
href="0b8e5c842f"><code>0b8e5c8</code></a>
Bump smithy-go dependency (<a
href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/2902">#2902</a>)</li>
<li><a
href="50ba45ce16"><code>50ba45c</code></a>
Release 2024-11-15.2</li>
<li><a
href="444bdffccd"><code>444bdff</code></a>
Regenerated Clients</li>
<li><a
href="55ab381b20"><code>55ab381</code></a>
Update endpoints model</li>
<li><a
href="94c083768b"><code>94c0837</code></a>
Update API model</li>
<li><a
href="2398a7903c"><code>2398a79</code></a>
Remove elastictranscoder service's integration test (<a
href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/2901">#2901</a>)</li>
<li><a
href="93e0f294f6"><code>93e0f29</code></a>
Release 2024-11-15</li>
<li>Additional commits viewable in <a
href="https://github.com/aws/aws-sdk-go-v2/compare/config/v1.28.4...config/v1.28.5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/aws/aws-sdk-go-v2/config&package-manager=go_modules&previous-version=1.28.4&new-version=1.28.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 11:16:04 +00:00
dependabot[bot]
dea2686365 feat(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.28.3 to 1.28.4 (#221)
Bumps
[github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2)
from 1.28.3 to 1.28.4.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="f0fcf5955d"><code>f0fcf59</code></a>
Release 2024-11-14</li>
<li><a
href="af5e138e30"><code>af5e138</code></a>
Regenerated Clients</li>
<li><a
href="6b6d9d09c4"><code>6b6d9d0</code></a>
Update endpoints model</li>
<li><a
href="6646f21285"><code>6646f21</code></a>
Update API model</li>
<li><a
href="27326538a1"><code>2732653</code></a>
Release 2024-11-13</li>
<li><a
href="91f9f43858"><code>91f9f43</code></a>
Regenerated Clients</li>
<li><a
href="b95013d5d8"><code>b95013d</code></a>
Update endpoints model</li>
<li><a
href="673ae94b2b"><code>673ae94</code></a>
Update API model</li>
<li><a
href="10b8b14449"><code>10b8b14</code></a>
Release 2024-11-12</li>
<li><a
href="c96e678745"><code>c96e678</code></a>
Regenerated Clients</li>
<li>Additional commits viewable in <a
href="https://github.com/aws/aws-sdk-go-v2/compare/config/v1.28.3...config/v1.28.4">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/aws/aws-sdk-go-v2/config&package-manager=go_modules&previous-version=1.28.3&new-version=1.28.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-18 15:07:33 +00:00
dependabot[bot]
00bc2fa831 chore(deps): bump codecov/codecov-action from 4 to 5 (#220)
Bumps
[codecov/codecov-action](https://github.com/codecov/codecov-action) from
4 to 5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/codecov/codecov-action/releases">codecov/codecov-action's
releases</a>.</em></p>
<blockquote>
<h2>v5.0.0</h2>
<h2>v5 Release</h2>
<p><code>v5</code> of the Codecov GitHub Action will use the <a
href="https://github.com/codecov/wrapper">Codecov Wrapper</a> to
encapsulate the <a
href="https://github.com/codecov/codecov-cli">CLI</a>. This will help
ensure that the Action gets updates quicker.</p>
<h3>Migration Guide</h3>
<p>The <code>v5</code> release also coincides with the opt-out feature
for tokens for public repositories. In the <code>Global Upload
Token</code> section of the settings page of an organization in
codecov.io, you can set the ability for Codecov to receive a coverage
reports from any source. This will allow contributors or other members
of a repository to upload without needing access to the Codecov token.
For more details see <a
href="https://docs.codecov.com/docs/codecov-tokens#uploading-without-a-token">how
to upload without a token</a>.</p>
<blockquote>
<p>[!WARNING]<br />
<strong>The following arguments have been changed</strong></p>
<ul>
<li><code>file</code> (this has been deprecated in favor of
<code>files</code>)</li>
<li><code>plugin</code> (this has been deprecated in favor of
<code>plugins</code>)</li>
</ul>
</blockquote>
<p>The following arguments have been added:</p>
<ul>
<li><code>binary</code></li>
<li><code>gcov_args</code></li>
<li><code>gcov_executable</code></li>
<li><code>gcov_ignore</code></li>
<li><code>gcov_include</code></li>
<li><code>report_type</code></li>
<li><code>skip_validation</code></li>
<li><code>swift_project</code></li>
</ul>
<p>You can see their usage in the <code>action.yml</code> <a
href="https://github.com/codecov/codecov-action/blob/main/action.yml">file</a>.</p>
<h2>What's Changed</h2>
<ul>
<li>chore(deps): bump to eslint9+ and remove eslint-config-google by <a
href="https://github.com/thomasrockhu-codecov"><code>@​thomasrockhu-codecov</code></a>
in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1591">codecov/codecov-action#1591</a></li>
<li>build(deps-dev): bump <code>@​octokit/webhooks-types</code> from
7.5.1 to 7.6.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1595">codecov/codecov-action#1595</a></li>
<li>build(deps-dev): bump typescript from 5.6.2 to 5.6.3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1604">codecov/codecov-action#1604</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/parser</code> from
8.8.0 to 8.8.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1601">codecov/codecov-action#1601</a></li>
<li>build(deps): bump <code>@​actions/core</code> from 1.11.0 to 1.11.1
by <a href="https://github.com/dependabot"><code>@​dependabot</code></a>
in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1597">codecov/codecov-action#1597</a></li>
<li>build(deps): bump github/codeql-action from 3.26.9 to 3.26.11 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1596">codecov/codecov-action#1596</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 8.8.0 to 8.8.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1600">codecov/codecov-action#1600</a></li>
<li>build(deps-dev): bump eslint from 9.11.1 to 9.12.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1598">codecov/codecov-action#1598</a></li>
<li>build(deps): bump github/codeql-action from 3.26.11 to 3.26.12 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1609">codecov/codecov-action#1609</a></li>
<li>build(deps): bump actions/checkout from 4.2.0 to 4.2.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1608">codecov/codecov-action#1608</a></li>
<li>build(deps): bump actions/upload-artifact from 4.4.0 to 4.4.3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1607">codecov/codecov-action#1607</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/parser</code> from
8.8.1 to 8.9.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1612">codecov/codecov-action#1612</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 8.8.1 to 8.9.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1611">codecov/codecov-action#1611</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 8.9.0 to 8.10.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1615">codecov/codecov-action#1615</a></li>
<li>build(deps-dev): bump eslint from 9.12.0 to 9.13.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1618">codecov/codecov-action#1618</a></li>
<li>build(deps): bump github/codeql-action from 3.26.12 to 3.26.13 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1617">codecov/codecov-action#1617</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/parser</code> from
8.9.0 to 8.10.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1614">codecov/codecov-action#1614</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 8.10.0 to 8.11.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1620">codecov/codecov-action#1620</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/parser</code> from
8.10.0 to 8.11.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1619">codecov/codecov-action#1619</a></li>
<li>build(deps-dev): bump <code>@​types/jest</code> from 29.5.13 to
29.5.14 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1622">codecov/codecov-action#1622</a></li>
<li>build(deps): bump actions/checkout from 4.2.1 to 4.2.2 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1625">codecov/codecov-action#1625</a></li>
<li>build(deps): bump github/codeql-action from 3.26.13 to 3.27.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1624">codecov/codecov-action#1624</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 8.11.0 to 8.12.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1626">codecov/codecov-action#1626</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 8.12.1 to 8.12.2 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1629">codecov/codecov-action#1629</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md">codecov/codecov-action's
changelog</a>.</em></p>
<blockquote>
<h2>4.0.0-beta.2</h2>
<h3>Fixes</h3>
<ul>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/1085">#1085</a>
not adding -n if empty to do-upload command</li>
</ul>
<h2>4.0.0-beta.1</h2>
<p><code>v4</code> represents a move from the <a
href="https://github.com/codecov/uploader">universal uploader</a> to the
<a href="https://github.com/codecov/codecov-cli">Codecov CLI</a>.
Although this will unlock new features for our users, the CLI is not yet
at feature parity with the universal uploader.</p>
<h3>Breaking Changes</h3>
<ul>
<li>No current support for <code>aarch64</code> and <code>alpine</code>
architectures.</li>
<li>Tokenless uploading is unsuported</li>
<li>Various arguments to the Action have been removed</li>
</ul>
<h2>3.1.4</h2>
<h3>Fixes</h3>
<ul>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/967">#967</a>
Fix typo in README.md</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/971">#971</a>
fix: add back in working dir</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/969">#969</a>
fix: CLI option names for uploader</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/970">#970</a>
build(deps-dev): bump <code>@​types/node</code> from 18.15.12 to
18.16.3</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/979">#979</a>
build(deps-dev): bump <code>@​types/node</code> from 20.1.0 to
20.1.2</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/981">#981</a>
build(deps-dev): bump <code>@​types/node</code> from 20.1.2 to
20.1.4</li>
</ul>
<h2>3.1.3</h2>
<h3>Fixes</h3>
<ul>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/960">#960</a>
fix: allow for aarch64 build</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/957">#957</a>
build(deps-dev): bump jest-junit from 15.0.0 to 16.0.0</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/958">#958</a>
build(deps): bump openpgp from 5.7.0 to 5.8.0</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/959">#959</a>
build(deps-dev): bump <code>@​types/node</code> from 18.15.10 to
18.15.12</li>
</ul>
<h2>3.1.2</h2>
<h3>Fixes</h3>
<ul>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/718">#718</a>
Update README.md</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/851">#851</a>
Remove unsupported path_to_write_report argument</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/898">#898</a>
codeql-analysis.yml</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/901">#901</a>
Update README to contain correct information - inputs and negate
feature</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/955">#955</a>
fix: add in all the extra arguments for uploader</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/819">#819</a>
build(deps): bump openpgp from 5.4.0 to 5.5.0</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/835">#835</a>
build(deps): bump node-fetch from 3.2.4 to 3.2.10</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/840">#840</a>
build(deps): bump ossf/scorecard-action from 1.1.1 to 2.0.4</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/841">#841</a>
build(deps): bump <code>@​actions/core</code> from 1.9.1 to 1.10.0</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/843">#843</a>
build(deps): bump <code>@​actions/github</code> from 5.0.3 to 5.1.1</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/869">#869</a>
build(deps): bump node-fetch from 3.2.10 to 3.3.0</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/872">#872</a>
build(deps-dev): bump jest-junit from 13.2.0 to 15.0.0</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/879">#879</a>
build(deps): bump decode-uri-component from 0.2.0 to 0.2.2</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="968872560f"><code>9688725</code></a>
Update README.md</li>
<li><a
href="2112eaec1b"><code>2112eae</code></a>
chore(deps): bump wrapper to 0.0.23 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1644">#1644</a>)</li>
<li><a
href="193421c5b3"><code>193421c</code></a>
fixL use the correct source (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1642">#1642</a>)</li>
<li><a
href="6018df70b0"><code>6018df7</code></a>
fix: update container builds (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1640">#1640</a>)</li>
<li><a
href="eff1a643d6"><code>eff1a64</code></a>
fix: add missing vars (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1638">#1638</a>)</li>
<li><a
href="4582d54fd3"><code>4582d54</code></a>
Update README.md (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1639">#1639</a>)</li>
<li><a
href="bb7467c2bc"><code>bb7467c</code></a>
feat: use wrapper (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1621">#1621</a>)</li>
<li><a
href="1d6059880c"><code>1d60598</code></a>
build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 8.12.2 to 8.13.0 ...</li>
<li><a
href="e587ce276e"><code>e587ce2</code></a>
build(deps-dev): bump <code>@​typescript-eslint/parser</code> from
8.12.2 to 8.13.0 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1635">#1635</a>)</li>
<li><a
href="e43f28e103"><code>e43f28e</code></a>
build(deps-dev): bump <code>@​typescript-eslint/parser</code> from
8.11.0 to 8.12.2 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1628">#1628</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/codecov/codecov-action/compare/v4...v5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=codecov/codecov-action&package-manager=github_actions&previous-version=4&new-version=5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-18 10:25:02 +00:00
dependabot[bot]
32e3d1b480 feat(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.28.2 to 1.28.3 (#219)
Bumps
[github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2)
from 1.28.2 to 1.28.3.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2f70834c69"><code>2f70834</code></a>
Release 2024-11-07</li>
<li><a
href="ef9a3c319f"><code>ef9a3c3</code></a>
Regenerated Clients</li>
<li><a
href="c054fe7021"><code>c054fe7</code></a>
Update endpoints model</li>
<li><a
href="b68675cf59"><code>b68675c</code></a>
Update API model</li>
<li><a
href="1d989f31b0"><code>1d989f3</code></a>
send opt-in query-compatible header where applicable (<a
href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/2891">#2891</a>)</li>
<li><a
href="5d0eb2386a"><code>5d0eb23</code></a>
Merge pull request <a
href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/2889">#2889</a>
from aws/customize-identity-store-exception-message</li>
<li><a
href="a39caf38c1"><code>a39caf3</code></a>
adding new line and inline comments</li>
<li><a
href="2ae3246d27"><code>2ae3246</code></a>
refactor logic to use switch case</li>
<li><a
href="7c4bde0ae1"><code>7c4bde0</code></a>
add changelog</li>
<li><a
href="f01c284662"><code>f01c284</code></a>
regenerating clients</li>
<li>Additional commits viewable in <a
href="https://github.com/aws/aws-sdk-go-v2/compare/config/v1.28.2...config/v1.28.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/aws/aws-sdk-go-v2/config&package-manager=go_modules&previous-version=1.28.2&new-version=1.28.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-08 10:27:01 +00:00
dependabot[bot]
f6db6f56a1 feat(deps): bump google.golang.org/api from 0.204.0 to 0.205.0 (#217)
Bumps
[google.golang.org/api](https://github.com/googleapis/google-api-go-client)
from 0.204.0 to 0.205.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/googleapis/google-api-go-client/releases">google.golang.org/api's
releases</a>.</em></p>
<blockquote>
<h2>v0.205.0</h2>
<h2><a
href="https://github.com/googleapis/google-api-go-client/compare/v0.204.0...v0.205.0">0.205.0</a>
(2024-11-06)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2852">#2852</a>)
(<a
href="e3868f25db">e3868f2</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2855">#2855</a>)
(<a
href="47ea8e4b72">47ea8e4</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2857">#2857</a>)
(<a
href="78320944b8">7832094</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2858">#2858</a>)
(<a
href="eb00c845e2">eb00c84</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2860">#2860</a>)
(<a
href="3347fa1ef8">3347fa1</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md">google.golang.org/api's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/googleapis/google-api-go-client/compare/v0.204.0...v0.205.0">0.205.0</a>
(2024-11-06)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2852">#2852</a>)
(<a
href="e3868f25db">e3868f2</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2855">#2855</a>)
(<a
href="47ea8e4b72">47ea8e4</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2857">#2857</a>)
(<a
href="78320944b8">7832094</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2858">#2858</a>)
(<a
href="eb00c845e2">eb00c84</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2860">#2860</a>)
(<a
href="3347fa1ef8">3347fa1</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="69dcf2750e"><code>69dcf27</code></a>
chore(main): release 0.205.0 (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2853">#2853</a>)</li>
<li><a
href="a01d19843e"><code>a01d198</code></a>
chore(all): upgrade cloud.google.com/go/auth to v0.10.1 (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2861">#2861</a>)</li>
<li><a
href="3347fa1ef8"><code>3347fa1</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2860">#2860</a>)</li>
<li><a
href="eb00c845e2"><code>eb00c84</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2858">#2858</a>)</li>
<li><a
href="78320944b8"><code>7832094</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2857">#2857</a>)</li>
<li><a
href="47ea8e4b72"><code>47ea8e4</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2855">#2855</a>)</li>
<li><a
href="e3868f25db"><code>e3868f2</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2852">#2852</a>)</li>
<li>See full diff in <a
href="https://github.com/googleapis/google-api-go-client/compare/v0.204.0...v0.205.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/api&package-manager=go_modules&previous-version=0.204.0&new-version=0.205.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: James Carnegie <kipz@users.noreply.github.com>
2024-11-07 11:33:32 +00:00
dependabot[bot]
6727d529ed feat(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.28.1 to 1.28.2 (#218)
Bumps
[github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2)
from 1.28.1 to 1.28.2.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="70eb57ac77"><code>70eb57a</code></a>
Release 2024-11-06</li>
<li><a
href="cd2c6b1af3"><code>cd2c6b1</code></a>
Regenerated Clients</li>
<li><a
href="2b2a737b17"><code>2b2a737</code></a>
Update API model</li>
<li><a
href="8c9892f042"><code>8c9892f</code></a>
bump smithy-go codegen to latest (<a
href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/2890">#2890</a>)</li>
<li><a
href="82897becac"><code>82897be</code></a>
fix potential for user-agent lang value mismatch in tests (<a
href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/2888">#2888</a>)</li>
<li><a
href="061540b5a7"><code>061540b</code></a>
Cloudfront - add expire time in signed cookie. (<a
href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/2862">#2862</a>)</li>
<li><a
href="aa3bd1f75d"><code>aa3bd1f</code></a>
fix makefile to not spam releases for feature/dynamodb/attributevalue
(<a
href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/2885">#2885</a>)</li>
<li><a
href="eb96051d5b"><code>eb96051</code></a>
Release 2024-11-01</li>
<li><a
href="7f2d000cf7"><code>7f2d000</code></a>
Regenerated Clients</li>
<li><a
href="8542f2f22d"><code>8542f2f</code></a>
Update endpoints model</li>
<li>Additional commits viewable in <a
href="https://github.com/aws/aws-sdk-go-v2/compare/config/v1.28.1...config/v1.28.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/aws/aws-sdk-go-v2/config&package-manager=go_modules&previous-version=1.28.1&new-version=1.28.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 09:20:05 +00:00
dependabot[bot]
f7f8802e6c feat(deps): bump github.com/containerd/containerd/v2 from 2.0.0-rc.6 to 2.0.0 (#216)
Bumps
[github.com/containerd/containerd/v2](https://github.com/containerd/containerd)
from 2.0.0-rc.6 to 2.0.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/containerd/containerd/releases">github.com/containerd/containerd/v2's
releases</a>.</em></p>
<blockquote>
<h2>containerd 2.0.0</h2>
<p>Welcome to the v2.0.0 release of containerd!</p>
<p>The first major release of containerd 2.x focuses on the continued
stability of
containerd's core feature set with an easy upgrade from containerd 1.x.
This
release includes the stabilization of new features added in the last 1.x
release
as well as the removal of features which were deprecated in 1.x. The
goal is to
support the vast community of containerd users well into the future
along with
their ever increasing deployment footprints and variety of use
cases.</p>
<p>See <a
href="https://github.com/containerd/containerd/blob/main/docs/containerd-2.0.md">containerd
2.0</a> documentation for details on what is new and has changed in this
release.</p>
<h3>Highlights</h3>
<ul>
<li>Allow sections of Plugins to be merged, and not overwritten as
entire sections. (<a
href="https://redirect.github.com/containerd/containerd/pull/9982">#9982</a>)</li>
<li>Add Update API for sandbox controller (<a
href="https://redirect.github.com/containerd/containerd/pull/9903">#9903</a>)</li>
<li>Configure otel from env instead of config.toml (<a
href="https://redirect.github.com/containerd/containerd/pull/8970">#8970</a>)</li>
<li>Enable NRI by default (<a
href="https://redirect.github.com/containerd/containerd/pull/9744">#9744</a>)</li>
<li>Add PluginInfo to introspection API (<a
href="https://redirect.github.com/containerd/containerd/pull/9442">#9442</a>)</li>
<li>Remove overlayfs volatile option on temp mounts (<a
href="https://redirect.github.com/containerd/containerd/pull/9555">#9555</a>)</li>
<li>Expose usage of deprecated features (<a
href="https://redirect.github.com/containerd/containerd/pull/9258">#9258</a>)</li>
<li>Use Intel ISA-L's igzip if available (<a
href="https://redirect.github.com/containerd/containerd/pull/9200">#9200</a>)</li>
<li>Introduce top level config migration (<a
href="https://redirect.github.com/containerd/containerd/pull/9223">#9223</a>)</li>
<li>Add image delete target (<a
href="https://redirect.github.com/containerd/containerd/pull/8989">#8989</a>)</li>
<li>Remove <code>LimitNOFILE</code> from <code>containerd.service</code>
(<a
href="https://redirect.github.com/containerd/containerd/pull/8924">#8924</a>)</li>
<li>Add support for image expiration during garbage collection (<a
href="https://redirect.github.com/containerd/containerd/pull/9022">#9022</a>)</li>
<li>Reduce the contention between ref lock and boltdb lock in content
store (<a
href="https://redirect.github.com/containerd/containerd/pull/8792">#8792</a>)</li>
<li>Remove &quot;containerd.io/restart.logpath&quot; label (<a
href="https://redirect.github.com/containerd/containerd/pull/8264">#8264</a>)</li>
<li>Remove <code>aufs</code> snapshotter (<a
href="https://redirect.github.com/containerd/containerd/pull/8263">#8263</a>)</li>
<li>Fix deadlock during NRI plugin registration (<a
href="https://redirect.github.com/containerd/nri/pull/79">containerd/nri#79</a>)</li>
<li>Support arm64/v9 and minor variants (<a
href="https://redirect.github.com/containerd/platforms/pull/8">containerd/platforms#8</a>)</li>
<li>Fix deadlock when writing to pipe blocks (<a
href="https://redirect.github.com/containerd/ttrpc/pull/168">containerd/ttrpc#168</a>)</li>
</ul>
<h4>Build and Release Toolchain</h4>
<ul>
<li>Generate attestation for artifacts during release (<a
href="https://redirect.github.com/containerd/containerd/pull/10543">#10543</a>)</li>
<li>Remove <code>cri-containerd-*.tar.gz</code> release bundles (<a
href="https://redirect.github.com/containerd/containerd/pull/9096">#9096</a>)</li>
</ul>
<h4>Container Runtime Interface (CRI)</h4>
<ul>
<li>Use 'UserSpecifiedImage' from CRI to set the image-name annotation
(<a
href="https://redirect.github.com/containerd/containerd/pull/10747">#10747</a>)</li>
<li>Fine-grained SupplementalGroups control (<a
href="https://redirect.github.com/containerd/containerd/pull/9737">#9737</a>)</li>
<li>Add support to set loopback to up (<a
href="https://redirect.github.com/containerd/containerd/pull/10238">#10238</a>)</li>
<li>KEP-3857: Recursive Read-only (RRO) mounts (<a
href="https://redirect.github.com/containerd/containerd/pull/9787">#9787</a>)</li>
<li>Add support for multiple subscribers to CRI container events (<a
href="https://redirect.github.com/containerd/containerd/pull/9661">#9661</a>)</li>
<li>Enable CDI by default (<a
href="https://redirect.github.com/containerd/containerd/pull/9621">#9621</a>)</li>
<li>Remove non-sandboxed CRI implementation (<a
href="https://redirect.github.com/containerd/containerd/pull/9228">#9228</a>)</li>
<li>Add support for userns in stateless and stateful pods with idmap
mounts (KEP-127, k8s &gt;= 1.27) (<a
href="https://redirect.github.com/containerd/containerd/pull/8287">#8287</a>)</li>
<li>Use sandboxed CRI by default (<a
href="https://redirect.github.com/containerd/containerd/pull/8994">#8994</a>)</li>
<li>Implement RuntimeConfig CRI call (<a
href="https://redirect.github.com/containerd/containerd/pull/8722">#8722</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/containerd/containerd/blob/main/RELEASES.md">github.com/containerd/containerd/v2's
changelog</a>.</em></p>
<blockquote>
<h1>Versioning and Release</h1>
<p>This document details the versioning and release plan for containerd.
Stability
is a top goal for this project, and we hope that this document and the
processes
it entails will help to achieve that. It covers the release process,
versioning
numbering, backporting, API stability and support horizons.</p>
<p>If you rely on containerd, it would be good to spend time
understanding the
areas of the API that are and are not supported and how they impact your
project in the future.</p>
<p>This document will be considered a living document. Supported
timelines,
backport targets and API stability guarantees will be updated here as
they
change.</p>
<p>If there is something that you require or this document leaves out,
please
reach out by <a
href="https://github.com/containerd/containerd/issues">filing an
issue</a>.</p>
<h2>Releases</h2>
<p>Releases of containerd will be versioned using dotted triples,
similar to
<a href="http://semver.org/">Semantic Version</a>. For the purposes of
this document, we
will refer to the respective components of this triple as
<code>&lt;major&gt;.&lt;minor&gt;.&lt;patch&gt;</code>. The version
number may have additional information,
such as alpha, beta and release candidate qualifications. Such releases
will be
considered &quot;pre-releases&quot;.</p>
<h3>Major and Minor Releases</h3>
<p>Major and minor releases of containerd will be made from main.
Releases of
containerd will be marked with GPG signed tags and announced at
<a
href="https://github.com/containerd/containerd/releases">https://github.com/containerd/containerd/releases</a>.
The tag will be of the
format <code>v&lt;major&gt;.&lt;minor&gt;.&lt;patch&gt;</code> and
should be made with the command <code>git tag -s
v&lt;major&gt;.&lt;minor&gt;.&lt;patch&gt;</code>.</p>
<p>After a minor release, a branch will be created, with the format
<code>release/&lt;major&gt;.&lt;minor&gt;</code> from the minor tag. All
further patch releases will
be done from that branch. For example, once we release
<code>v1.0.0</code>, a branch
<code>release/1.0</code> will be created from that tag. All future patch
releases will be
done against that branch.</p>
<h3>Pre-releases</h3>
<p>Pre-releases, such as alphas, betas and release candidates will be
conducted
from their source branch. For major and minor releases, these releases
will be
done from main. For patch releases, these pre-releases should be done
within
the corresponding release branch.</p>
<p>While pre-releases are done to assist in the stabilization process,
no
guarantees are provided.</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="207ad711ea"><code>207ad71</code></a>
Merge pull request <a
href="https://redirect.github.com/containerd/containerd/issues/10939">#10939</a>
from dmcgowan/prepare-v2.0.0</li>
<li><a
href="03ba4ce1f7"><code>03ba4ce</code></a>
Update release notes for v2.0.0</li>
<li><a
href="f2da3fd688"><code>f2da3fd</code></a>
Update release docs for v2.0.0</li>
<li><a
href="6369206870"><code>6369206</code></a>
Merge pull request <a
href="https://redirect.github.com/containerd/containerd/issues/10954">#10954</a>
from dmcgowan/update-typeurl-2.2.2</li>
<li><a
href="ff09b428e1"><code>ff09b42</code></a>
Update typeurl to v2.2.2</li>
<li><a
href="18caa33fdc"><code>18caa33</code></a>
Merge pull request <a
href="https://redirect.github.com/containerd/containerd/issues/10944">#10944</a>
from containerd/dependabot/github_actions/softprops...</li>
<li><a
href="545605d5b4"><code>545605d</code></a>
Merge pull request <a
href="https://redirect.github.com/containerd/containerd/issues/10945">#10945</a>
from containerd/dependabot/github_actions/google-gi...</li>
<li><a
href="71c274bb81"><code>71c274b</code></a>
Merge pull request <a
href="https://redirect.github.com/containerd/containerd/issues/10947">#10947</a>
from containerd/dependabot/go_modules/github.com/co...</li>
<li><a
href="e841240997"><code>e841240</code></a>
Merge pull request <a
href="https://redirect.github.com/containerd/containerd/issues/10946">#10946</a>
from containerd/dependabot/go_modules/github.com/co...</li>
<li><a
href="9fe6f7c067"><code>9fe6f7c</code></a>
Merge pull request <a
href="https://redirect.github.com/containerd/containerd/issues/10943">#10943</a>
from containerd/dependabot/github_actions/google-gi...</li>
<li>Additional commits viewable in <a
href="https://github.com/containerd/containerd/compare/v2.0.0-rc.6...v2.0.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/containerd/containerd/v2&package-manager=go_modules&previous-version=2.0.0-rc.6&new-version=2.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-06 12:16:34 +00:00
dependabot[bot]
1fd228fb5c feat(deps): bump github.com/open-policy-agent/opa from 0.69.0 to 0.70.0 (#215)
Bumps
[github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa)
from 0.69.0 to 0.70.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/open-policy-agent/opa/releases">github.com/open-policy-agent/opa's
releases</a>.</em></p>
<blockquote>
<h2>v0.70.0</h2>
<p>This release contains a mix of features, performance improvements,
and bugfixes.</p>
<h3>Optimized read mode for OPA's in-memory store (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7125">#7125</a>)</h3>
<p>A new optimized read mode has been added to the default in-memory
store, where data written to the store is eagerly converted
to AST values (the data format used during evaluation). This removes the
time spent converting raw data values to AST
during policy evaluation, thereby improving performance.</p>
<p>The memory footprint of the store will increase, as processed AST
values generally take up more space in memory than the
corresponding raw data values, but overall memory usage of OPA might
remain more stable over time, as pre-converted data
is shared across evaluations and isn't recomputed for each evaluation,
which can cause spikes in memory usage.</p>
<p>This mode can be enabled for <code>opa run</code>, <code>opa
eval</code>, and <code>opa bench</code> by setting the
<code>--optimize-store-for-read-speed</code> flag.</p>
<p>More information about this feature can be found <a
href="https://www.openpolicyagent.org/docs/v0.70.0/policy-performance/#storage-optimization">here</a>.</p>
<p>Co-authored by <a
href="https://github.com/johanfylling"><code>@​johanfylling</code></a>
and <a
href="https://github.com/ashutosh-narkar"><code>@​ashutosh-narkar</code></a>.</p>
<h3>Topdown and Rego</h3>
<ul>
<li>topdown: Use new Inter-Query Value Cache for
<code>json.match_schema</code> built-in function (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7011">#7011</a>)
authored by <a
href="https://github.com/anderseknert"><code>@​anderseknert</code></a>
reported by <a
href="https://github.com/lcarva"><code>@​lcarva</code></a></li>
<li>ast: Fix location text attribute for multi-value rules with
generated body (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7128">#7128</a>)
authored by <a
href="https://github.com/anderseknert"><code>@​anderseknert</code></a></li>
<li>ast: Fix regression in <code>opa check</code> where a file that
referenced non-provided schemas failed validation (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7124">#7124</a>)
authored by <a
href="https://github.com/tjons"><code>@​tjons</code></a></li>
<li>test/cases/testdata: Fix bug in test by replacing unification by
explicit equality check (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7093">#7093</a>)
authored by <a
href="https://github.com/matajoh"><code>@​matajoh</code></a></li>
<li>ast: Replace use of yaml.v2 library with yaml.v3. The earlier
version would parse <code>yes</code>/<code>no</code> values as boolean.
The usage of yaml.v2 in the parser was unintentional and now has been
updated to yaml.v3 (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7090">#7090</a>)
authored by <a
href="https://github.com/anderseknert"><code>@​anderseknert</code></a></li>
</ul>
<h3>Runtime, Tooling, SDK</h3>
<ul>
<li>cmd: Make <code>opa check</code> respect <code>--ignore</code> when
<code>--bundle</code> flag is set (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7136">#7136</a>)
authored by <a
href="https://github.com/anderseknert"><code>@​anderseknert</code></a></li>
<li>server/writer: Properly handle result encoding errors which earlier
on failure would emit logs such as <code>superfluous call to
WriteHeader()</code> while still returning <code>200</code> HTTP status
code. Now, errors encoding the payload properly lead to <code>500</code>
HTTP status code, without extra logs. Also use Header().Set() not
Header().Add() to avoid duplicate content-type headers (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7114">#7114</a>)
authored by <a
href="https://github.com/srenatus"><code>@​srenatus</code></a></li>
<li>cmd: Support <code>file://</code> format for TLS key material file
flags in <code>opa run</code> (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7094">#7094</a>)
authored by <a
href="https://github.com/alexrohozneanu"><code>@​alexrohozneanu</code></a></li>
<li>plugins/rest/azure: Support managed identity for App Service /
Container Apps (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7085">#7085</a>)
reported and authored by <a
href="https://github.com/apc-kamezaki"><code>@​apc-kamezaki</code></a></li>
<li>debug: Fix step-over behaviour when exiting partial rules (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7096">#7096</a>)
authored by <a
href="https://github.com/johanfylling"><code>@​johanfylling</code></a></li>
<li>util+plugins: Fix potential memory leaks with explicit timer
cancellation (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7089">#7089</a>)
authored by <a
href="https://github.com/philipaconrad"><code>@​philipaconrad</code></a></li>
</ul>
<h3>Docs, Website, Ecosystem</h3>
<ul>
<li>docs: Fix OCI example with updated flag used by the ORAS CLI (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7130">#7130</a>)
authored by <a
href="https://github.com/b3n3d17"><code>@​b3n3d17</code></a></li>
<li>docs: Delete Atom editor from supported editor integrations (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7111">#7111</a>)
authored by <a
href="https://github.com/KaranbirSingh7"><code>@​KaranbirSingh7</code></a></li>
<li>docs/website: Add Styra OPA ASP.NET Core SDK integration (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7073">#7073</a>)
authored by <a
href="https://github.com/philipaconrad"><code>@​philipaconrad</code></a></li>
<li>docs/website: Update compatibility information on the rego-cpp
integration (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7078">#7078</a>)
authored by <a
href="https://github.com/matajoh"><code>@​matajoh</code></a></li>
</ul>
<h3>Miscellaneous</h3>
<ul>
<li>Dependency updates; notably:
<ul>
<li>build(deps): bump github.com/containerd/containerd from 1.7.22 to
1.7.23</li>
<li>build(deps): bump github.com/prometheus/client_golang from 1.20.4 to
1.20.5</li>
<li>build(deps): bump golang.org/x/net from 0.29.0 to 0.30.0</li>
<li>build(deps): bump golang.org/x/time from 0.6.0 to 0.7.0</li>
<li>build(deps): bump google.golang.org/grpc from 1.67.0 to 1.67.1</li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md">github.com/open-policy-agent/opa's
changelog</a>.</em></p>
<blockquote>
<h2>0.70.0</h2>
<p>This release contains a mix of features, performance improvements,
and bugfixes.</p>
<h3>Optimized read mode for OPA's in-memory store (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7125">#7125</a>)</h3>
<p>A new optimized read mode has been added to the default in-memory
store, where data written to the store is eagerly converted
to AST values (the data format used during evaluation). This removes the
time spent converting raw data values to AST
during policy evaluation, thereby improving performance.</p>
<p>The memory footprint of the store will increase, as processed AST
values generally take up more space in memory than the
corresponding raw data values, but overall memory usage of OPA might
remain more stable over time, as pre-converted data
is shared across evaluations and isn't recomputed for each evaluation,
which can cause spikes in memory usage.</p>
<p>This mode can be enabled for <code>opa run</code>, <code>opa
eval</code>, and <code>opa bench</code> by setting the
<code>--optimize-store-for-read-speed</code> flag.</p>
<p>More information about this feature can be found <a
href="https://www.openpolicyagent.org/docs/v0.70.0/policy-performance/#storage-optimization">here</a>.</p>
<p>Co-authored by <a
href="https://github.com/johanfylling"><code>@​johanfylling</code></a>
and <a
href="https://github.com/ashutosh-narkar"><code>@​ashutosh-narkar</code></a>.</p>
<h3>Topdown and Rego</h3>
<ul>
<li>topdown: Use new Inter-Query Value Cache for
<code>json.match_schema</code> built-in function (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7011">#7011</a>)
authored by <a
href="https://github.com/anderseknert"><code>@​anderseknert</code></a>
reported by <a
href="https://github.com/lcarva"><code>@​lcarva</code></a></li>
<li>ast: Fix location text attribute for multi-value rules with
generated body (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7128">#7128</a>)
authored by <a
href="https://github.com/anderseknert"><code>@​anderseknert</code></a></li>
<li>ast: Fix regression in <code>opa check</code> where a file that
referenced non-provided schemas failed validation (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7124">#7124</a>)
authored by <a
href="https://github.com/tjons"><code>@​tjons</code></a></li>
<li>test/cases/testdata: Fix bug in test by replacing unification by
explicit equality check (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7093">#7093</a>)
authored by <a
href="https://github.com/matajoh"><code>@​matajoh</code></a></li>
<li>ast: Replace use of yaml.v2 library with yaml.v3. The earlier
version would parse <code>yes</code>/<code>no</code> values as boolean.
The usage of yaml.v2 in the parser was unintentional and now has been
updated to yaml.v3 (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7090">#7090</a>)
authored by <a
href="https://github.com/anderseknert"><code>@​anderseknert</code></a></li>
</ul>
<h3>Runtime, Tooling, SDK</h3>
<ul>
<li>cmd: Make <code>opa check</code> respect <code>--ignore</code> when
<code>--bundle</code> flag is set (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7136">#7136</a>)
authored by <a
href="https://github.com/anderseknert"><code>@​anderseknert</code></a></li>
<li>server/writer: Properly handle result encoding errors which earlier
on failure would emit logs such as <code>superfluous call to
WriteHeader()</code> while still returning <code>200</code> HTTP status
code. Now, errors encoding the payload properly lead to <code>500</code>
HTTP status code, without extra logs. Also use Header().Set() not
Header().Add() to avoid duplicate content-type headers (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7114">#7114</a>)
authored by <a
href="https://github.com/srenatus"><code>@​srenatus</code></a></li>
<li>cmd: Support <code>file://</code> format for TLS key material file
flags in <code>opa run</code> (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7094">#7094</a>)
authored by <a
href="https://github.com/alexrohozneanu"><code>@​alexrohozneanu</code></a></li>
<li>plugins/rest/azure: Support managed identity for App Service /
Container Apps (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7085">#7085</a>)
reported and authored by <a
href="https://github.com/apc-kamezaki"><code>@​apc-kamezaki</code></a></li>
<li>debug: Fix step-over behaviour when exiting partial rules (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7096">#7096</a>)
authored by <a
href="https://github.com/johanfylling"><code>@​johanfylling</code></a></li>
<li>util+plugins: Fix potential memory leaks with explicit timer
cancellation (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7089">#7089</a>)
authored by <a
href="https://github.com/philipaconrad"><code>@​philipaconrad</code></a></li>
</ul>
<h3>Docs, Website, Ecosystem</h3>
<ul>
<li>docs: Fix OCI example with updated flag used by the ORAS CLI (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7130">#7130</a>)
authored by <a
href="https://github.com/b3n3d17"><code>@​b3n3d17</code></a></li>
<li>docs: Delete Atom editor from supported editor integrations (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7111">#7111</a>)
authored by <a
href="https://github.com/KaranbirSingh7"><code>@​KaranbirSingh7</code></a></li>
<li>docs/website: Add Styra OPA ASP.NET Core SDK integration (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7073">#7073</a>)
authored by <a
href="https://github.com/philipaconrad"><code>@​philipaconrad</code></a></li>
<li>docs/website: Update compatibility information on the rego-cpp
integration (<a
href="https://redirect.github.com/open-policy-agent/opa/pull/7078">#7078</a>)
authored by <a
href="https://github.com/matajoh"><code>@​matajoh</code></a></li>
</ul>
<h3>Miscellaneous</h3>
<ul>
<li>Dependency updates; notably:
<ul>
<li>build(deps): bump github.com/containerd/containerd from 1.7.22 to
1.7.23</li>
<li>build(deps): bump github.com/prometheus/client_golang from 1.20.4 to
1.20.5</li>
<li>build(deps): bump golang.org/x/net from 0.29.0 to 0.30.0</li>
<li>build(deps): bump golang.org/x/time from 0.6.0 to 0.7.0</li>
<li>build(deps): bump google.golang.org/grpc from 1.67.0 to 1.67.1</li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2ea031ea04"><code>2ea031e</code></a>
Prepare v0.70.0 release</li>
<li><a
href="6af5e79bd9"><code>6af5e79</code></a>
storage: Optimized read mode for default data storage</li>
<li><a
href="1b797d9c1b"><code>1b797d9</code></a>
Make <code>opa check</code> respect <code>--ignore</code> when
<code>--bundle</code> flag is set (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7137">#7137</a>)</li>
<li><a
href="8e44b98993"><code>8e44b98</code></a>
build(deps): bump actions/setup-go from 5.0.2 to 5.1.0 (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7138">#7138</a>)</li>
<li><a
href="ad6ffdae6a"><code>ad6ffda</code></a>
build(deps): bump actions/checkout from 4.2.1 to 4.2.2 (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7135">#7135</a>)</li>
<li><a
href="67fe53bfbe"><code>67fe53b</code></a>
Update Andrew Peabody to emeritus (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7133">#7133</a>)</li>
<li><a
href="30f374713b"><code>30f3747</code></a>
build(deps): bump github/codeql-action from 3.26.13 to 3.27.0</li>
<li><a
href="f7957bdd73"><code>f7957bd</code></a>
🐛 fix: oras cli changed to --config</li>
<li><a
href="58ec50b4b0"><code>58ec50b</code></a>
Fix location for multivalue rules with generated bodies (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7129">#7129</a>)</li>
<li><a
href="555fe84094"><code>555fe84</code></a>
only check schemas when schemas are provided (<a
href="https://redirect.github.com/open-policy-agent/opa/issues/7124">#7124</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/open-policy-agent/opa/compare/v0.69.0...v0.70.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/open-policy-agent/opa&package-manager=go_modules&previous-version=0.69.0&new-version=0.70.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-04 08:46:23 +00:00
dependabot[bot]
d481832f61 feat(deps): bump google.golang.org/api from 0.203.0 to 0.204.0 (#214)
Bumps
[google.golang.org/api](https://github.com/googleapis/google-api-go-client)
from 0.203.0 to 0.204.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/googleapis/google-api-go-client/releases">google.golang.org/api's
releases</a>.</em></p>
<blockquote>
<h2>v0.204.0</h2>
<h2><a
href="https://github.com/googleapis/google-api-go-client/compare/v0.203.0...v0.204.0">0.204.0</a>
(2024-10-31)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2837">#2837</a>)
(<a
href="343ae0e822">343ae0e</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2839">#2839</a>)
(<a
href="37b271e98c">37b271e</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2841">#2841</a>)
(<a
href="fb3747b8f9">fb3747b</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2842">#2842</a>)
(<a
href="7221d2c798">7221d2c</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2844">#2844</a>)
(<a
href="56c5ddb8ea">56c5ddb</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2845">#2845</a>)
(<a
href="9d5f008642">9d5f008</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2848">#2848</a>)
(<a
href="5bc448d649">5bc448d</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2850">#2850</a>)
(<a
href="7955ec454d">7955ec4</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>transport/grpc:</strong> Pass through cert source to new
auth lib (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2840">#2840</a>)
(<a
href="c67e7c09f9">c67e7c0</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>Mark WithUniverseDomain as stable (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2847">#2847</a>)
(<a
href="29e20f6029">29e20f6</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md">google.golang.org/api's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/googleapis/google-api-go-client/compare/v0.203.0...v0.204.0">0.204.0</a>
(2024-10-31)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2837">#2837</a>)
(<a
href="343ae0e822">343ae0e</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2839">#2839</a>)
(<a
href="37b271e98c">37b271e</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2841">#2841</a>)
(<a
href="fb3747b8f9">fb3747b</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2842">#2842</a>)
(<a
href="7221d2c798">7221d2c</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2844">#2844</a>)
(<a
href="56c5ddb8ea">56c5ddb</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2845">#2845</a>)
(<a
href="9d5f008642">9d5f008</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2848">#2848</a>)
(<a
href="5bc448d649">5bc448d</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2850">#2850</a>)
(<a
href="7955ec454d">7955ec4</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>transport/grpc:</strong> Pass through cert source to new
auth lib (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2840">#2840</a>)
(<a
href="c67e7c09f9">c67e7c0</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>Mark WithUniverseDomain as stable (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2847">#2847</a>)
(<a
href="29e20f6029">29e20f6</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="bcf158044a"><code>bcf1580</code></a>
chore(main): release 0.204.0 (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2838">#2838</a>)</li>
<li><a
href="c67e7c09f9"><code>c67e7c0</code></a>
fix(transport/grpc): pass through cert source to new auth lib (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2840">#2840</a>)</li>
<li><a
href="29e20f6029"><code>29e20f6</code></a>
docs: mark WithUniverseDomain as stable (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2847">#2847</a>)</li>
<li><a
href="4b4eacf4f2"><code>4b4eacf</code></a>
chore: bump auth deps (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2851">#2851</a>)</li>
<li><a
href="7955ec454d"><code>7955ec4</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2850">#2850</a>)</li>
<li><a
href="5bc448d649"><code>5bc448d</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2848">#2848</a>)</li>
<li><a
href="9d5f008642"><code>9d5f008</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2845">#2845</a>)</li>
<li><a
href="48dda95162"><code>48dda95</code></a>
chore(all): update all to 324edc3 (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2843">#2843</a>)</li>
<li><a
href="56c5ddb8ea"><code>56c5ddb</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2844">#2844</a>)</li>
<li><a
href="7221d2c798"><code>7221d2c</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2842">#2842</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/googleapis/google-api-go-client/compare/v0.203.0...v0.204.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/api&package-manager=go_modules&previous-version=0.203.0&new-version=0.204.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-04 08:42:08 +00:00
dependabot[bot]
dbac7405c7 feat(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.28.0 to 1.28.1 (#213)
Bumps
[github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2)
from 1.28.0 to 1.28.1.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6b53348f84"><code>6b53348</code></a>
Release 2024-10-28</li>
<li><a
href="784d2d39b0"><code>784d2d3</code></a>
Regenerated Clients</li>
<li><a
href="7258bd236c"><code>7258bd2</code></a>
Update endpoints model</li>
<li><a
href="f322198c04"><code>f322198</code></a>
Update API model</li>
<li><a
href="b65b80a89b"><code>b65b80a</code></a>
Merge pull request <a
href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/2852">#2852</a>
from RanVaknin/signature-header-parsing-fix</li>
<li><a
href="803614d34f"><code>803614d</code></a>
Fixing changelog description and implementation to use TrimSpace</li>
<li><a
href="b12c8cf885"><code>b12c8cf</code></a>
adding changelog</li>
<li><a
href="f0caa97e86"><code>f0caa97</code></a>
patching GetSignedRequestSignature to cover edge cases with the
signature</li>
<li><a
href="e05890387e"><code>e058903</code></a>
drop service/nimble (<a
href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/2851">#2851</a>)</li>
<li><a
href="896793a682"><code>896793a</code></a>
Release 2024-10-25</li>
<li>Additional commits viewable in <a
href="https://github.com/aws/aws-sdk-go-v2/compare/v1.28.0...config/v1.28.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/aws/aws-sdk-go-v2/config&package-manager=go_modules&previous-version=1.28.0&new-version=1.28.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-04 08:33:57 +00:00
dependabot[bot]
868e44228e feat(deps): bump github.com/containerd/containerd/v2 from 2.0.0-rc.5 to 2.0.0-rc.6 (#212)
Bumps
[github.com/containerd/containerd/v2](https://github.com/containerd/containerd)
from 2.0.0-rc.5 to 2.0.0-rc.6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/containerd/containerd/releases">github.com/containerd/containerd/v2's
releases</a>.</em></p>
<blockquote>
<h2>containerd 2.0.0-rc.6</h2>
<p>Welcome to the v2.0.0-rc.6 release of containerd!
<em>This is a pre-release of containerd</em></p>
<p>The first major release of containerd 2.x focuses on the continued
stability of
containerd's core feature set with an easy upgrade from containerd 1.x.
This
release includes the stabilization of new features added in the last 1.x
release
as well as the removal of features which were deprecated in 1.x. The
goal is to
support the vast community of containerd users well into the future
along with
their ever increasing deployment footprints and variety of use
cases.</p>
<h3>Highlights</h3>
<ul>
<li>Allow sections of Plugins to be merged, and not overwritten as
entire sections. (<a
href="https://redirect.github.com/containerd/containerd/pull/9982">#9982</a>)</li>
<li>Add Update API for sandbox controller (<a
href="https://redirect.github.com/containerd/containerd/pull/9903">#9903</a>)</li>
<li>Configure otel from env instead of config.toml (<a
href="https://redirect.github.com/containerd/containerd/pull/8970">#8970</a>)</li>
<li>Enable NRI by default (<a
href="https://redirect.github.com/containerd/containerd/pull/9744">#9744</a>)</li>
<li>Add PluginInfo to introspection API (<a
href="https://redirect.github.com/containerd/containerd/pull/9442">#9442</a>)</li>
<li>Remove overlayfs volatile option on temp mounts (<a
href="https://redirect.github.com/containerd/containerd/pull/9555">#9555</a>)</li>
<li>Expose usage of deprecated features (<a
href="https://redirect.github.com/containerd/containerd/pull/9258">#9258</a>)</li>
<li>Use Intel ISA-L's igzip if available (<a
href="https://redirect.github.com/containerd/containerd/pull/9200">#9200</a>)</li>
<li>Introduce top level config migration (<a
href="https://redirect.github.com/containerd/containerd/pull/9223">#9223</a>)</li>
<li>Add image delete target (<a
href="https://redirect.github.com/containerd/containerd/pull/8989">#8989</a>)</li>
<li>Remove <code>LimitNOFILE</code> from <code>containerd.service</code>
(<a
href="https://redirect.github.com/containerd/containerd/pull/8924">#8924</a>)</li>
<li>Add support for image expiration during garbage collection (<a
href="https://redirect.github.com/containerd/containerd/pull/9022">#9022</a>)</li>
<li>Reduce the contention between ref lock and boltdb lock in content
store (<a
href="https://redirect.github.com/containerd/containerd/pull/8792">#8792</a>)</li>
<li>Remove &quot;containerd.io/restart.logpath&quot; label (<a
href="https://redirect.github.com/containerd/containerd/pull/8264">#8264</a>)</li>
<li>Remove <code>aufs</code> snapshotter (<a
href="https://redirect.github.com/containerd/containerd/pull/8263">#8263</a>)</li>
<li>Fix deadlock during NRI plugin registration (<a
href="https://redirect.github.com/containerd/nri/pull/79">containerd/nri#79</a>)</li>
<li>Fix deadlock when writing to pipe blocks (<a
href="https://redirect.github.com/containerd/ttrpc/pull/168">containerd/ttrpc#168</a>)</li>
</ul>
<h4>Build and Release Toolchain</h4>
<ul>
<li>Generate attestation for artifacts during release (<a
href="https://redirect.github.com/containerd/containerd/pull/10543">#10543</a>)</li>
<li>Remove <code>cri-containerd-*.tar.gz</code> release bundles (<a
href="https://redirect.github.com/containerd/containerd/pull/9096">#9096</a>)</li>
</ul>
<h4>Container Runtime Interface (CRI)</h4>
<ul>
<li>Use 'UserSpecifiedImage' from CRI to set the image-name annotation
(<a
href="https://redirect.github.com/containerd/containerd/pull/10747">#10747</a>)</li>
<li>Fine-grained SupplementalGroups control (<a
href="https://redirect.github.com/containerd/containerd/pull/9737">#9737</a>)</li>
<li>Add support to set loopback to up (<a
href="https://redirect.github.com/containerd/containerd/pull/10238">#10238</a>)</li>
<li>Add support for multiple subscribers to CRI container events (<a
href="https://redirect.github.com/containerd/containerd/pull/9661">#9661</a>)</li>
<li>Enable CDI by default (<a
href="https://redirect.github.com/containerd/containerd/pull/9621">#9621</a>)</li>
<li>Remove non-sandboxed CRI implementation (<a
href="https://redirect.github.com/containerd/containerd/pull/9228">#9228</a>)</li>
<li>Add support for userns in stateless and stateful pods with idmap
mounts (KEP-127, k8s &gt;= 1.27) (<a
href="https://redirect.github.com/containerd/containerd/pull/8287">#8287</a>)</li>
<li>Use sandboxed CRI by default (<a
href="https://redirect.github.com/containerd/containerd/pull/8994">#8994</a>)</li>
<li>Implement RuntimeConfig CRI call (<a
href="https://redirect.github.com/containerd/containerd/pull/8722">#8722</a>)</li>
<li>Add support for user namespaces (KEP-127) (<a
href="https://redirect.github.com/containerd/containerd/pull/8803">#8803</a>)</li>
<li>Remove CRI v1alpha2 (<a
href="https://redirect.github.com/containerd/containerd/pull/8276">#8276</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b70cce2085"><code>b70cce2</code></a>
Merge pull request <a
href="https://redirect.github.com/containerd/containerd/issues/10887">#10887</a>
from dmcgowan/prepare-v2.0.0-rc.6</li>
<li><a
href="5c65a3d7b0"><code>5c65a3d</code></a>
Update version to v2.0.0-rc.6</li>
<li><a
href="9aa637b22d"><code>9aa637b</code></a>
Update api vendor to latest</li>
<li><a
href="574f0daa3e"><code>574f0da</code></a>
Merge pull request <a
href="https://redirect.github.com/containerd/containerd/issues/10884">#10884</a>
from samuelkarp/nri-0.7.0</li>
<li><a
href="4b9d6c0144"><code>4b9d6c0</code></a>
deps: bump github.com/containerd/nri</li>
<li><a
href="651757761f"><code>6517577</code></a>
Merge pull request <a
href="https://redirect.github.com/containerd/containerd/issues/10864">#10864</a>
from djdongjin/dedup-cri-util</li>
<li><a
href="deccefc8cf"><code>deccefc</code></a>
Merge pull request <a
href="https://redirect.github.com/containerd/containerd/issues/9982">#9982</a>
from rayburgemeestre/merge-toml-configurations-for-p...</li>
<li><a
href="e370f0e31c"><code>e370f0e</code></a>
Merge pull request <a
href="https://redirect.github.com/containerd/containerd/issues/10882">#10882</a>
from samuelkarp/containerd-2.0-guide</li>
<li><a
href="bc819bc97a"><code>bc819bc</code></a>
docs: add command for finding schema 1 images</li>
<li><a
href="18654db80a"><code>18654db</code></a>
Merge pull request <a
href="https://redirect.github.com/containerd/containerd/issues/10881">#10881</a>
from samuelkarp/containerd-2.0-guide</li>
<li>Additional commits viewable in <a
href="https://github.com/containerd/containerd/compare/v2.0.0-rc.5...v2.0.0-rc.6">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/containerd/containerd/v2&package-manager=go_modules&previous-version=2.0.0-rc.5&new-version=2.0.0-rc.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-24 09:05:49 +00:00
dependabot[bot]
31d303ff9c feat(deps): bump google.golang.org/api from 0.202.0 to 0.203.0 (#211)
Bumps
[google.golang.org/api](https://github.com/googleapis/google-api-go-client)
from 0.202.0 to 0.203.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/googleapis/google-api-go-client/releases">google.golang.org/api's
releases</a>.</em></p>
<blockquote>
<h2>v0.203.0</h2>
<h2><a
href="https://github.com/googleapis/google-api-go-client/compare/v0.202.0...v0.203.0">0.203.0</a>
(2024-10-23)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2834">#2834</a>)
(<a
href="c77b5f4cd2">c77b5f4</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md">google.golang.org/api's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/googleapis/google-api-go-client/compare/v0.202.0...v0.203.0">0.203.0</a>
(2024-10-23)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2834">#2834</a>)
(<a
href="c77b5f4cd2">c77b5f4</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="5ca1495a58"><code>5ca1495</code></a>
chore(main): release 0.203.0 (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2835">#2835</a>)</li>
<li><a
href="71d3f5c68e"><code>71d3f5c</code></a>
chore(all): upgrade cloud.google.com/go/auth to v0.9.9 (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2836">#2836</a>)</li>
<li><a
href="c77b5f4cd2"><code>c77b5f4</code></a>
feat(all): auto-regenerate discovery clients (<a
href="https://redirect.github.com/googleapis/google-api-go-client/issues/2834">#2834</a>)</li>
<li>See full diff in <a
href="https://github.com/googleapis/google-api-go-client/compare/v0.202.0...v0.203.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/api&package-manager=go_modules&previous-version=0.202.0&new-version=0.203.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-24 10:03:31 +01:00
dependabot[bot]
b0d6219e34 feat(deps): bump google.golang.org/api from 0.201.0 to 0.202.0 (#210)
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.201.0 to 0.202.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.201.0...v0.202.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-23 13:37:33 +01:00
Jonny Stoten
b4a9283ec3 Update go git (#209) 2024-10-22 15:31:55 +01:00
Jonny Stoten
ca97a23d07 Skip DCO requirement for org members (#208)
Signed-off-by: Jonny Stoten <jonny.stoten@docker.com>
2024-10-22 14:41:44 +01:00
Jonny Stoten
a078fba81d feat: add internal reproducible git checksum builtin (#203)
Adds a new rego builtin `attest.internals.reproducible_git_checksum`.
This is needed for verifying DOI provenance, see
https://github.com/docker/doi-image-policy/blob/main/slsa.md#doi-build-reproducible-git-checksum.

We use https://github.com/go-git/go-git for as much of this as possible,
but it doesn't support the actual archive operation, so we shell out to
`git` for that.

There is some similar unexported code in bashbrew, and we should
probably be using the same code in the build process as we are here.
I'll create a follow-up ticket to sort that out.
2024-10-22 14:30:27 +01:00
Joel Kamp
3cf2d929f7 Merge pull request #206 from docker/feat-add-code-of-conduct
feat: add code of conduct
2024-10-21 10:09:26 -05:00
mrjoelkamp
c7b2ebefac feat: add code of conduct
Signed-off-by: mrjoelkamp <joel.kamp@docker.com>
2024-10-21 10:00:36 -05:00
Joel Kamp
85cf56de49 Merge pull request #205 from docker/feat-add-pr-issue-templates
feat: add pr and issue templates
2024-10-18 10:00:53 -05:00
mrjoelkamp
f426fa367c feat: add pr and issue templates 2024-10-18 09:55:27 -05:00
Joel Kamp
c7c3d23717 Merge pull request #204 from docker/chore-apply-license
chore: apply license headers
2024-10-18 09:45:31 -05:00
mrjoelkamp
01a6a2ab7d refactor: remove copyright year; add newline 2024-10-18 09:25:31 -05:00
mrjoelkamp
6fd73fe45d chore: add notice 2024-10-17 14:08:33 -05:00
mrjoelkamp
0215b620cd chore: apply license headers 2024-10-17 13:43:30 -05:00
Joel Kamp
79bbc9b55b Merge pull request #198 from docker/dependabot/go_modules/github.com/sigstore/sigstore/pkg/signature/kms/aws-1.8.10
feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/aws from 1.8.9 to 1.8.10
2024-10-17 08:31:45 -05:00
Joel Kamp
47669993c6 Merge branch 'main' into dependabot/go_modules/github.com/sigstore/sigstore/pkg/signature/kms/aws-1.8.10 2024-10-17 08:25:14 -05:00
Joel Kamp
7414fb7339 Merge pull request #199 from docker/dependabot/go_modules/github.com/sigstore/sigstore/pkg/signature/kms/gcp-1.8.10
feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/gcp from 1.8.9 to 1.8.10
2024-10-17 08:24:47 -05:00
dependabot[bot]
0e1005d0f7 feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/aws
Bumps [github.com/sigstore/sigstore/pkg/signature/kms/aws](https://github.com/sigstore/sigstore) from 1.8.9 to 1.8.10.
- [Release notes](https://github.com/sigstore/sigstore/releases)
- [Commits](https://github.com/sigstore/sigstore/compare/v1.8.9...v1.8.10)

---
updated-dependencies:
- dependency-name: github.com/sigstore/sigstore/pkg/signature/kms/aws
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-17 13:18:42 +00:00
dependabot[bot]
94f69c75d2 feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/gcp
Bumps [github.com/sigstore/sigstore/pkg/signature/kms/gcp](https://github.com/sigstore/sigstore) from 1.8.9 to 1.8.10.
- [Release notes](https://github.com/sigstore/sigstore/releases)
- [Commits](https://github.com/sigstore/sigstore/compare/v1.8.9...v1.8.10)

---
updated-dependencies:
- dependency-name: github.com/sigstore/sigstore/pkg/signature/kms/gcp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-17 13:18:39 +00:00
Joel Kamp
b2e8166079 Merge pull request #200 from docker/dependabot/go_modules/github.com/sigstore/sigstore-1.8.10
feat(deps): bump github.com/sigstore/sigstore from 1.8.9 to 1.8.10
2024-10-17 08:16:22 -05:00
Joel Kamp
8c4ee60f50 Merge branch 'main' into dependabot/go_modules/github.com/sigstore/sigstore-1.8.10 2024-10-17 08:12:39 -05:00
mrjoelkamp
9b6234f0ae chore: go mod tidy 2024-10-17 08:12:10 -05:00
Joel Kamp
17b0978b44 Merge pull request #201 from docker/feat--add-verifier-version-to-vsa
feat: add verifier version to vsa
2024-10-17 08:09:17 -05:00
Joel Kamp
7ff20a9328 Merge branch 'main' into feat--add-verifier-version-to-vsa 2024-10-17 08:03:47 -05:00
James Carnegie
273b61ebd6 Merge branch 'main' into dependabot/go_modules/github.com/sigstore/sigstore-1.8.10 2024-10-17 09:36:58 +01:00
dependabot[bot]
eda0b23910 feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#202)
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.43 to 1.28.0.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.43...v1.28.0)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-17 09:36:43 +01:00
mrjoelkamp
4a82bb9981 feat: add version checker test 2024-10-16 15:18:34 -05:00
mrjoelkamp
84c0b116a7 feat: add verifier version to vsa 2024-10-16 12:01:31 -05:00
James Carnegie
16f65fefeb Merge branch 'main' into dependabot/go_modules/github.com/sigstore/sigstore-1.8.10 2024-10-16 10:13:44 +01:00
dependabot[bot]
e39a4ea9f3 feat(deps): bump google.golang.org/api from 0.200.0 to 0.201.0 (#197)
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.200.0 to 0.201.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.200.0...v0.201.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-16 10:13:11 +01:00
dependabot[bot]
2e4f8f79bd feat(deps): bump github.com/sigstore/sigstore from 1.8.9 to 1.8.10
Bumps [github.com/sigstore/sigstore](https://github.com/sigstore/sigstore) from 1.8.9 to 1.8.10.
- [Release notes](https://github.com/sigstore/sigstore/releases)
- [Commits](https://github.com/sigstore/sigstore/compare/v1.8.9...v1.8.10)

---
updated-dependencies:
- dependency-name: github.com/sigstore/sigstore
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-16 09:03:50 +00:00
James Carnegie
da667de610 feat: support arbitrary rego input parameters (#196)
* feat: support arbitrary rego input parameters
2024-10-15 16:07:26 +01:00
Joel Kamp
7027d2d054 Merge pull request #188 from docker/dependabot/go_modules/github.com/sigstore/cosign/v2-2.4.1
feat(deps): bump github.com/sigstore/cosign/v2 from 2.4.0 to 2.4.1
2024-10-15 09:37:02 -05:00
mrjoelkamp
163c1828e3 chore: go mod tidy 2024-10-15 09:28:32 -05:00
dependabot[bot]
168a574c15 feat(deps): bump github.com/sigstore/cosign/v2 from 2.4.0 to 2.4.1
Bumps [github.com/sigstore/cosign/v2](https://github.com/sigstore/cosign) from 2.4.0 to 2.4.1.
- [Release notes](https://github.com/sigstore/cosign/releases)
- [Changelog](https://github.com/sigstore/cosign/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sigstore/cosign/compare/v2.4.0...v2.4.1)

---
updated-dependencies:
- dependency-name: github.com/sigstore/cosign/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-15 14:27:14 +00:00
Joel Kamp
ad2f8befa2 Merge pull request #195 from docker/dependabot/go_modules/google.golang.org/api-0.200.0
feat(deps): bump google.golang.org/api from 0.199.0 to 0.200.0
2024-10-15 08:53:56 -05:00
dependabot[bot]
8460357880 feat(deps): bump google.golang.org/api from 0.199.0 to 0.200.0
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.199.0 to 0.200.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.199.0...v0.200.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-15 13:44:46 +00:00
Joel Kamp
994240018e Merge pull request #187 from docker/dependabot/go_modules/github.com/containerd/containerd/v2-2.0.0-rc.5
feat(deps): bump github.com/containerd/containerd/v2 from 2.0.0-rc.4 to 2.0.0-rc.5
2024-10-15 08:42:03 -05:00
Joel Kamp
5c51ee7c19 Merge pull request #194 from docker/dependabot/go_modules/github.com/aws/aws-sdk-go-v2/config-1.27.43
feat(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.27.39 to 1.27.43
2024-10-15 08:36:49 -05:00
Joel Kamp
8ae43ba5e9 Merge branch 'main' into dependabot/go_modules/github.com/containerd/containerd/v2-2.0.0-rc.5 2024-10-15 08:33:48 -05:00
dependabot[bot]
ec659e62cd feat(deps): bump github.com/aws/aws-sdk-go-v2/config
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.39 to 1.27.43.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.39...config/v1.27.43)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-09 08:42:48 +00:00
Joel Kamp
2d7f6cae3c Merge pull request #191 from docker/feat-vsa-input-attestations
feat: vsa input attestations
2024-10-08 08:30:06 -05:00
mrjoelkamp
a686de72fd feat: add input atts to result summary 2024-10-07 15:07:21 -05:00
mrjoelkamp
d58ce0c600 feat: add reference wrapper for envelope 2024-10-07 13:34:04 -05:00
dependabot[bot]
bf33de5b48 feat(deps): bump github.com/theupdateframework/go-tuf/v2 (#186)
Bumps [github.com/theupdateframework/go-tuf/v2](https://github.com/theupdateframework/go-tuf) from 2.0.1 to 2.0.2.
- [Release notes](https://github.com/theupdateframework/go-tuf/releases)
- [Changelog](https://github.com/theupdateframework/go-tuf/blob/master/.goreleaser.yaml)
- [Commits](https://github.com/theupdateframework/go-tuf/compare/v2.0.1...v2.0.2)

---
updated-dependencies:
- dependency-name: github.com/theupdateframework/go-tuf/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-02 10:05:46 +01:00
dependabot[bot]
b8ca85152d feat(deps): bump github.com/containerd/containerd/v2
Bumps [github.com/containerd/containerd/v2](https://github.com/containerd/containerd) from 2.0.0-rc.4 to 2.0.0-rc.5.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v2.0.0-rc.4...v2.0.0-rc.5)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-02 08:36:57 +00:00
Joel Kamp
e06d8736df Merge pull request #182 from docker/dependabot/go_modules/github.com/aws/aws-sdk-go-v2/config-1.27.39
feat(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.27.38 to 1.27.39
2024-10-01 16:02:00 -05:00
Joel Kamp
fcf98ebc3f Merge branch 'main' into dependabot/go_modules/github.com/aws/aws-sdk-go-v2/config-1.27.39 2024-10-01 15:46:46 -05:00
Joel Kamp
acd8d427a1 Merge pull request #185 from docker/dependabot/go_modules/github.com/open-policy-agent/opa-0.69.0
feat(deps): bump github.com/open-policy-agent/opa from 0.68.0 to 0.69.0
2024-10-01 15:46:34 -05:00
Joel Kamp
f2f13933df Merge branch 'main' into dependabot/go_modules/github.com/open-policy-agent/opa-0.69.0 2024-10-01 15:42:13 -05:00
Joel Kamp
503410bb7b Merge pull request #184 from docker/dependabot/go_modules/github.com/theupdateframework/go-tuf/v2-2.0.1
feat(deps): bump github.com/theupdateframework/go-tuf/v2 from 2.0.0 to 2.0.1
2024-10-01 15:41:54 -05:00
dependabot[bot]
ac04e8a9ea feat(deps): bump github.com/open-policy-agent/opa from 0.68.0 to 0.69.0
Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.68.0 to 0.69.0.
- [Release notes](https://github.com/open-policy-agent/opa/releases)
- [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-policy-agent/opa/compare/v0.68.0...v0.69.0)

---
updated-dependencies:
- dependency-name: github.com/open-policy-agent/opa
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-01 08:30:48 +00:00
dependabot[bot]
e3927acf17 feat(deps): bump github.com/theupdateframework/go-tuf/v2
Bumps [github.com/theupdateframework/go-tuf/v2](https://github.com/theupdateframework/go-tuf) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/theupdateframework/go-tuf/releases)
- [Changelog](https://github.com/theupdateframework/go-tuf/blob/master/.goreleaser.yaml)
- [Commits](https://github.com/theupdateframework/go-tuf/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: github.com/theupdateframework/go-tuf/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-01 08:30:33 +00:00
James Carnegie
c0510fb76c Support images as well as indexes in ImageDetailResolvers (#183)
* build: Generate test data for unsigned and no provenance image indexes
* feat: Add function to build index without SBOM or provenance for linux/amd64 platform
* feat: add build_image function to build image without SBOM or provenance for linux/amd64
* feat: Rename NO_SBOM_NO_PROVENANCE_INDEX_DIR to UNSIGNED_IMAGE_DIR
* feat: support images in details resolvers
2024-09-30 20:53:13 +01:00
dependabot[bot]
251506fd9b feat(deps): bump github.com/aws/aws-sdk-go-v2/config
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.38 to 1.27.39.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.38...config/v1.27.39)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-30 08:47:36 +00:00
dependabot[bot]
5e16b97e02 feat(deps): bump google.golang.org/api from 0.198.0 to 0.199.0 (#181)
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.198.0 to 0.199.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.198.0...v0.199.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-27 15:11:28 +01:00
dependabot[bot]
0ff28b2deb feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#180)
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.35 to 1.27.38.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.35...config/v1.27.38)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-27 15:05:53 +01:00
Jonny Stoten
4ca962b70c Add function for parsing DOI definition files (#172)
Add a Rego builtin called `attest.internals.parse_library_definition`
for parsing the DOI definition files in
https://github.com/docker-library/official-images/tree/master/library.
This will allow us to verify DOI provenance fields against these files
which are the source of truth for DOI images.

This function just defers to
https://github.com/docker-library/bashbrew/blob/master/manifest/rfc2822.go.
2024-09-27 12:32:24 +01:00
Joel Kamp
2a4bef091e Merge pull request #179 from docker/fix-sign-unsigned-statements
fix: only sign statements
2024-09-26 10:02:41 -05:00
mrjoelkamp
bb0843cd51 fix: only sign statements 2024-09-24 15:12:46 -05:00
David Dooling
203577e965 Remove long-term aspiration from README (#174) 2024-09-20 09:06:02 -05:00
James Carnegie
a98604bdd5 chore: add rekor prod TUF system test (#176) 2024-09-20 11:02:36 +01:00
dependabot[bot]
02b8063d71 feat(deps): bump google.golang.org/api from 0.197.0 to 0.198.0 (#175)
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.197.0 to 0.198.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.197.0...v0.198.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-20 10:01:47 +01:00
Joel Kamp
dcf5c578dd Merge pull request #173 from docker/feat-support-containerd-subject-annotations
feat: support containerd subject annotations
2024-09-19 16:03:32 -05:00
mrjoelkamp
0378c94226 test: make test layouts smaller 2024-09-19 15:36:20 -05:00
mrjoelkamp
fd4e741a1f feat: support containerd subject annotations 2024-09-19 15:10:56 -05:00
James Carnegie
2ace988b1c chore: add test for RegoFnOpts (#171) 2024-09-19 13:54:10 +01:00
dependabot[bot]
be7a17f214 feat(deps): bump github.com/sigstore/sigstore from 1.8.8 to 1.8.9 (#169)
Bumps [github.com/sigstore/sigstore](https://github.com/sigstore/sigstore) from 1.8.8 to 1.8.9.
- [Release notes](https://github.com/sigstore/sigstore/releases)
- [Commits](https://github.com/sigstore/sigstore/compare/v1.8.8...v1.8.9)

---
updated-dependencies:
- dependency-name: github.com/sigstore/sigstore
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-19 11:24:30 +01:00
dependabot[bot]
1a49b5c068 chore(deps): bump actions/create-github-app-token from 1.10.4 to 1.11.0 (#164)
Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 1.10.4 to 1.11.0.
- [Release notes](https://github.com/actions/create-github-app-token/releases)
- [Commits](3378cda945...5d869da34e)

---
updated-dependencies:
- dependency-name: actions/create-github-app-token
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-19 11:18:57 +01:00
James Carnegie
3e82338649 refactor: remove explicit closures. expose rego fns (#170) 2024-09-19 11:04:00 +01:00
James Carnegie
4a70e5ae36 Add platform filtering support to mapping.yml (#167)
* chore!: rename package config -> mapping
* feat: add platform filtering support to mapping.yml
2024-09-18 21:11:55 +01:00
James Carnegie
05caa959c4 Use a Factory to create signature verifiers at policy evaluation time (#165)
* Make verifiers composable

* fix: remove unused code and improve signature verification logic

* fix: simplify abstractions and renamed some things

* fix: improve tl interface.

* fix: sort out signer/verifier
2024-09-18 13:34:10 +01:00
dependabot[bot]
5335a56da1 feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#168)
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.33 to 1.27.35.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.33...config/v1.27.35)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 09:43:50 +01:00
Jonny Stoten
7fffbf9d3f Suppress logs from ecr credential helper (#163)
This gets rid of those annoying logs like:

```
time="2024-09-11T15:22:04Z" level=error msg="Error parsing the serverURL" error="docker-credential-ecr-login can only be used with Amazon Elastic Container Registry." serverURL="localhost:5000"
time="2024-09-11T15:22:04Z" level=error msg="Error parsing the serverURL" error="docker-credential-ecr-login can only be used with Amazon Elastic Container Registry." serverURL="localhost:5000"
time="2024-09-11T15:22:04Z" level=error msg="Error parsing the serverURL" error="docker-credential-ecr-login can only be used with Amazon Elastic Container Registry." serverURL="localhost:5000"
time="2024-09-11T15:22:04Z" level=error msg="Error parsing the serverURL" error="docker-credential-ecr-login can only be used with Amazon Elastic Container Registry." serverURL="localhost:5000"
```
2024-09-11 16:36:28 +01:00
dependabot[bot]
070fa33d0d feat(deps): bump google.golang.org/api from 0.196.0 to 0.197.0 (#162)
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.196.0 to 0.197.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.196.0...v0.197.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-11 12:27:09 +01:00
Jonny Stoten
602295492f fix: regexes for autolabeler (#160)
* Fix regexes for autolabeler

* Remove branch autolabeler rules
2024-09-10 21:02:05 +01:00
Jonny Stoten
6edcc3d5d7 Test on Go 1.23 as well (#161) 2024-09-10 17:40:43 +01:00
Jonny Stoten
c029bcfbaa feat: add a prefix path to TUF client (#159)
This is to allow us to store new policy files in the production TUF repository
under a testing delegation, and for clients to opt-in to using this testing
delegation when retrieving policy from TUF.

If the prefix path is set, it is prepended to every target path on download
with path.Join. For example, if the prefix path is testing and we download
the target a/b, the TUF client with actually download testing/a/b.

Also get the latest testdata from tuf-dev.
2024-09-10 17:40:20 +01:00
James Carnegie
206b33c5d9 fix: expose version and user agent to consumers (#158) 2024-09-09 12:08:01 -05:00
James Carnegie
b4e6767cc6 feature!: support for setting HTTP User-Agent header (#157)
* feature!: support for setting HTTP User-Agent header

* fix lint

* fix e2e

* refactor: move http.go to internal/util/useragent package and rename functions to Get and Set

* Move packages and use attest version
2024-09-09 14:22:17 +01:00
James Carnegie
ed0ae8ecf6 fix: verify mapped image name against subjects (#156)
* fix: verify mapped image name against subjects
2024-09-05 08:08:55 -05:00
Joel Kamp
a363be7f3a Merge pull request #150 from docker/dependabot/go_modules/google.golang.org/api-0.196.0
feat(deps): bump google.golang.org/api from 0.195.0 to 0.196.0
2024-09-04 16:47:27 -05:00
dependabot[bot]
99846a3483 feat(deps): bump google.golang.org/api from 0.195.0 to 0.196.0
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.195.0 to 0.196.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.195.0...v0.196.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-04 21:43:32 +00:00
Joel Kamp
f760b12bb2 Merge pull request #151 from docker/dependabot/go_modules/github.com/sigstore/sigstore/pkg/signature/kms/gcp-1.8.9
feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/gcp from 1.8.8 to 1.8.9
2024-09-04 16:41:22 -05:00
Joel Kamp
bab474669f Merge pull request #155 from docker/dependabot/go_modules/github.com/aws/aws-sdk-go-v2/config-1.27.33
feat(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.27.31 to 1.27.33
2024-09-04 16:40:18 -05:00
dependabot[bot]
0705a71115 feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/gcp
Bumps [github.com/sigstore/sigstore/pkg/signature/kms/gcp](https://github.com/sigstore/sigstore) from 1.8.8 to 1.8.9.
- [Release notes](https://github.com/sigstore/sigstore/releases)
- [Commits](https://github.com/sigstore/sigstore/compare/v1.8.8...v1.8.9)

---
updated-dependencies:
- dependency-name: github.com/sigstore/sigstore/pkg/signature/kms/gcp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-04 21:37:46 +00:00
dependabot[bot]
b00e02af01 feat(deps): bump github.com/aws/aws-sdk-go-v2/config
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.31 to 1.27.33.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.31...config/v1.27.33)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-04 21:36:36 +00:00
Joel Kamp
ff53657cc9 Merge pull request #153 from docker/dependabot/github_actions/actions/create-github-app-token-1.10.4
chore(deps): bump actions/create-github-app-token from 1.10.3 to 1.10.4
2024-09-04 16:34:38 -05:00
Joel Kamp
c8383f3f5a Merge pull request #149 from docker/dependabot/go_modules/github.com/sigstore/sigstore/pkg/signature/kms/aws-1.8.9
feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/aws from 1.8.8 to 1.8.9
2024-09-04 16:33:50 -05:00
Joel Kamp
dc247bd348 Merge pull request #143 from docker/dependabot/go_modules/github.com/open-policy-agent/opa-0.68.0
feat(deps): bump github.com/open-policy-agent/opa from 0.67.1 to 0.68.0
2024-09-04 16:33:28 -05:00
Joel Kamp
ce7d173150 Merge pull request #154 from docker/feat--add-slsa-v1-predicate-type
feat: add slsa v1 predicate type
2024-09-04 16:32:21 -05:00
mrjoelkamp
fb69d9a09b feat: add slsa v1 predicate type 2024-09-04 16:15:56 -05:00
James Carnegie
48e58a9115 Verify input image/platform against attestation subjects before passing to rego (#148)
* feat: verify subjects before passing to rego
2024-09-04 10:20:00 +01:00
dependabot[bot]
bfacaf1de0 chore(deps): bump actions/create-github-app-token from 1.10.3 to 1.10.4
Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 1.10.3 to 1.10.4.
- [Release notes](https://github.com/actions/create-github-app-token/releases)
- [Commits](31c86eb3b3...3378cda945)

---
updated-dependencies:
- dependency-name: actions/create-github-app-token
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-04 08:46:09 +00:00
dependabot[bot]
67ad27ac22 feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/aws
Bumps [github.com/sigstore/sigstore/pkg/signature/kms/aws](https://github.com/sigstore/sigstore) from 1.8.8 to 1.8.9.
- [Release notes](https://github.com/sigstore/sigstore/releases)
- [Commits](https://github.com/sigstore/sigstore/compare/v1.8.8...v1.8.9)

---
updated-dependencies:
- dependency-name: github.com/sigstore/sigstore/pkg/signature/kms/aws
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-04 08:41:30 +00:00
James Carnegie
41847ef238 fix: escape ! remove .* (global match) (#146) 2024-09-03 12:24:26 +01:00
James Carnegie
1f806f33a8 feat: validate mapping files on load (#147) 2024-09-03 12:21:24 +01:00
James Carnegie
8982778507 refactor! remove pkg directory (#145)
* refactor!: remove pkg directory

* chore: include breaking changes in draft
2024-09-02 16:17:50 +01:00
James Carnegie
23849c1c2e fix: use canonical names inside TUF fetcher (#144)
* fix: use canonical names inside TUF fetcher
* keep hold of reference to Config
2024-08-30 17:03:29 +01:00
dependabot[bot]
16834292de feat(deps): bump github.com/open-policy-agent/opa from 0.67.1 to 0.68.0
Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.67.1 to 0.68.0.
- [Release notes](https://github.com/open-policy-agent/opa/releases)
- [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-policy-agent/opa/compare/v0.67.1...v0.68.0)

---
updated-dependencies:
- dependency-name: github.com/open-policy-agent/opa
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-30 08:37:19 +00:00
dependabot[bot]
bada1df262 feat(deps): bump google.golang.org/api from 0.194.0 to 0.195.0 (#139)
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.194.0 to 0.195.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.194.0...v0.195.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: James Carnegie <kipz@users.noreply.github.com>
2024-08-30 09:29:19 +01:00
James Carnegie
4778d3de6a fix: tuf oci image parsing (#142)
* fix: tuf oci image parsing
2024-08-29 12:27:13 -05:00
James Carnegie
a4ac09e7da refactor! don't use ctx for policy evaluator (#140)
* refactor! don't use ctx for policy evaluator
2024-08-29 17:43:45 +01:00
Joel Kamp
9250552c5b Merge pull request #138 from docker/feat-add-tuf-resolver-tests
feat: add policy resolver tests
2024-08-29 10:28:34 -05:00
mrjoelkamp
2acc30693f fix: remove mock tuf client output 2024-08-29 10:03:07 -05:00
mrjoelkamp
5db1b5c4c1 feat: add tuf resolver test 2024-08-28 17:08:46 -05:00
Jonny Stoten
6f94d59a96 refactor!: add policy.Resolver struct to reduce parameters (#130)
* Add `policy.Resolver` struct to reduce parameters

* Pass image name directly rather than resolver

* Move policy match stuff to its own file
2024-08-28 11:27:00 +01:00
dependabot[bot]
95319494b5 feat(deps): bump github.com/testcontainers/testcontainers-go/modules/registry (#127)
Bumps [github.com/testcontainers/testcontainers-go/modules/registry](https://github.com/testcontainers/testcontainers-go) from 0.32.0 to 0.33.0.
- [Release notes](https://github.com/testcontainers/testcontainers-go/releases)
- [Commits](https://github.com/testcontainers/testcontainers-go/compare/v0.32.0...v0.33.0)

---
updated-dependencies:
- dependency-name: github.com/testcontainers/testcontainers-go/modules/registry
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: James Carnegie <kipz@users.noreply.github.com>
2024-08-28 09:58:09 +01:00
dependabot[bot]
64046df6f8 feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#134)
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.28 to 1.27.31.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.28...config/v1.27.31)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-28 08:57:31 +00:00
dependabot[bot]
57b6df0ab5 feat(deps): bump google.golang.org/api from 0.192.0 to 0.194.0 (#131)
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.192.0 to 0.194.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.192.0...v0.194.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-28 09:56:53 +01:00
dependabot[bot]
857be568b5 feat(deps): bump github.com/Masterminds/semver/v3 from 3.2.1 to 3.3.0 (#136)
Bumps [github.com/Masterminds/semver/v3](https://github.com/Masterminds/semver) from 3.2.1 to 3.3.0.
- [Release notes](https://github.com/Masterminds/semver/releases)
- [Changelog](https://github.com/Masterminds/semver/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Masterminds/semver/compare/v3.2.1...v3.3.0)

---
updated-dependencies:
- dependency-name: github.com/Masterminds/semver/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-28 09:55:28 +01:00
James Carnegie
9d39c5ae3d feat!: remove MockTUFClient (#135)
* feat! remove MockTUFClient

*Breaking*
- use LocalPolicyDir and nil TUFClient instead

Other:
- add stateful Verifier
2024-08-28 09:53:52 +01:00
Jonny Stoten
aed959f858 fix: use a client pointing at Docker's TUF by default (#104)
`policy.Options` now contains the arguments to `tuf.Client`'s constructor rather than an actual Client. If these arguments are not provided, defaults pointing at Docker's TUF repo will be used. An actual TUF client can be passed in on the context (which is useful for testing). If this is not provided `attest.Verify` will create a TUF client using the options on `policy.Options`.

---------

Co-authored-by: Joel Kamp <joel.kamp@docker.com>
2024-08-23 09:33:30 +01:00
James Carnegie
802725caf0 feat: add purl details to policy inputs (#129) 2024-08-21 12:01:11 -05:00
Joel Kamp
9c3f267870 Merge pull request #126 from docker/dependabot/go_modules/go_modules-56f2e24de8
feat(deps): bump github.com/docker/docker from 27.1.0+incompatible to 27.1.1+incompatible in the go_modules group
2024-08-16 09:10:37 -05:00
Joel Kamp
6cc9191e1e Merge branch 'main' into dependabot/go_modules/go_modules-56f2e24de8 2024-08-16 09:06:27 -05:00
Joel Kamp
7ce2817111 Merge pull request #123 from docker/dependabot/go_modules/google.golang.org/api-0.192.0
feat(deps): bump google.golang.org/api from 0.191.0 to 0.192.0
2024-08-16 09:06:00 -05:00
dependabot[bot]
a60aab9338 feat(deps): bump github.com/docker/docker in the go_modules group
Bumps the go_modules group with 1 update: [github.com/docker/docker](https://github.com/docker/docker).


Updates `github.com/docker/docker` from 27.1.0+incompatible to 27.1.1+incompatible
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v27.1.0...v27.1.1)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: indirect
  dependency-group: go_modules
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-16 14:05:57 +00:00
Joel Kamp
2ef3a158ae Merge branch 'main' into dependabot/go_modules/google.golang.org/api-0.192.0 2024-08-16 09:04:20 -05:00
Joel Kamp
4f163f4283 Merge pull request #125 from docker/dependabot/go_modules/github.com/aws/aws-sdk-go-v2/config-1.27.28
feat(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.27.27 to 1.27.28
2024-08-16 09:03:58 -05:00
dependabot[bot]
74e8d8beb3 feat(deps): bump github.com/aws/aws-sdk-go-v2/config
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.27 to 1.27.28.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.27...config/v1.27.28)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-16 08:19:46 +00:00
Joel Kamp
a4a0bf3cbe Merge pull request #124 from docker/feat-generate-vsa-policy-uri
feat: add `digest` and `downloadLocation` to VSA policy
2024-08-14 16:50:16 -05:00
mrjoelkamp
52499053d2 feat: add no policy file error 2024-08-14 16:25:41 -05:00
mrjoelkamp
5f17f97229 test: change test to use yaml file instead 2024-08-14 16:13:36 -05:00
mrjoelkamp
8d8f09661f test: add mapping no rego test 2024-08-14 16:10:54 -05:00
mrjoelkamp
059ee8926c refactor: move fullURL only needed for DefaultFetcher 2024-08-14 15:27:02 -05:00
mrjoelkamp
cb47507650 chore: pr comments 2024-08-14 15:01:01 -05:00
Joel Kamp
7c0966de81 Update README.md
Co-authored-by: David Dooling <141646279+whalelines@users.noreply.github.com>
2024-08-14 14:39:06 -05:00
mrjoelkamp
2bf7dec72e feat: add policy.downloadLocation 2024-08-14 12:52:36 -05:00
mrjoelkamp
6de792c1b5 docs: update README with policy.digest 2024-08-14 11:33:15 -05:00
mrjoelkamp
d2a8348ae8 feat: generate vsa policy value from file 2024-08-14 10:57:15 -05:00
dependabot[bot]
881e9d9582 feat(deps): bump google.golang.org/api from 0.191.0 to 0.192.0
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.191.0 to 0.192.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.191.0...v0.192.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-14 08:58:17 +00:00
Joel Kamp
8c6df28540 Merge pull request #122 from docker/feat-mirror-empty-config-image
feat: mirror empty config image
2024-08-13 10:09:35 -05:00
mrjoelkamp
5162cfa404 refactor: ensure tests are in correct pkg 2024-08-13 10:03:33 -05:00
mrjoelkamp
72f6517b2c refactor: move empty config image test 2024-08-13 08:26:36 -05:00
mrjoelkamp
84cadeb97e feat: output comments 2024-08-13 08:13:27 -05:00
mrjoelkamp
57a61cc266 fix: e2e auth test 2024-08-12 16:54:44 -05:00
mrjoelkamp
5a772633b0 feat: use EmptyConfigImage for mirror 2024-08-12 16:43:42 -05:00
mrjoelkamp
1febc55a19 fix: cyclical imports 2024-08-12 16:36:18 -05:00
mrjoelkamp
0db96d56aa fix: err check not needed 2024-08-12 14:20:24 -05:00
Joel Kamp
d97d20eb93 Merge pull request #121 from docker/dependabot/go_modules/google.golang.org/api-0.191.0
feat(deps): bump google.golang.org/api from 0.190.0 to 0.191.0
2024-08-08 15:28:52 -05:00
Joel Kamp
42390b5fc2 Merge branch 'main' into dependabot/go_modules/google.golang.org/api-0.191.0 2024-08-08 15:18:54 -05:00
Joel Kamp
70e6345942 Merge pull request #119 from docker/dependabot/go_modules/github.com/sigstore/cosign/v2-2.4.0
feat(deps): bump github.com/sigstore/cosign/v2 from 2.3.0 to 2.4.0
2024-08-08 15:18:19 -05:00
dependabot[bot]
f853875eea feat(deps): bump github.com/sigstore/cosign/v2 from 2.3.0 to 2.4.0
Bumps [github.com/sigstore/cosign/v2](https://github.com/sigstore/cosign) from 2.3.0 to 2.4.0.
- [Release notes](https://github.com/sigstore/cosign/releases)
- [Changelog](https://github.com/sigstore/cosign/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sigstore/cosign/compare/v2.3.0...v2.4.0)

---
updated-dependencies:
- dependency-name: github.com/sigstore/cosign/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 20:11:27 +00:00
Joel Kamp
050497e5a7 Merge pull request #118 from docker/dependabot/go_modules/github.com/sigstore/sigstore/pkg/signature/kms/aws-1.8.8
feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/aws from 1.8.7 to 1.8.8
2024-08-08 15:09:20 -05:00
dependabot[bot]
d69334a1e6 feat(deps): bump google.golang.org/api from 0.190.0 to 0.191.0
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.190.0 to 0.191.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.190.0...v0.191.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 20:00:21 +00:00
dependabot[bot]
a84268b133 feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/aws
Bumps [github.com/sigstore/sigstore/pkg/signature/kms/aws](https://github.com/sigstore/sigstore) from 1.8.7 to 1.8.8.
- [Release notes](https://github.com/sigstore/sigstore/releases)
- [Commits](https://github.com/sigstore/sigstore/compare/v1.8.7...v1.8.8)

---
updated-dependencies:
- dependency-name: github.com/sigstore/sigstore/pkg/signature/kms/aws
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 20:00:19 +00:00
Joel Kamp
2cd2e2da96 Merge pull request #117 from docker/dependabot/go_modules/github.com/sigstore/sigstore/pkg/signature/kms/gcp-1.8.8
feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/gcp from 1.8.7 to 1.8.8
2024-08-08 14:58:19 -05:00
Joel Kamp
f1ece6893f Merge branch 'main' into dependabot/go_modules/github.com/sigstore/sigstore/pkg/signature/kms/gcp-1.8.8 2024-08-08 14:48:59 -05:00
Joel Kamp
116b9ea770 Merge pull request #120 from docker/refactor-referrers-output
feat!: push attestation artifacts by digest
2024-08-08 14:48:42 -05:00
mrjoelkamp
d291912208 refactor!: move oci output from mirror to oci pkg
BREAKING_CHANGE: output methods to save and push images are now part of the oci pkg
2024-08-08 14:23:46 -05:00
mrjoelkamp
9cad88a687 fix: EmptyConfigImage digest 2024-08-08 13:06:56 -05:00
mrjoelkamp
77ccbc097b feat: use docker/go-containerregistry 2024-08-08 11:37:45 -05:00
mrjoelkamp
45927967c8 test: debug push 2024-08-08 10:04:41 -05:00
mrjoelkamp
9aa56e564d feat: push attestation artifacts by digest 2024-08-07 15:19:48 -05:00
dependabot[bot]
6d0a6de520 feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/gcp
Bumps [github.com/sigstore/sigstore/pkg/signature/kms/gcp](https://github.com/sigstore/sigstore) from 1.8.7 to 1.8.8.
- [Release notes](https://github.com/sigstore/sigstore/releases)
- [Commits](https://github.com/sigstore/sigstore/compare/v1.8.7...v1.8.8)

---
updated-dependencies:
- dependency-name: github.com/sigstore/sigstore/pkg/signature/kms/gcp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-07 09:10:08 +00:00
Joel Kamp
8767951fa2 Merge pull request #114 from docker/dependabot/go_modules/google.golang.org/api-0.190.0
feat(deps): bump google.golang.org/api from 0.189.0 to 0.190.0
2024-08-06 10:15:47 -05:00
Joel Kamp
f18b5877d3 Merge branch 'main' into dependabot/go_modules/google.golang.org/api-0.190.0 2024-08-06 10:09:55 -05:00
Joel Kamp
93fd9daeb9 Merge pull request #116 from docker/dependabot/go_modules/github.com/open-policy-agent/opa-0.67.1
feat(deps): bump github.com/open-policy-agent/opa from 0.67.0 to 0.67.1
2024-08-06 10:08:32 -05:00
Joel Kamp
5df79de1c7 Merge branch 'main' into dependabot/go_modules/github.com/open-policy-agent/opa-0.67.1 2024-08-06 09:37:08 -05:00
Joel Kamp
5b5e43b07a Merge pull request #113 from docker/fix-oci-layout-referrers
fix: let OCI layouts use referrers attestations
2024-08-06 09:24:53 -05:00
dependabot[bot]
4c5135eb1b feat(deps): bump github.com/open-policy-agent/opa from 0.67.0 to 0.67.1
Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.67.0 to 0.67.1.
- [Release notes](https://github.com/open-policy-agent/opa/releases)
- [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-policy-agent/opa/compare/v0.67.0...v0.67.1)

---
updated-dependencies:
- dependency-name: github.com/open-policy-agent/opa
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-06 09:01:35 +00:00
mrjoelkamp
0133423f0d feat: add nil pointer test 2024-08-05 16:50:40 -05:00
mrjoelkamp
501b9b442d feat: add CreateAttestationResolver tests 2024-08-05 16:31:39 -05:00
Joel Kamp
d84ed4821c Merge branch 'main' into fix-oci-layout-referrers 2024-08-05 16:01:36 -05:00
Joel Kamp
c9e2ddd448 Merge pull request #115 from docker/chore--disable-codecov-patch-status
chore: disable codecov patch status
2024-08-05 16:01:25 -05:00
mrjoelkamp
165241de42 chore: disable codecov patch status 2024-08-05 15:56:06 -05:00
mrjoelkamp
c7d17faf05 fix: layout attestation resolver 2024-08-05 15:32:24 -05:00
mrjoelkamp
58021646e3 feat: add oci layout test 2024-08-05 11:24:28 -05:00
mrjoelkamp
3e7a85e9b8 fix: nil pointer dereference 2024-08-05 11:24:05 -05:00
mrjoelkamp
bb7a9a257e chore: remove duplicate code 2024-08-05 11:23:32 -05:00
mrjoelkamp
c690d1090c chore: use prefix const 2024-08-05 11:22:49 -05:00
mrjoelkamp
1d1c258f9c fix: referrers resolver only works for registry resolvers 2024-08-05 10:20:40 -05:00
mrjoelkamp
5d096e226f refactor: fix import cycle for mock resolver 2024-08-05 10:19:50 -05:00
dependabot[bot]
7fc7ceaba0 feat(deps): bump google.golang.org/api from 0.189.0 to 0.190.0
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.189.0 to 0.190.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.189.0...v0.190.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-02 08:34:34 +00:00
mrjoelkamp
78ec0b7666 fix: use referrers repo img spec and resolver 2024-08-01 15:24:35 -05:00
Joel Kamp
053f764b8f Merge branch 'main' into fix-oci-layout-referrers 2024-08-01 13:48:53 -05:00
mrjoelkamp
ad3b8b9e49 fix: let OCI layouts use referrers attestations 2024-08-01 13:41:49 -05:00
James Carnegie
9582e69968 fix: standardize casing of initialisms (#112)
* fix: standardize casing of initialisms
* fix: rename intoto -> inToto and Intoto to InToto
* fix: fix all linting errors
2024-08-01 15:35:15 +01:00
James Carnegie
b0b37f73f3 fix: upgrade go to 1.22.x for testing (#110) 2024-08-01 15:34:35 +01:00
dependabot[bot]
d21fc7853c feat(deps): bump github.com/docker/docker (#105)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 27.0.3+incompatible to 27.1.0+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v27.0.3...v27.1.0)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-31 16:50:39 +01:00
Jonny Stoten
008c14e3f3 chore: reformat with gofumpt (#109)
* Reformat with gofumpt

* Suppress issue about laxer perms than 0600

---------

Co-authored-by: Joel Kamp <joel.kamp@docker.com>
2024-07-31 15:48:00 +01:00
Jonny Stoten
fbe9a0c726 Add linting github action (#107)
* Add golangci config

* Add lint action
2024-07-31 14:42:19 +01:00
James Carnegie
9571f17476 fix: add auth to referrers image retrieval (#108) 2024-07-31 14:31:29 +01:00
James Carnegie
63246e2b96 docs: add contribution guidelines (#102)
* docs: add contribution guidelines
2024-07-31 11:08:59 +01:00
Jonny Stoten
91fdf7ece6 Remove dependency on github.com/pkg/errors (#106) 2024-07-30 14:07:19 +01:00
James Carnegie
a1c7bbb991 debt: remove goyaml. Fixup directives (#103) 2024-07-29 17:21:15 +01:00
Jonny Stoten
2ffdfdf0eb docs: first cut of a new README (#99)
Lots of this is taken from image-signer-verifier's README. The stuff on
policy is all new.

Co-authored-by: James Carnegie <kipz@users.noreply.github.com>
2024-07-29 16:43:31 +01:00
dependabot[bot]
781a738b54 feat(deps): bump github.com/open-policy-agent/opa from 0.66.0 to 0.67.0 (#101)
Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.66.0 to 0.67.0.
- [Release notes](https://github.com/open-policy-agent/opa/releases)
- [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-policy-agent/opa/compare/v0.66.0...v0.67.0)

---
updated-dependencies:
- dependency-name: github.com/open-policy-agent/opa
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-26 09:46:59 +01:00
James Carnegie
c735bb0d3f feat: roll out updates on release (#100)
* feat: roll out updates on release

* Use app token. Fix repo
2024-07-25 16:15:35 +01:00
dependabot[bot]
bd2c4d7d8a feat(deps): bump github.com/sigstore/cosign/v2 from 2.2.4 to 2.3.0 (#98)
* feat(deps): bump github.com/sigstore/cosign/v2 from 2.2.4 to 2.3.0

Bumps [github.com/sigstore/cosign/v2](https://github.com/sigstore/cosign) from 2.2.4 to 2.3.0.
- [Release notes](https://github.com/sigstore/cosign/releases)
- [Changelog](https://github.com/sigstore/cosign/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sigstore/cosign/compare/v2.2.4...v2.3.0)

---
updated-dependencies:
- dependency-name: github.com/sigstore/cosign/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* update to go 1.22.5

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mrjoelkamp <joel.kamp@docker.com>
2024-07-24 13:30:56 +01:00
dependabot[bot]
03ba59c6b9 feat(deps): bump google.golang.org/api from 0.188.0 to 0.189.0 (#97) 2024-07-23 19:08:01 +00:00
dependabot[bot]
ac82c65d7c feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#93) 2024-07-23 19:03:53 +00:00
Joel Kamp
24a81bbfe1 Merge pull request #96 from docker/chore-update-dev-root
chore: update dev root
2024-07-23 08:03:03 -05:00
mrjoelkamp
1e3c120272 fix: test targets file no ext 2024-07-22 16:03:23 -05:00
mrjoelkamp
d252a7f4d7 chore: update oci test data 2024-07-22 15:57:35 -05:00
mrjoelkamp
02421f8cf5 chore: update http test data 2024-07-22 15:52:14 -05:00
mrjoelkamp
a6cd978bc0 chore: update dev root 2024-07-22 15:23:28 -05:00
James Carnegie
efb73f4cae Use DSSE artifactType in referrers (#95)
* bug: Use DSSE media types for artifactType

* Don't serialize DSSE extension if not present

* Update pkg/attestation/types.go

Co-authored-by: Joel Kamp <joel.kamp@docker.com>

* Don't error on no referrers

---------

Co-authored-by: Joel Kamp <joel.kamp@docker.com>
2024-07-22 18:17:12 +01:00
James Carnegie
5e68d94ad4 set artifactType correctly for referrers fallback (#94)
* set artifactType correctly for referrers fallback
2024-07-19 16:39:35 +01:00
dependabot[bot]
10d4f129b5 feat(deps): bump github.com/theupdateframework/go-tuf/v2 from 2.0.0-20240504210453-5a634eb214ae to 2.0.0 (#92)
* feat(deps): bump github.com/theupdateframework/go-tuf/v2

Bumps [github.com/theupdateframework/go-tuf/v2](https://github.com/theupdateframework/go-tuf) from 2.0.0-20240504210453-5a634eb214ae to 2.0.0.
- [Release notes](https://github.com/theupdateframework/go-tuf/releases)
- [Changelog](https://github.com/theupdateframework/go-tuf/blob/master/.goreleaser.yaml)
- [Commits](https://github.com/theupdateframework/go-tuf/commits/v2.0.0)

---
updated-dependencies:
- dependency-name: github.com/theupdateframework/go-tuf/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Tidy go.mod

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jonny Stoten <jonny.stoten@docker.com>
2024-07-17 17:01:09 +01:00
James Carnegie
de5668aca2 chore: fix linting errors (#91) 2024-07-16 12:52:33 +01:00
dependabot[bot]
79566ff70a feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/aws (#88)
Bumps [github.com/sigstore/sigstore/pkg/signature/kms/aws](https://github.com/sigstore/sigstore) from 1.8.6 to 1.8.7.
- [Release notes](https://github.com/sigstore/sigstore/releases)
- [Commits](https://github.com/sigstore/sigstore/compare/v1.8.6...v1.8.7)

---
updated-dependencies:
- dependency-name: github.com/sigstore/sigstore/pkg/signature/kms/aws
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-16 11:46:03 +01:00
dependabot[bot]
d01395144b feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/gcp (#89)
Bumps [github.com/sigstore/sigstore/pkg/signature/kms/gcp](https://github.com/sigstore/sigstore) from 1.8.6 to 1.8.7.
- [Release notes](https://github.com/sigstore/sigstore/releases)
- [Commits](https://github.com/sigstore/sigstore/compare/v1.8.6...v1.8.7)

---
updated-dependencies:
- dependency-name: github.com/sigstore/sigstore/pkg/signature/kms/gcp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-16 11:32:17 +01:00
James Carnegie
065b354d3c Make referrers attestations OCI compliant (#80)
* Single attestation when creating VSA

* Create single layer images for referrers attestations

* Move mock to test package. Add artifacts test

* Add test for envelope detection

* Add tests for image/index saving

* Add mirror tests

* Remove AttestationImage field from AttestationManifest

* Update naming. strictReferers != laxReferrers

* Add specific test for SaveReferrers
2024-07-16 10:05:17 +01:00
Jonny Stoten
a4c3bd07fe Add proper mirror support (#74)
* Add rewrite support and fix existing tests

* Add unit tests for policy matching

* Compile regexes up front and store policies in map

* Add test for verify flow with mirror

* Rename ImageName -> ResolvedName

And only set it when necessary

* Rename Rewrite -> Replacement

but keep it as rewrite in the yaml
2024-07-12 17:09:41 +01:00
dependabot[bot]
247448a765 feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#86)
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.24 to 1.27.26.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.24...config/v1.27.26)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-12 10:38:33 +01:00
dependabot[bot]
64e7f1ccab feat(deps): bump github.com/testcontainers/testcontainers-go/modules/registry (#79)
Bumps [github.com/testcontainers/testcontainers-go/modules/registry](https://github.com/testcontainers/testcontainers-go) from 0.31.0 to 0.32.0.
- [Release notes](https://github.com/testcontainers/testcontainers-go/releases)
- [Commits](https://github.com/testcontainers/testcontainers-go/compare/v0.31.0...v0.32.0)

---
updated-dependencies:
- dependency-name: github.com/testcontainers/testcontainers-go/modules/registry
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-12 10:35:30 +01:00
dependabot[bot]
f3354d1251 feat(deps): bump github.com/google/go-containerregistry (#81)
Bumps [github.com/google/go-containerregistry](https://github.com/google/go-containerregistry) from 0.19.2 to 0.20.0.
- [Release notes](https://github.com/google/go-containerregistry/releases)
- [Changelog](https://github.com/google/go-containerregistry/blob/main/.goreleaser.yml)
- [Commits](https://github.com/google/go-containerregistry/compare/v0.19.2...v0.20.0)

---
updated-dependencies:
- dependency-name: github.com/google/go-containerregistry
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-12 10:27:37 +01:00
dependabot[bot]
a36c43a173 feat(deps): bump google.golang.org/api from 0.187.0 to 0.188.0 (#83)
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.187.0 to 0.188.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.187.0...v0.188.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-12 10:27:14 +01:00
Joel Kamp
7e9b48baf9 Merge pull request #87 from docker/chore-update-defaults-export-roots
chore: update default urls and export roots
2024-07-11 13:22:36 -05:00
mrjoelkamp
da310234a4 feat: export embedded root names 2024-07-11 09:55:00 -05:00
mrjoelkamp
d65be7be7c fix: use prod as default for mirroring 2024-07-11 09:41:04 -05:00
Joel Kamp
2e2bc49387 Merge pull request #85 from docker/refactor--use-embedded-root-types
feat: add EmbeddedRoot type
2024-07-11 03:36:06 -05:00
mrjoelkamp
0330ea4755 feat: add EmbeddedRoot type 2024-07-10 17:30:35 -05:00
Joel Kamp
0336a21a7d Merge pull request #84 from docker/feat-add-prod-tuf-root
feat: add production TUF root
2024-07-10 16:39:44 -05:00
mrjoelkamp
1754a98e4e fix: dont use keyword var 2024-07-10 16:35:48 -05:00
Joel Kamp
a05fc10d53 Update pkg/tuf/tuf_test.go
Co-authored-by: David Dooling <141646279+whalelines@users.noreply.github.com>
2024-07-10 16:19:58 -05:00
mrjoelkamp
e830271d01 feat: add test 2024-07-10 14:39:52 -05:00
mrjoelkamp
1cb3e4a281 feat: add production tuf root 2024-07-10 14:29:59 -05:00
James Carnegie
6b199f027a Enable GCP integration test (#82) 2024-07-09 15:02:49 +01:00
dependabot[bot]
aaf043e9cd feat(deps): bump google.golang.org/api from 0.185.0 to 0.187.0 (#76)
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.185.0 to 0.187.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.185.0...v0.187.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-05 12:36:47 +01:00
Jonny Stoten
ac693a45c7 Don't upload coverage results if we don't run e2e (#77)
this way the coverage doesn't fluctuate between e2e and non-e2e test
runs
2024-07-05 12:28:47 +01:00
James Carnegie
0038e3d23d Unify functions for use in sign & verify --vsa (#71)
* Use receivers for manifest functions
* Move SaveImage/SaveIndex from image-signing-verifier
* Ignore test fixtures in coverage
* Add AddImagesToIndex function
2024-07-05 09:29:14 +01:00
James Carnegie
0dd63bf5a3 Add GCP KMS support (#73)
* Add GCP KMS support
2024-07-04 15:32:10 +01:00
dependabot[bot]
5d56efa2df feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#75)
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.23 to 1.27.24.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.23...config/v1.27.24)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-04 09:41:32 +01:00
dependabot[bot]
3ffef89dda feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/aws from 1.8.5 to 1.8.6 (#67) 2024-07-03 19:56:29 +00:00
dependabot[bot]
3c26a89496 feat(deps): bump github.com/open-policy-agent/opa from 0.65.0 to 0.66.0 (#69) 2024-07-03 19:45:50 +00:00
dependabot[bot]
6ee1d32ddc feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#70) 2024-07-03 19:33:53 +00:00
Joel Kamp
3140e2d903 Merge pull request #72 from docker/dependabot/go_modules/github.com/containerd/containerd-1.7.19
feat(deps): bump github.com/containerd/containerd from 1.7.18 to 1.7.19
2024-07-03 14:28:01 -05:00
dependabot[bot]
dd1141c231 feat(deps): bump github.com/containerd/containerd from 1.7.18 to 1.7.19
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.7.18 to 1.7.19.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.7.18...v1.7.19)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-03 08:39:19 +00:00
James Carnegie
bda1910107 Add e2e auth test (#68)
* Add e2e auth test
2024-07-01 14:14:23 +01:00
dependabot[bot]
80658a4b5f feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/aws (#65) 2024-06-26 16:43:41 +00:00
Joel Kamp
46db2b9fd5 Merge pull request #59 from docker/feat-cloud-provider-authn
feat: cloud provider authn
2024-06-26 09:28:03 -05:00
mrjoelkamp
e37f788865 refactor: drop ACR support for now 2024-06-25 13:44:29 -05:00
Joel Kamp
13172cb502 Merge branch 'main' into feat-cloud-provider-authn 2024-06-25 12:06:46 -05:00
mrjoelkamp
abb3163628 fix: update aws-sdk-go-v2 2024-06-25 11:49:58 -05:00
James Carnegie
742f98fbeb Generate coverage when tests are run (#64)
* Generate coverage when tests are run
* Use docker's codecov account
2024-06-24 14:26:07 +01:00
Joel Kamp
8cae188735 Merge branch 'main' into feat-cloud-provider-authn 2024-06-21 16:39:45 -05:00
Joel Kamp
7586f4dfc4 Merge pull request #61 from docker/dependabot/go_modules/github.com/aws/aws-sdk-go-v2/config-1.27.21
feat(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.27.19 to 1.27.21
2024-06-21 16:35:01 -05:00
Joel Kamp
acb862ea42 Merge branch 'main' into dependabot/go_modules/github.com/aws/aws-sdk-go-v2/config-1.27.21 2024-06-21 16:32:11 -05:00
James Carnegie
357768d421 Various fixes (#63)
* Fix digest resolution and attestation style

* Add a bunch more tests

* Rename fields for consistency

* Remove copy-pasta

* Value -> pointer
2024-06-21 22:12:42 +01:00
James Carnegie
6bd57e02b6 Add support for separate attestation storage repo (#62)
* Add support for separate attestation storage repo
* Move mapping file types and parsing to config package
* Change signature of Verify to take image/platform
* Separate Attestation Resolvers to their own files (registry, layout and referrers)
* Add support configuring referrers resolution style in mapping.yaml
* Add registry test
2024-06-21 11:29:16 +01:00
dependabot[bot]
92985e9a12 feat(deps): bump github.com/aws/aws-sdk-go-v2/config
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.19 to 1.27.21.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.19...config/v1.27.21)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-20 08:23:40 +00:00
mrjoelkamp
08e823e05b refactor: make common authn function 2024-06-18 12:00:47 -05:00
Joel Kamp
ff38975c76 Merge branch 'main' into feat-cloud-provider-authn 2024-06-18 10:09:04 -05:00
dependabot[bot]
86878482c3 feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#58) 2024-06-18 15:06:00 +00:00
mrjoelkamp
f95760d8b2 chore: fmt go.mod 2024-06-18 10:04:38 -05:00
mrjoelkamp
f611f81fff feat: add support for ecr, gcp, acr authn 2024-06-18 09:59:04 -05:00
mrjoelkamp
8e3c6a2ec5 feat: use os.ModePerm 2024-06-18 09:39:12 -05:00
mrjoelkamp
a3921c206a fix: ineffectual assign 2024-06-18 09:38:50 -05:00
James Carnegie
130e1f640b Support referrers using digest, not just tag (#55)
* Support referrers using digest, not just tag

* ParseRef and switch on type

* Call DigestStr instead of String
2024-06-17 17:30:12 +01:00
Jonny Stoten
0d0d86854c Return policy input with verification result (#56) 2024-06-17 17:28:22 +01:00
Jonny Stoten
1d9e14b99f Avoid pointers to map (#57) 2024-06-17 17:24:29 +01:00
dependabot[bot]
83c7d7634a feat(deps): bump github.com/google/go-containerregistry (#54)
Bumps [github.com/google/go-containerregistry](https://github.com/google/go-containerregistry) from 0.19.1 to 0.19.2.
- [Release notes](https://github.com/google/go-containerregistry/releases)
- [Changelog](https://github.com/google/go-containerregistry/blob/main/.goreleaser.yml)
- [Commits](https://github.com/google/go-containerregistry/compare/v0.19.1...v0.19.2)

---
updated-dependencies:
- dependency-name: github.com/google/go-containerregistry
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-17 11:03:08 +01:00
Joel Kamp
5c07bd70d9 Merge pull request #53 from docker/fix-default-mkdir-perms
fix: mkdir perms
2024-06-14 15:42:23 -05:00
mrjoelkamp
c02e628600 fix: mkdir perms 2024-06-14 15:23:25 -05:00
Joel Kamp
3d46780a1c Merge pull request #52 from docker/refactor-use-interface-value
refactor: use interface value
2024-06-14 11:58:45 -05:00
mrjoelkamp
83dfd746b9 fix: update output dir permissions 2024-06-14 11:11:48 -05:00
mrjoelkamp
845fe93c11 refactor: remove any; split into functions 2024-06-14 10:04:18 -05:00
mrjoelkamp
c154613c52 refactor: use interface value 2024-06-14 10:03:39 -05:00
James Carnegie
e44390d2bc Don't use pointers for image interfaces (#51)
* Don't use pointers for image interfaces

* Also for oci layout

* Remove default case
2024-06-14 10:28:14 +01:00
James Carnegie
8ba9656645 Add support for OCI Referrers and fallback (#50)
* Add support for OCI Referrers and fallback
2024-06-13 16:10:41 +01:00
dependabot[bot]
e120439035 feat(deps): bump github.com/containerd/containerd from 1.7.17 to 1.7.18 (#48) 2024-06-12 20:16:09 +00:00
dependabot[bot]
b20f452004 feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#49) 2024-06-10 17:23:42 +00:00
James Carnegie
4be882aeb0 Handle errors from Go in Rego. Support for skipping TL (#47)
* Make TL logging/verification optional

* Return errors from go-lang fns

* Update pkg/policy/rego.go

Co-authored-by: Jonny Stoten <jonny@jonnystoten.com>

* Update pkg/attestation/sign.go

Co-authored-by: Joel Kamp <joel.kamp@docker.com>

* Move public key marshelling until later

* Simplify logSignature and pass down opts

---------

Co-authored-by: Jonny Stoten <jonny@jonnystoten.com>
Co-authored-by: Joel Kamp <joel.kamp@docker.com>
2024-06-06 09:59:32 +01:00
dependabot[bot]
3b5c506739 feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#46)
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.16 to 1.27.17.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.16...config/v1.27.17)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-04 15:53:00 +01:00
dependabot[bot]
f36bb50af5 feat(deps): bump github.com/open-policy-agent/opa from 0.64.1 to 0.65.0 (#44)
Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.64.1 to 0.65.0.
- [Release notes](https://github.com/open-policy-agent/opa/releases)
- [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-policy-agent/opa/compare/v0.64.1...v0.65.0)

---
updated-dependencies:
- dependency-name: github.com/open-policy-agent/opa
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-31 11:15:43 +01:00
James Carnegie
c8c148c70a Expose ParsePlatform (#45) 2024-05-31 11:02:14 +01:00
James Carnegie
a334599635 *Breaking* Parse platform earlier (#43)
* *Breaking* Parse platform earlier

* Use constructors and hide fields to avoid confusion
2024-05-30 17:38:58 +01:00
dependabot[bot]
e81016fc31 feat(deps): bump github.com/sigstore/sigstore/pkg/signature/kms/aws (#42)
Bumps [github.com/sigstore/sigstore/pkg/signature/kms/aws](https://github.com/sigstore/sigstore) from 1.8.3 to 1.8.4.
- [Release notes](https://github.com/sigstore/sigstore/releases)
- [Commits](https://github.com/sigstore/sigstore/compare/v1.8.3...v1.8.4)

---
updated-dependencies:
- dependency-name: github.com/sigstore/sigstore/pkg/signature/kms/aws
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-29 12:04:38 +01:00
James Carnegie
2ae5606c92 Add support for selecting a policy by ID (#41) 2024-05-28 15:17:37 +01:00
dependabot[bot]
8a6e75ce39 feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#40) 2024-05-24 13:47:05 +00:00
Jonny Stoten
6397dcede8 Check version of attest against constraints in TUF (#19)
* Check version of attest against constraints in TUF

* Add link to semver lib constraints docs
2024-05-22 17:02:25 +01:00
Jonny Stoten
1a7897a052 Return VSA and rich errors from verification (#38)
* Start of richer results from verification

* Pull out VSA code from signing

* Expose attestation signing fns

* Add VSA test

* Notes for policy result

* Require separate policy for VSA creation

* Load test signing key from tests

* Return rich object from policy

* Add result object schema and fix tests

* Ensure example test runs

* Remove data.yaml files from mock policies

* Don't run example - TUF policy isn't compatible

* Add attestation to manifests for all subjects

* Ensure adding attestation doesn't touch statements

* Don't export sign function

* Remove attestations from VerificationResult

* Change bool to Outcome enum in result

* Use outputLayout directly

* Make clearer that Outcome strings are for VSA

* Return multiple SLSA levels from policy

* Fix unmarshalling of policy-id (#39)

* Rename function

* Rename policy.VerificationResult -> policy.Result

* Re-add test for canonical input

---------

Co-authored-by: James Carnegie <james.carnegie@docker.com>
Co-authored-by: James Carnegie <kipz@users.noreply.github.com>
2024-05-22 14:49:23 +01:00
James Carnegie
745eea09e8 Fix image detection based on platform (#33) 2024-05-20 09:37:53 +01:00
dependabot[bot]
84d7903c46 feat(deps): bump github.com/containerd/containerd from 1.7.16 to 1.7.17 (#35) 2024-05-17 17:19:30 +00:00
dependabot[bot]
7234e29829 feat(deps): bump github.com/package-url/packageurl-go (#36) 2024-05-17 17:14:13 +00:00
Joel Kamp
b46f544f0c Merge pull request #34 from docker/dependabot/go_modules/github.com/aws/aws-sdk-go-v2/config-1.27.15
feat(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.27.14 to 1.27.15
2024-05-17 12:13:31 -05:00
dependabot[bot]
85d7b34e18 feat(deps): bump github.com/aws/aws-sdk-go-v2/config
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.14 to 1.27.15.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.14...config/v1.27.15)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-17 17:07:46 +00:00
Joel Kamp
c416c11e10 Merge pull request #37 from docker/fix-is-canonical-policy
fix: canonical policy
2024-05-17 09:34:27 -05:00
mrjoelkamp
0020ece3b4 fix: canonical policy 2024-05-17 09:29:06 -05:00
James Carnegie
ec1c994f04 Use id/policy-id in mapping.yaml (#32) 2024-05-16 15:34:19 +01:00
James Carnegie
6ebf042966 Upgrade some deps to fix vulnerabilities (#31) 2024-05-16 15:22:30 +01:00
James Carnegie
a86c8c1209 Use policy files from mapping.yaml (#30)
* Use policy files from mapping.yaml

* Rename location to root in mapping.yaml

* Remove location/root
2024-05-16 14:49:57 +01:00
dependabot[bot]
dd621e2a13 feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#29) 2024-05-16 13:12:49 +00:00
Joel Kamp
b05523e7ea Merge pull request #28 from docker/fix-missing-download-dir
fix: no such directory error
2024-05-15 18:06:19 -05:00
mrjoelkamp
eddb277d7e feat: add tuf download target tests 2024-05-15 16:22:35 -05:00
mrjoelkamp
a103e0e9d7 revert: query 2024-05-15 15:23:22 -05:00
mrjoelkamp
249cf5bcf3 fix: query 2024-05-15 15:21:54 -05:00
mrjoelkamp
33a1996b2b fix: no such directory error 2024-05-15 14:47:20 -05:00
Joel Kamp
1b24098027 Merge pull request #27 from docker/revert-forked-go-tuf
revert: go-tuf fork
2024-05-13 10:02:53 -05:00
mrjoelkamp
64f3c9b149 revert: go-tuf fork 2024-05-13 09:48:04 -05:00
dependabot[bot]
3ee718ee67 feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#26)
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.12 to 1.27.13.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.12...config/v1.27.13)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 09:54:32 +01:00
dependabot[bot]
06947cf992 feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#21)
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.11 to 1.27.12.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.11...config/v1.27.12)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-10 12:15:15 +01:00
dependabot[bot]
4648680a75 feat(deps): bump github.com/testcontainers/testcontainers-go/modules/registry (#24)
Bumps [github.com/testcontainers/testcontainers-go/modules/registry](https://github.com/testcontainers/testcontainers-go) from 0.30.0 to 0.31.0.
- [Release notes](https://github.com/testcontainers/testcontainers-go/releases)
- [Commits](https://github.com/testcontainers/testcontainers-go/compare/v0.30.0...v0.31.0)

---
updated-dependencies:
- dependency-name: github.com/testcontainers/testcontainers-go/modules/registry
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-10 12:14:52 +01:00
Jonny Stoten
17902c4eb8 Merge pull request #20 from docker/small-tidies
Small tidies
2024-05-08 15:54:31 +01:00
Jonny Stoten
bd6d130e17 Don't use builtin print function 2024-05-08 13:12:40 +01:00
Jonny Stoten
bd849d9b43 Simplify some string concats 2024-05-08 13:09:25 +01:00
Jonny Stoten
8d45522fe8 Use assert.NoError for nil checks on errors 2024-05-08 13:09:25 +01:00
Jonny Stoten
da22f71207 Use maps.Clone from stdlib 2024-05-08 13:09:25 +01:00
Jonny Stoten
c69a9586c5 Remove string contains func (it's in the stdlib) 2024-05-08 13:09:25 +01:00
Jonny Stoten
e3d02ab2e1 Simplify and rename hash functions 2024-05-08 13:09:25 +01:00
Jonny Stoten
d5b059043f Merge pull request #18 from docker/docs--update-examples-in-README.md
docs: update examples in README.md
2024-05-08 13:04:56 +01:00
mrjoelkamp
54996b3c0b docs: pr comments 2024-05-02 16:07:04 -05:00
Joel Kamp
4566ea56b3 Update pkg/attest/example_verify_test.go
Co-authored-by: David Dooling <141646279+whalelines@users.noreply.github.com>
2024-05-02 15:57:27 -05:00
Joel Kamp
20dd9da7c0 Update pkg/attest/example_verify_test.go
Co-authored-by: David Dooling <141646279+whalelines@users.noreply.github.com>
2024-05-02 15:57:19 -05:00
Joel Kamp
3aa738b246 Update pkg/tuf/example_registry_test.go
Co-authored-by: David Dooling <141646279+whalelines@users.noreply.github.com>
2024-05-02 15:57:11 -05:00
Joel Kamp
c99f90cbbf docs: update examples in README.md 2024-05-02 13:49:14 -05:00
mrjoelkamp
3701942bf1 docs: update examples in README.md 2024-05-02 13:35:57 -05:00
James Carnegie
0cadeefe6f Fix query and tests (#17) 2024-05-02 16:03:59 +01:00
James Carnegie
bc7139deaa Move policy mock for external use (#16) 2024-05-02 14:46:21 +01:00
James Carnegie
b461c7f8d8 Revert "revert: rego evaluator result" (#15)
This reverts commit 0126ba9a0b.
2024-05-02 11:36:29 +01:00
Joel Kamp
f6245405ee Merge pull request #13 from docker/feat--add-attest-sign/verify
feat: add attest sign/verify
2024-04-30 16:29:09 -05:00
mrjoelkamp
3e0086e7e2 docs: prioritize verification over signing 2024-04-30 16:09:45 -05:00
mrjoelkamp
34fcb0ca6d chore: rename SignIndexAttestations to just Sign 2024-04-30 15:55:21 -05:00
mrjoelkamp
6b8c5b56bc fix: default to v1.ImageIndex for *mutate.index support 2024-04-30 15:46:55 -05:00
mrjoelkamp
8cbdf6d4de docs: update README.md 2024-04-30 15:45:36 -05:00
mrjoelkamp
0126ba9a0b revert: rego evaluator result 2024-04-30 13:13:30 -05:00
mrjoelkamp
80f72a0059 refactor: SignIndexAttestations 2024-04-30 12:23:07 -05:00
mrjoelkamp
94d7f99c3c refactor: remove evelopeStyle 2024-04-30 09:34:36 -05:00
James Carnegie
90393ea6fd Return results from rego evaluation (#14) 2024-04-30 15:32:52 +01:00
mrjoelkamp
fb1a43acfd feat: add attest sign/verify 2024-04-29 16:17:58 -05:00
mrjoelkamp
20fc372988 docs: update README.md 2024-04-29 14:11:58 -05:00
mrjoelkamp
b16511d6e4 feat: add attest sign/verify 2024-04-29 12:55:19 -05:00
dependabot[bot]
ee218dd402 feat(deps): bump github.com/open-policy-agent/opa from 0.64.0 to 0.64.1 (#12) 2024-04-29 13:09:14 +00:00
dependabot[bot]
6342784b53 feat(deps): bump github.com/containerd/containerd from 1.7.14 to 1.7.16 (#11) 2024-04-26 16:22:49 +00:00
dependabot[bot]
b2a5462a83 feat(deps): bump github.com/open-policy-agent/opa from 0.63.0 to 0.64.0 (#10) 2024-04-26 15:43:47 +00:00
dependabot[bot]
b440d7fea3 feat(deps): bump github.com/distribution/reference from 0.5.0 to 0.6.0 (#9) 2024-04-26 15:31:07 +00:00
386 changed files with 15166 additions and 3603 deletions

19
.github/ISSUE_TEMPLATE/bug-report.md vendored Normal file
View File

@@ -0,0 +1,19 @@
---
name: Bug report
about: Create a report to help us improve
title: ""
labels: bug
assignees: ""
---
**What steps did you take and what happened:**
[A clear and concise description of what the bug is.]
**What did you expect to happen:**
**Anything else you would like to add:**
[Miscellaneous information that will assist in solving the issue.]
**Environment:**
- Attest version:

View File

@@ -0,0 +1,13 @@
---
name: Feature request
about: Suggest an idea for this project
title: ""
labels: enhancement
assignees: ""
---
**Describe the solution you'd like**
[A clear and concise description of what you want to happen.]
**Anything else you would like to add:**
[Miscellaneous information that will assist in solving the issue.]

11
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,11 @@
## Summary
<!-- Description of why the pull request is needed and what it changes -->
### Tests
<!-- Provide evidence of testing -->
### Issue
<!-- Link to issue that this is part of -->

2
.github/dco.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
require:
members: false

View File

@@ -14,6 +14,9 @@ categories:
- title: "🧰 Maintenance"
labels:
- "chore"
- title: "💥 Breaking Changes"
labels:
- "breaking"
change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
@@ -21,6 +24,7 @@ version-resolver:
major:
labels:
- "major"
- "breaking"
minor:
labels:
- "minor"
@@ -37,26 +41,19 @@ autolabeler:
- label: "chore"
files:
- "*.md"
branch:
- '/docs{0,1}\/.+/'
- '/tests{0,1}\/.+/'
- '/chore{0,1}\/.+/'
title:
- "/docs/i"
- "/test/i"
- "/chore/i"
- "/^docs!?:/i"
- "/^test!?:/i"
- "/^chore!?:/i"
- "/^refactor!?:/i"
- label: "bug"
branch:
- '/fix\/.+/'
- '/revert\/.+/'
title:
- "/fix/i"
- "/revert/i"
- "/^fix!?:/i"
- "/^revert!?:/i"
- label: "feature"
branch:
- '/feature\/.+/'
- '/feat\/.+/'
- '/add\/.+/'
title:
- "/feat/i"
- "/add/i"
- "/^feat!?:/i"
- "/^add!?:/i"
- label: "breaking"
title:
- "/^[a-zA-Z]+!:/i"

19
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: lint code
on:
pull_request:
workflow_dispatch:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Set git to use LF
run: git config --global core.autocrlf false
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 1.22.x
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.59
only-new-issues: true

24
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: release
on:
release:
types: [published]
jobs:
trigger_attest_update:
name: Update attest lib - ALL
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Generate GitHub App Token
id: app-token
uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
with:
app-id: ${{ vars.ATTEST_RELEASE_APP_ID }}
private-key: ${{ secrets.ATTEST_RELEASE_APP_PRIVATE_KEY }}
repositories: "attest-actions"
- name: Send repository_dispatch event
uses: peter-evans/repository-dispatch@v3.0.0
with:
token: ${{ steps.app-token.outputs.token }}
event-type: update_attest_all
repository: docker/attest-actions
client-payload: '{"attest_version": "${{ github.ref_name }}"}'

View File

@@ -7,9 +7,12 @@ on:
workflow_dispatch:
jobs:
golang:
permissions:
contents: read
id-token: write
strategy:
matrix:
go-version: [1.21.x]
go-version: [1.22.x, 1.23.x]
# temp disable windows tests see https://github.com/docker/image-signer-verifier/pull/154
# os: [ubuntu-latest, macos-latest, windows-latest]
os: [ubuntu-latest, macos-latest]
@@ -21,9 +24,42 @@ jobs:
- uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- name: Login to Docker Hub
if: matrix.os == 'ubuntu-latest' && github.actor != 'dependabot[bot]'
uses: docker/login-action@v3
with:
username: dockerpublicbot
password: ${{ secrets.DOCKERPUBLICBOT_WRITE_PAT }}
- name: Authenticate to AWS
if: matrix.os == 'ubuntu-latest' && github.actor != 'dependabot[bot]'
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 #v4.0.2
with:
aws-region: "us-east-1"
role-to-assume: arn:aws:iam::175142243308:role/doi-github-actions-signing
- name: auth-with-gcp
if: matrix.os == 'ubuntu-latest' && github.actor != 'dependabot[bot]'
uses: google-github-actions/auth@v2
with:
project_id: 'attest-kms-test'
export_environment_variables: true
workload_identity_provider: 'projects/385966116051/locations/global/workloadIdentityPools/attest-kms-test/providers/attest-kms-test'
service_account: 'attest-kms-test@attest-kms-test.iam.gserviceaccount.com'
- name: Setup Testcontainers Cloud Client
uses: atomicjar/testcontainers-cloud-setup-action@v1
with:
token: ${{ secrets.TC_CLOUD_TOKEN }}
- name: go test
run: go test ./...
- name: go test including e2e
if: matrix.os == 'ubuntu-latest' && github.actor != 'dependabot[bot]'
run: go test -tags=e2e -v ./... -coverpkg=./... -coverprofile=coverage.out -covermode=atomic
- name: go test excluding e2e
if: matrix.os == 'macos-latest' || github.actor == 'dependabot[bot]'
run: go test -v ./...
- name: Upload coverage to Codecov
if: matrix.os == 'ubuntu-latest' && github.actor != 'dependabot[bot]'
uses: codecov/codecov-action@v5
with:
file: ./coverage.out
flags: unittests
name: codecov-umbrella
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}

1
.gitignore vendored
View File

@@ -19,3 +19,4 @@
# Go workspace file
go.work
.aider*

36
.golangci.yaml Normal file
View File

@@ -0,0 +1,36 @@
run:
timeout: 5m
go: "1.22"
linters-settings:
gocritic:
enabled-tags:
- performance
lll:
line-length: 200
misspell:
locale: US
linters:
disable-all: true
enable:
- errcheck
- forcetypeassert
- gocritic
- goconst
- godot
- gofmt
- gofumpt
- goimports
- gosec
- gosimple
- govet
- importas
- ineffassign
- misspell
- revive # replacement for golint
- staticcheck
- typecheck
- unused
- whitespace

131
CODE-OF-CONDUCT.md Normal file
View File

@@ -0,0 +1,131 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official email address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by submitting an [incident report](https://docs.google.com/forms/d/e/1FAIpQLScezna1ZXRPzC_phSDoPEF4c5nvw8yQW-vvtI8xHjv-BB9MOg/viewform?c=0&w=1).
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
at [https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

85
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,85 @@
# Contribute to the attest Library
Want to hack on the attest library? This guide will help you to find out how to contribute.
This page contains information about reporting issues as well as some tips and guidelines useful to experienced open source contributors. Finally, make sure you read our [community guidelines](#community-guidelines) before you start participating.
## Topics
* [Reporting Security Issues](#reporting-security-issues)
* [Design and Cleanup Proposals](#design-and-cleanup-proposals)
* [Reporting Issues](#reporting-other-issues)
* [Quick Contribution Tips and Guidelines](#quick-contribution-tips-and-guidelines)
* [Community Guidelines](#community-guidelines)
## Reporting security issues
The attest maintainers take security seriously. If you discover a security issue, please bring it to their attention right away!
Please **DO NOT** file a public issue, instead send your report privately to [security@docker.com](mailto:security@docker.com).
Security reports are greatly appreciated and we will publicly thank you for it, although we keep your name confidential if you request it. We also like to send gifts—if you're into schwag, make sure to let us know. We currently do not offer a paid security bounty program, but are not ruling it out in the future.
## Reporting other issues
A great way to contribute to the project is to send a detailed report when you encounter an issue. We always appreciate a well-written, thorough bug report, and will thank you for it!
Check that [our issue database](https://github.com/docker/attest/issues) doesn't already include that problem or suggestion before submitting an issue. If you find a match, you can use the "subscribe" button to get notified on updates. Do *not* leave random "+1" or "I have this too" comments. Those comments can become annoying very quickly. Instead, use [GitHub reactions](https://docs.github.com/en/free-pro-team@latest/github/writing-on-github/using-emojis).
### How to report a bug
* **Use a clear and descriptive title** for the issue to identify the problem.
* **Describe the exact steps which reproduce the problem** in as many details as possible. When listing steps, **don't just say what you did, but explain how you did it**.
* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
* **Explain which behavior you expected to see instead and why.**
* **Include screenshots and animated GIFs** which show you following the described steps and clearly demonstrate the problem.
* **If the problem is related to performance or memory**, include a [CPU profile capture](https://blog.golang.org/profiling-go-programs) with your report.
* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened.
* **Include the version of attest you are using**.
* **Include the name and version of the OS you're using**.
## Quick contribution tips and guidelines
This section gives a brief overview of how to propose a change to attest.
### Contribution flow
1. Fork the repository on GitHub.
2. Create a topic branch from where you want to base your work.
3. Make commits of logical units.
4. Make sure your commit messages are in the proper format (see below).
5. Push your changes to a topic branch in your fork of the repository.
6. Submit a pull request to the original repository.
### Format of the commit message
We follow a rough convention for commit messages [borrowed from Angular](https://www.conventionalcommits.org/en/v1.0.0/).
- **feat**: A new feature
- **fix**: A bug fix
- **docs**: Documentation only changes
- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
- **refactor**: A code change that neither fixes a bug nor adds a feature
- **perf**: A code change that improves performance
- **test**: Adding missing or correcting existing tests
- **chore**: Changes to the build process or auxiliary tools and libraries such as documentation generation
### Code review process
All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose.
### Tips for contributors
1. All code should be formatted with `gofmt -s`.
2. All code should pass the default levels of [`golint`](https://github.com/golang/lint).
3. All code should follow the guidelines covered in [Effective Go](http://golang.org/doc/effective_go.html) and [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments).
4. Comment the code. Tell us the why, the history, and the context.
5. Document _all_ public declarations and methods. Declare expectations, caveats, and anything else that may be important. If a type gets exported, having the comments already there will ensure it's ready.
6. Variable name length should be proportional to its context and no longer. `noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`. In practice, short methods will have short variable names and globals will have longer names.
7. No underscores in package names. If you need a compound name, step back, and re-examine why you need a compound name. If you still think you need a compound name, lose the underscore.
8. No utils or helpers packages. If a function is not general enough to warrant its own package, it has not been written generally enough to be a part of a util package. Just leave it unexported and well-documented.
9. All tests should run with `go test` and outside tooling should not be required. No, we don't need another unit testing framework.
10. Even though we call these "rules" above, they are actually just guidelines. Since you've read all the rules, you now know that.
If you are having trouble getting into the mood of idiomatic Go, we recommend reading through [Effective Go](https://go.dev/doc/effective_go). The [Go Blog](https://go.dev/blog/) is also a great resource. Drinking the kool-aid is a lot easier than going thirsty.

15
NOTICE Normal file
View File

@@ -0,0 +1,15 @@
Docker attest
Copyright Docker attest authors
This product includes software developed at Docker, Inc. (https://www.docker.com).
The following is courtesy of our legal counsel:
Use and transfer of Docker may be subject to certain restrictions by the
United States and other governments.
It is your responsibility to ensure that your use and/or transfer does not
violate applicable laws.
For more information, please see https://www.bis.doc.gov
See also https://www.apache.org/dev/crypto.html and/or seek legal counsel.

384
README.md
View File

@@ -1,2 +1,382 @@
# attest
Library to create, verify, and evaluate policy for attestations on container images
# `attest`
<div align="center">
Library to create attestation signatures on container images, and verify images against policy.
[![Go Reference](https://pkg.go.dev/badge/github.com/docker/attest.svg)](https://pkg.go.dev/github.com/docker/attest)
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/docker/attest/test.yml?branch=main)](https://github.com/docker/attest/actions/workflows/test.yml)
[![codecov](https://codecov.io/gh/docker/attest/graph/badge.svg?token=cGT0f1ACKg)](https://codecov.io/gh/docker/attest)
</div>
# Table of Contents
- [`attest`](#attest)
- [Table of Contents](#table-of-contents)
- [What is this?](#what-is-this)
- [Features](#features)
- [Installation](#installation)
- [Usage](#usage)
- [Verifying Image Attestations](#verifying-image-attestations)
- [Signing Attestations](#signing-attestations)
- [Rego Policy](#rego-policy)
- [Writing Policy](#writing-policy)
- [Input](#input)
- [Builtin Functions](#builtin-functions)
- [Policy Mapping](#policy-mapping)
- [Public Key IDs](#public-key-ids)
- [Transparency Logging](#transparency-logging)
- [Verification Summary Attestation (VSA)](#verification-summary-attestation-vsa)
- [Example VSA](#example-vsa)
- [API Reference](#api-reference)
- [Project Layout](#project-layout)
- [Versioning](#versioning)
# What is this?
`attest` is a library for signing and verifying [in-toto](https://in-toto.io/) attestations on container images.
Examples of attestations include statements about the provenance and SBOM of an image.
This library can be used to verify these attestations using Rego policy.
Policy can be used to check whether an attestation is correctly signed, and that the contents of the attestation are correct.
# Features
- Sign in-toto attestations
- Push attestations to container registries using OCI 1.1 compatible artifacts
- Verify attestations on container images using Rego policy and attestations fetched using OCI 1.1 referrers
# Installation
```shell
$ go get github.com/docker/attest
```
# Usage
## Verifying Image Attestations
An image's attestations can be verified against a policy using the `attest.Verify` function.
This function takes an [oci.ImageSpec](https://github.com/docker/attest/blob/781a738b54b9549c1dabfd7ea3f7ea582514ddec/pkg/oci/types.go#L35-L41) for the image to verify, and a set of options for policy resolution.
By default, the policy is resolved from the [the Docker TUF repository](https://github.com/docker/tuf), but the options can be used to specify an alternative TUF repository, a local policy directory, and/or a policy ID to use.
See [Policy Mapping](#policy-mapping) for more details.
The `attest.Verify` function returns a `VerificationSummary` object, which contains the results of the policy evaluation.
See [example_verify_test.go](./example_verify_test.go) for an example of how to verify an image against a policy.
## Signing Attestations
in-toto statements can be signed directly using the `attestation.SignInTotoStatement` function.
This function takes a statement and DSSE signer, and returns a signed DSSE envelope containing a copy of the original statement.
For the common use case of signing a statement and adding it to a manifest, e.g. for pushing to a registry as a referrer to the image being attested, the `attestation.AttestationManifest` type can be used.
See [example_attestation_manifest_test.go](./pkg/attestation/example_attestation_manifest_test.go)
See also [example_sign_test.go](./example_sign_test.go) for an example of how to sign all attached in-toto statements on an image, e.g. those produced by buildkit.
# Rego Policy
An image policy consists of one or more `rego` files and, optionally, `json` or `yaml` data files.
The policies for trusted namespaces `docker.io/docker` and `docker.io/library` are stored in [the Docker TUF root](https://github.com/docker/tuf) under the `docker` and `doi` target sub-directories respectively.
## Writing Policy
`attest` uses [Open Policy Agent](https://www.openpolicyagent.org/) (OPA) for policy evaluation, and policies are written in Rego.
A full guide to writing Rego policies is available in the [Rego documentation](https://www.openpolicyagent.org/docs/latest/policy-language/).
For attest, a policy must contain at a minimum a `result` rule in a package called `attest` that returns an object matching the schema defined by the [`policy.Result`](https://github.com/docker/attest/blob/bd2c4d7d8aa497754b674412b09628be8d02fab5/pkg/policy/types.go#L23-L27) struct.
For example:
```rego
package attest
import rego.v1
result := {
"success": true,
"violations": set(),
"summary": {
"subjects": subjects,
"slsa_levels": ["SLSA_BUILD_LEVEL_3"],
"verifier": "docker-official-images",
"policy_uri": "https://docker.com/official/policy/v0.1",
},
}
```
The meanings of the fields in the `result` object are as follows:
- `success` (bool): whether the policy passes
- `violations` (set): a set of strings describing any policy violations
- `summary` (object): a summary of the policy evaluation, used to construct a Verification Summary Attestation (VSA)
- `subjects` (set): a set of strings representing the subjects of each attestation that was evaluated
- `slsa_levels` (list): a list of strings representing the SLSA levels that the policy complies with
- `verifier` (string): the entity that verified the policy
- `policy_uri` (string): the URI of the policy
The `violations` set may contain policy violations even if `success` is `true`.
This can be useful if there are attestations that are invalid, but are not required by the policy.
### Input
The input to the policy is an object with the following fields:
- `digest` (string): the digest of the image being verified
- `purl` (string): the package URL of the image being verified
- `platform` (string): the platform of the image being verified
- `normalized_name` (string): defaults are filled out. e.g. if the image is `alpine`, this would be `library/alpine`
- `familiar_name` (string): short version of above (e.g. `alpine`)
- `tag`: (string): tag of the image being verified (if present)
### Builtin Functions
There are two builtin functions provided by `attest` that can be used to help with policy evaluation:
- `attest.fetch(predicate_type)`: fetches all attestations for the input image with the given predicate type.
For example, `attest.fetch("https://spdx.dev/Document")` will fetch all SPDX SBOM attestations for the input image.
- `attest.verify(attestation, options)`: verifies the DSSE envelope of the given attestation, and returns the statement.
The options object can contain the following fields:
- `keys` (array): keys to use for signature verification. Each key contains the following fields:
- `id` (string): the key ID as specified in [Public Key IDs](#public-key-ids)
- `key` (string): the PEM-encoded public key
- `from` (string): the time from which the key is valid, or `null` if the key was always valid (default: `null`)
- `status` (string): `active` if the key is active, otherwise the reason the key is inactive.
This is only used in error messages if the `from` date is in the past
- `distrust` (bool): whether the key should be distrusted (default: `false`).
If `true`, the key will be considered invalid
- `signing-format` (string): the format of the signing key, must be `dssev1`
- `skip_tl` (bool): whether to skip transparency log entry verification (see [Transparency Logging](#transparency-logging)) (default: `false`)
Both `attest.fetch` and `attest.verify` return an object with the following fields:
- `value`: the return value of the function if successful
- `error`: an error message if the function failed
This is to allow the policy to easily construct a violation if an error occurs, which isn't usually possible with custom functions in Rego.
The return value of `attest.fetch` is an attestation which can be passed to `attest.verify`.
## Policy Mapping
A `mapping.yaml` file is stored at the root of TUF targets and contains the mapping from repository name to files containing the corresponding policy.
A simple mapping file might look like this:
```yaml
version: v1
kind: policy-mapping
policies:
- id: docker-official-images
description: Docker Official Images
files:
- path: doi/policy.rego
rules:
- pattern: "^docker[.]io/library/(.*)$"
policy-id: docker-official-images
```
The `policies` section contains a list of policies, each with an `id` and a `description`, and a list of `files` containing the policy.
The `rules` section contains a list of rules that map regex expressions to policies.
If the `pattern` regex matches the repository name, the policy with the `policy-id` is used to evaluate the image.
In the above example, any repository in the `docker.io/library` namespace will be evaluated against the policy in `doi/policy.rego`.
Sometimes it is necessary to rewrite the repository name before evaluating the policy.
This can be useful when the repository name which is used to reference the image is different from the repository name in the attestations.
For example, when mirroring images from a public registry to a private registry, the repository name in the attestations will be the public registry, but the image will be referenced by the name of the private registry.
An example of a mapping file with rewrite rules might look like this:
```yaml
version: v1
kind: policy-mapping
policies:
- id: docker-official-images
description: Docker Official Images
files:
- path: doi/policy.rego
rules:
- pattern: "^docker[.]io/library/(.*)$"
policy-id: docker-official-images
- pattern: "^public[.]ecr[.]aws/docker/library/(.*)$"
platforms: ["linux/amd64"] # optional: restrict image platforms for matching policies (default: all)
rewrite: docker.io/library/$1
```
`platforms` in the second rule above is optional and can be used to restrict the platforms for which the policy
is evaluated. If the `platforms` field is not present, the policy will be applied to all platforms.
It's important to note that the `platforms` field is a filter, and is applied before the `pattern`
field is processed, so both `platforms` and `pattern` need to match in order for the policy to be selected
(or the rewrite to be processed if present).
As before, any repository in the `docker.io/library` namespace will be evaluated against the policy in `doi/policy.rego`.
The second rule will rewrite any repository in the `public.ecr.aws/docker/library` namespace to `docker.io/library`.
This means two things:
1. The rules are evaluated again using the rewritten repository name until a policy is found (in this case the first rule will match); and
2. The rewritten name is passed into the actual policy when it is evaluated.
The `rewrite` field is not a simple string replacement, but a regex replacement.
This means that the `rewrite` field can contain capture groups that are referenced in the `pattern` field.
For example, the `rewrite` field in the example above contains `$1`, which is a reference to the first capture group in the `pattern` field.
> [!IMPORTANT]
> It's important to remember to escape the `.` character in the `pattern` field, as it is a special character in regex.
> This is why the `.` character is surrounded by `[]` in the example above.
>
> It's also important to make use of the `^` and `$` characters in the `pattern` field to ensure that the regex matches the entire repository name.
> This is to prevent the regex from matching a subset of the repository name, e.g. `docker.io/library` matching `notdocker.io/library`.
Local policy can also be specified via a local `mapping.yaml`, which can be used to create new mirrors of policies described in the Docker TUF root, as well as describing entirely independent policies. For example:
```golang
// configure policy options
opts := &policy.PolicyOptions{
TufClient: tufClient,
LocalPolicyDir: "<policy-dir>", // overrides TUF policy for local policy files if set
PolicyId: "<policy-id>", // set to ignore policy mapping and select a policy by id
}
src, err := oci.ParseImageSpec(image, oci.WithPlatform(platform))
if err != nil {
panic(err)
}
// verify attestations
result, err := attest.Verify(context.Background(), src, opts)
if err != nil {
panic(err)
}
```
where `<policy-dir>` is a directory containing a `mapping.yaml` file, and any policy files referenced in the `mapping.yaml`. For example:
```
├── myimages
│ ├── data.yaml
| ├── keys.yaml
│ └── policy.rego
└── mapping.yaml
```
> [!NOTE]
>
> `PolicyId` can also be set to select a policy by ID, completely ignoring the `rules` section of the mapping file.
The rules section of a local `mapping.yaml` can refer to the policies described in the `mapping.yaml` file in the Docker TUF root to specify additional mirrors to which the referenced policy can be applied.
For example, it might be desirable to mirror `docker.io/library` to a local registry for testing:
```yaml
version: v1
kind: policy-mapping
rules:
- pattern: "^localhost:5001/(.*)$"
rewrite: docker.io/library/$1
```
The rewritten repository name will match the `docker-official-images` polict in the TUF managed `mapping.yaml`.
> [!WARNING]
> Local `mapping.yaml` policies take precendence over TUF managed policies, so for example, it's possible to apply a custom policy to `docker.io/library` namespace:
>
> ```yaml
> version: v1
> kind: policy-mapping
> policies:
> - id: mydoi
> description: my doi policy
> files:
> - path: "mypolicy.rego"
>
> rules:
> - pattern: "^docker[.]io/library/(.*)$"
> policy-id: mydoi
> ```
# Public Key IDs
When signing attestations, a key-id is generated from the public key and added to envelope.
This is used at verification time to look up the public key.
To generate a key-id from a public key, use `openssl` as follows:
```shell
openssl pkey -in <public-key.pem> -pubin -outform DER | openssl dgst -sha256
```
# Transparency Logging
`attest` supports transparency logging for attestation signatures.
This serves two purposes:
1. the transparency log is a mechanism to ensure that all attestations are logged in a tamper-evident way, and that the logs are publicly auditable; and
2. the transparency log is a trusted source of timestamps for attestations, which allows signatures to be verified even if the key used to sign the attestation has expired.
By default, transparency logging is enabled and the logs are stored in the [public-good Rekor](https://docs.sigstore.dev/logging/overview/) instance.
Another transparency log can be used by creating an implementation of the [tl.TL](https://github.com/docker/attest/blob/781a738b54b9549c1dabfd7ea3f7ea582514ddec/pkg/tlog/tl.go#L57-L62) interface and using [`tl.WithTL`](https://github.com/docker/attest/blob/781a738b54b9549c1dabfd7ea3f7ea582514ddec/pkg/tlog/tl.go#L37) to set in on a context.
Alternatively, transparency logging can be disabled when signing by using `SkipTL` in the `SigningOptions`, and when verifying by using `skip_tl` in the options to `attest.verify` in the Rego policy.
# Verification Summary Attestation (VSA)
Verification of attestations can be expensive, especially when the attestations are large.
For example, an SBOM attestation can be several megabytes in size.
An alternative to consumers verifying the full attestation is to have a trusted entity verify the attestation and publish a [SLSA Verification Summary Attestation](https://slsa.dev/spec/v1.0/verification_summary) (VSA) to the registry.
The VSA can then be verified by the consumer without needing to verify the full attestation, as long as the consumer trusts the entity that signed the VSA.
This is useful when the consumer only needs to know that the attestation was verified by a trusted entity, and does not need to know the details of the attestation.
A useful pattern is to have apply a policy to a third-party image at initial ingress, then publish a VSA when publishing the image to an internal registry to attest that the image complies with the policy.
The VSA can be verified very quickly, for example in a Kubernetes admission controller.
`attest` always generates a [SLSA VSA](https://slsa.dev/spec/v1.0/verification_summary) when verifying attestations on an image.
The VSA can be signed and published to the registry using the signing functions mentioned in [Signing Attestations](#signing-attestations).
## Example VSA
```json
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [
{
"name": "pkg:docker/example.org/example-image@1.0?platform=linux%2Famd64",
"digest": {
"sha256": "49f717386e5462e945232569a97a05831cb83bef8c3369be3bb7ea1793686960"
}
}
],
"predicateType": "https://slsa.dev/verification_summary/v1",
"predicate": {
"verifier": {
"id": "https://example.org/internal-verifier"
},
"timeVerified": "2024-04-19T08:00:00.01Z",
"resourceUri": "pkg:docker/example.org/example-image@1.0?platform=linux%2Famd64&digest=sha256%3A49f717386e5462e945232569a97a05831cb83bef8c3369be3bb7ea1793686960",
"policy": {
"uri": "https://example.org/internal-policy/v1",
"downloadLocation": "https://docker.github.io/tuf-staging/targets/docker/d71d6b8f49fcba1295b16f5394dd5863a14e4277eb663d66d8c48e392509afe0.policy.rego",
"digest": {
"sha256": "d71d6b8f49fcba1295b16f5394dd5863a14e4277eb663d66d8c48e392509afe0"
}
},
"verificationResult": "PASSED",
"verifiedLevels": ["SLSA_BUILD_LEVEL_3"]
}
}
```
# API Reference
Full API reference can be found at [pkg.go.dev/github.com/docker/attest](https://pkg.go.dev/github.com/docker/attest).
# Project Layout
- [pkg/](https://pkg.go.dev/github.com/docker/image-signer-verifier/pkg) => packages that are okay to import for other projects
- [internal/](https://pkg.go.dev/github.com/docker/image-signer-verifier/pkg) => packages that are only for project internal purposes
- [scripts/](scripts/) => build scripts
- [test/](test/) => data for use in tests
# Versioning
`attest` uses [Semantic Versioning](https://semver.org/).
As such, until `attest` reaches version 1.0.0, breaking changes may be introduced in minor versions.
> Anything MAY change at any time. The public API SHOULD NOT be considered stable.

4
attestation/README.md Normal file
View File

@@ -0,0 +1,4 @@
## attestations
This package is for components that deal with the creation, storage, and retrieval of signed attestions using OCI.
For more generic OCI components see the `oci` package.

470
attestation/attestation.go Normal file
View File

@@ -0,0 +1,470 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"maps"
"strings"
"github.com/docker/attest/oci"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/google/go-containerregistry/pkg/v1/match"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/partial"
"github.com/google/go-containerregistry/pkg/v1/static"
"github.com/google/go-containerregistry/pkg/v1/types"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
// NewManifest creates a new attestation manifest from a descriptor.
func NewManifest(subject *v1.Descriptor) (*Manifest, error) {
return &Manifest{
OriginalDescriptor: &v1.Descriptor{
MediaType: "application/vnd.oci.image.manifest.v1+json",
},
OriginalLayers: []*Layer{},
SubjectDescriptor: subject,
}, nil
}
// ManifestsFromIndex extracts all attestation manifests from an index.
func ManifestsFromIndex(index v1.ImageIndex) ([]*Manifest, error) {
idx, err := index.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to extract IndexManifest from ImageIndex: %w", err)
}
subjects := make(map[string]*v1.Descriptor)
for i := range idx.Manifests {
subject := &idx.Manifests[i]
subjects[subject.Digest.String()] = subject
}
var attestationManifests []*Manifest
for i := range idx.Manifests {
desc := idx.Manifests[i]
if desc.Annotations[DockerReferenceType] == AttestationManifestType {
subject := subjects[desc.Annotations[DockerReferenceDigest]]
if subject == nil {
return nil, fmt.Errorf("failed to find subject for attestation manifest: %w", err)
}
attestationImage, err := index.Image(desc.Digest)
if err != nil {
return nil, fmt.Errorf("failed to extract attestation image with digest %s: %w", desc.Digest.String(), err)
}
attestationLayers, err := layersFromImage(attestationImage)
if err != nil {
return nil, fmt.Errorf("failed to get attestations from image: %w", err)
}
attestationManifests = append(attestationManifests,
&Manifest{
OriginalDescriptor: &desc,
SubjectDescriptor: subject,
OriginalLayers: attestationLayers,
})
}
}
return attestationManifests, nil
}
// LayersFromImage extracts all attestation layers from an image.
func layersFromImage(image v1.Image) ([]*Layer, error) {
layers, err := image.Layers()
if err != nil {
return nil, fmt.Errorf("failed to extract layers from image: %w", err)
}
var attestationLayers []*Layer
for _, layer := range layers {
// parse layer blob as json
r, err := layer.Uncompressed()
if err != nil {
return nil, fmt.Errorf("failed to get layer contents: %w", err)
}
defer r.Close()
mt, err := layer.MediaType()
if err != nil {
return nil, fmt.Errorf("failed to get layer media type: %w", err)
}
layerDesc, err := partial.Descriptor(layer)
if err != nil {
return nil, fmt.Errorf("failed to get descriptor for layer: %w", err)
}
// copy original annotations
ann := maps.Clone(layerDesc.Annotations)
// only decode intoto statements
var stmt *intoto.Statement
if mt == types.MediaType(intoto.PayloadType) {
stmt = new(intoto.Statement)
err = json.NewDecoder(r).Decode(&stmt)
if err != nil {
return nil, fmt.Errorf("failed to decode statement layer contents: %w", err)
}
}
attestationLayers = append(attestationLayers, &Layer{Layer: layer, Statement: stmt, Annotations: ann})
}
return attestationLayers, nil
}
func (manifest *Manifest) Add(ctx context.Context, signer dsse.SignerVerifier, statement *intoto.Statement, opts *SigningOptions) error {
layer, err := createSignedImageLayer(ctx, statement, signer, opts)
if err != nil {
return fmt.Errorf("failed to create signed layer: %w", err)
}
manifest.SignedLayers = append(manifest.SignedLayers, layer)
return nil
}
func createSignedImageLayer(ctx context.Context, statement *intoto.Statement, signer dsse.SignerVerifier, opts *SigningOptions) (*Layer, error) {
// sign the statement
env, err := signInTotoStatement(ctx, statement, signer, opts)
if err != nil {
return nil, fmt.Errorf("failed to sign statement: %w", err)
}
mediaType, err := DSSEMediaType(statement.PredicateType)
if err != nil {
return nil, fmt.Errorf("failed to get DSSE media type: %w", err)
}
data, err := json.Marshal(env)
if err != nil {
return nil, fmt.Errorf("failed to marshal envelope: %w", err)
}
return &Layer{
Statement: statement,
Annotations: map[string]string{
InTotoPredicateType: statement.PredicateType,
InTotoReferenceLifecycleStage: LifecycleStageExperimental,
},
Layer: static.NewLayer(data, types.MediaType(mediaType)),
}, nil
}
func signInTotoStatement(ctx context.Context, statement *intoto.Statement, signer dsse.SignerVerifier, opts *SigningOptions) (*Envelope, error) {
payload, err := json.Marshal(statement)
if err != nil {
return nil, fmt.Errorf("failed to marshal statement: %w", err)
}
env, err := SignDSSE(ctx, payload, signer, opts)
if err != nil {
return nil, fmt.Errorf("failed to sign statement: %w", err)
}
return env, nil
}
func updateImageIndex(
idx v1.ImageIndex,
manifest *Manifest,
options ...func(*ManifestImageOptions) error,
) (v1.ImageIndex, error) {
image, err := manifest.BuildImage(options...)
if err != nil {
return nil, fmt.Errorf("failed to build image: %w", err)
}
newDesc, err := partial.Descriptor(image)
if err != nil {
return nil, fmt.Errorf("failed to get descriptor: %w", err)
}
newDesc.Platform = &v1.Platform{
Architecture: "unknown",
OS: "unknown",
}
newDesc.MediaType = manifest.OriginalDescriptor.MediaType
newDesc.Annotations = manifest.OriginalDescriptor.Annotations
idx = mutate.RemoveManifests(idx, match.Digests(manifest.OriginalDescriptor.Digest))
idx = mutate.AppendManifests(idx, mutate.IndexAddendum{
Add: image,
Descriptor: *newDesc,
})
return idx, nil
}
func UpdateIndexImages(idx v1.ImageIndex, manifest []*Manifest, options ...func(*ManifestImageOptions) error) (v1.ImageIndex, error) {
var err error
for _, m := range manifest {
idx, err = updateImageIndex(idx, m, options...)
if err != nil {
return nil, fmt.Errorf("failed to add image to index: %w", err)
}
}
return idx, nil
}
func newOptions(options ...func(*ManifestImageOptions) error) (*ManifestImageOptions, error) {
opts := &ManifestImageOptions{}
for _, opt := range options {
err := opt(opts)
if err != nil {
return nil, err
}
}
return opts, nil
}
func WithoutSubject(skipSubject bool) func(*ManifestImageOptions) error {
return func(r *ManifestImageOptions) error {
r.skipSubject = skipSubject
return nil
}
}
func WithReplacedLayers(replaceLayers bool) func(*ManifestImageOptions) error {
return func(r *ManifestImageOptions) error {
r.replaceLayers = replaceLayers
return nil
}
}
// build an image with signed attestations, optionally replacing existing layers with signed layers.
func (manifest *Manifest) BuildImage(options ...func(*ManifestImageOptions) error) (v1.Image, error) {
opts, err := newOptions(options...)
if err != nil {
return nil, fmt.Errorf("failed to create options: %w", err)
}
resultLayers := manifest.SignedLayers
for _, existingLayer := range manifest.OriginalLayers {
var found bool
for _, signedLayer := range manifest.SignedLayers {
if existingLayer.Statement == signedLayer.Statement {
found = true
// copy over original annotations
for k, v := range existingLayer.Annotations {
signedLayer.Annotations[k] = v
}
break
}
}
// add existing layers if they've not been signed or we're not replacing them
if !found || !opts.replaceLayers {
resultLayers = append(resultLayers, existingLayer)
}
}
// so that we attach all attestations to a single attestations image - as per current buildkit
opts.laxReferrers = true
newImg, err := buildImageFromLayers(resultLayers, manifest.OriginalDescriptor, manifest.SubjectDescriptor, opts)
if err != nil {
return nil, fmt.Errorf("failed to build image: %w", err)
}
return newImg, nil
}
// build an image per attestation (layer) suitable for use as Referrers.
func (manifest *Manifest) BuildReferringArtifacts() ([]v1.Image, error) {
var images []v1.Image
for _, layer := range manifest.SignedLayers {
opts := &ManifestImageOptions{}
newImg, err := buildImageFromLayers([]*Layer{layer}, manifest.OriginalDescriptor, manifest.SubjectDescriptor, opts)
if err != nil {
return nil, fmt.Errorf("failed to build image: %w", err)
}
images = append(images, newImg)
}
return images, nil
}
// build an image containing only layers provided.
func buildImageFromLayers(layers []*Layer, manifest *v1.Descriptor, subject *v1.Descriptor, opts *ManifestImageOptions) (v1.Image, error) {
newImg := empty.Image
var err error
if len(layers) == 0 {
return nil, fmt.Errorf("no layers supplied to build image")
}
// NB: if we add the subject before the layers, it does not end up being computed/serialized in the output for some reason
// TODO - recreate this bug and push upstream
for _, layer := range layers {
add := mutate.Addendum{
Layer: layer.Layer,
Annotations: layer.Annotations,
}
newImg, err = mutate.Append(newImg, add)
if err != nil {
return nil, fmt.Errorf("failed to add layer to image: %w", err)
}
}
// this is for attaching attestations to an attestation image in the index
if opts.laxReferrers {
newImg = mutate.ConfigMediaType(newImg, "application/vnd.oci.image.config.v1+json")
} else {
dsseMediatType, err := DSSEMediaType(layers[0].Statement.PredicateType)
if err != nil {
return nil, fmt.Errorf("failed to get DSSE media type: %w", err)
}
newImg = mutate.ArtifactType(newImg, dsseMediatType)
newImg = mutate.ConfigMediaType(newImg, "application/vnd.oci.empty.v1+json")
}
// we need to set this even when we set the artifact type otherwise things break (even the go-container-registry client)
// even though it's allowed to be empty by spec when setting artifact type
newImg = mutate.MediaType(newImg, manifest.MediaType)
// see note above - must be added after the layers!
if !opts.skipSubject {
subject.Platform = nil
ok := false
newImg, ok = mutate.Subject(newImg, *subject).(v1.Image)
if !ok {
return nil, fmt.Errorf("failed to set subject: %w", err)
}
}
if !opts.laxReferrers {
// as per https://github.com/opencontainers/image-spec/blob/main/manifest.md#guidance-for-an-empty-descriptor
newImg = &oci.EmptyConfigImage{Image: newImg}
}
return newImg, nil
}
func ExtractEnvelopes(manifest *Manifest, predicateType string) ([]*EnvelopeReference, error) {
var envs []*EnvelopeReference
dsseMediaType, err := DSSEMediaType(predicateType)
if err != nil {
return nil, fmt.Errorf("failed to get DSSE media type for predicate '%s': %w", predicateType, err)
}
for _, attestationLayer := range manifest.OriginalLayers {
mt, err := attestationLayer.Layer.MediaType()
if err != nil {
return nil, fmt.Errorf("failed to get layer media type: %w", err)
}
if string(mt) == dsseMediaType {
reader, err := attestationLayer.Layer.Uncompressed()
if err != nil {
return nil, fmt.Errorf("failed to get layer contents: %w", err)
}
defer reader.Close()
env := new(EnvelopeReference)
err = json.NewDecoder(reader).Decode(&env)
if err != nil {
return nil, fmt.Errorf("failed to decode envelope: %w", err)
}
var uri string
if len(manifest.OriginalDescriptor.URLs) > 0 {
uri = manifest.OriginalDescriptor.URLs[0]
}
env.ResourceDescriptor = &ResourceDescriptor{
MediaType: string(mt),
Digest: map[string]string{manifest.OriginalDescriptor.Digest.Algorithm: manifest.OriginalDescriptor.Digest.Hex},
URI: uri,
}
envs = append(envs, env)
}
}
return envs, nil
}
func ExtractStatementsFromIndex(idx v1.ImageIndex, mediaType string) ([]*AnnotatedStatement, error) {
mfs2, err := idx.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to extract IndexManifest from ImageIndex: %w", err)
}
var statements []*AnnotatedStatement
for i := range mfs2.Manifests {
mf := &mfs2.Manifests[i]
if mf.Annotations[DockerReferenceType] != "attestation-manifest" {
continue
}
attestationImage, err := idx.Image(mf.Digest)
if err != nil {
return nil, fmt.Errorf("failed to extract attestation image with digest %s: %w", mf.Digest.String(), err)
}
layers, err := attestationImage.Layers()
if err != nil {
return nil, fmt.Errorf("failed to extract layers from attestation image: %w", err)
}
for _, layer := range layers {
// parse layer blob as json
mt, err := layer.MediaType()
if err != nil {
return nil, fmt.Errorf("failed to get layer media type: %w", err)
}
if string(mt) != mediaType {
continue
}
r, err := layer.Uncompressed()
if err != nil {
return nil, fmt.Errorf("failed to get layer contents: %w", err)
}
defer r.Close()
inTotoStatement := new(intoto.Statement)
var desc *v1.Descriptor
if strings.HasSuffix(string(mt), "+dsse") {
env := new(Envelope)
err = json.NewDecoder(r).Decode(env)
if err != nil {
return nil, fmt.Errorf("failed to decode env: %w", err)
}
payload, err := base64.StdEncoding.Strict().DecodeString(env.Payload)
if err != nil {
return nil, fmt.Errorf("failed to decode payload: %w", err)
}
err = json.Unmarshal([]byte(payload), inTotoStatement)
if err != nil {
return nil, fmt.Errorf("failed to decode %s statement: %w", mediaType, err)
}
} else {
desc := new(v1.Descriptor)
err = json.NewDecoder(r).Decode(desc)
if err != nil {
return nil, fmt.Errorf("failed to decode statement: %w", err)
}
}
layerDesc, err := partial.Descriptor(layer)
if err != nil {
return nil, fmt.Errorf("failed to get descriptor for layer: %w", err)
}
annotations := make(map[string]string)
for k, v := range layerDesc.Annotations {
annotations[k] = v
}
statements = append(statements, &AnnotatedStatement{
OCIDescriptor: desc,
InTotoStatement: inTotoStatement,
Annotations: annotations,
})
}
}
return statements, nil
}
func ExtractAnnotatedStatements(path string, mediaType string) ([]*AnnotatedStatement, error) {
idx, err := layout.ImageIndexFromPath(path)
if err != nil {
return nil, fmt.Errorf("failed to load image index: %w", err)
}
idxm, err := idx.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to get digest: %w", err)
}
idxDigest := idxm.Manifests[0].Digest
mfs, err := idx.ImageIndex(idxDigest)
if err != nil {
return nil, fmt.Errorf("failed to extract ImageIndex for digest %s: %w", idxDigest.String(), err)
}
return ExtractStatementsFromIndex(mfs, mediaType)
}

View File

@@ -0,0 +1,34 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation_test
import (
"testing"
"github.com/docker/attest/attestation"
"github.com/docker/attest/internal/test"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/stretchr/testify/assert"
)
const ExpectedStatements = 4
func TestExtractAnnotatedStatements(t *testing.T) {
statements, err := attestation.ExtractAnnotatedStatements(test.UnsignedTestIndex(".."), intoto.PayloadType)
assert.NoError(t, err)
assert.Equalf(t, len(statements), ExpectedStatements, "expected %d statement, got %d", ExpectedStatements, len(statements))
}

View File

@@ -0,0 +1,106 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation_test
import (
"context"
"time"
"github.com/docker/attest/attestation"
"github.com/docker/attest/oci"
"github.com/docker/attest/signerverifier"
v1 "github.com/google/go-containerregistry/pkg/v1"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
)
func ExampleManifest() {
// configure signerverifier
// local signer (unsafe for production)
signer, err := signerverifier.GenKeyPair()
if err != nil {
panic(err)
}
// example using AWS KMS signer
// aws_arn := "arn:aws:kms:us-west-2:123456789012:key/12345678-1234-1234-1234-123456789012"
// aws_region := "us-west-2"
// signer, err := signerverifier.GetAWSSigner(cmd.Context(), aws_arn, aws_region)
// configure signing options
opts := &attestation.SigningOptions{
TransparencyLog: nil, // set this to log to a transparency log
}
ref := "docker/image-signer-verifier:latest"
digest, err := v1.NewHash("sha256:7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390")
if err != nil {
panic(err)
}
desc := &v1.Descriptor{
Digest: digest,
Size: 1234,
MediaType: "application/vnd.oci.image.manifest.v1+json",
}
// the in-toto statement to be signed
statement := &intoto.Statement{
StatementHeader: intoto.StatementHeader{
PredicateType: attestation.VSAPredicateType,
Subject: []intoto.Subject{{Name: ref, Digest: common.DigestSet{digest.Algorithm: digest.Hex}}},
Type: intoto.StatementInTotoV01,
},
Predicate: attestation.VSAPredicate{
Verifier: attestation.VSAVerifier{
ID: "test-verifier",
},
TimeVerified: time.Now().UTC().Format(time.RFC3339),
ResourceURI: "some-uri",
Policy: attestation.VSAPolicy{URI: "some-uri"},
VerificationResult: "PASSED",
VerifiedLevels: []string{"SLSA_BUILD_LEVEL_1"},
},
}
// create a new manifest to hold the attestation
manifest, err := attestation.NewManifest(desc)
if err != nil {
panic(err)
}
// sign and add the attestation to the manifest
err = manifest.Add(context.Background(), signer, statement, opts)
if err != nil {
panic(err)
}
output, err := oci.ParseImageSpecs("docker/image-signer-verifier-referrers:latest")
if err != nil {
panic(err)
}
// save the manifest to the registry as a referrers artifact
artifacts, err := manifest.BuildReferringArtifacts()
if err != nil {
panic(err)
}
ctx := context.Background()
err = oci.SaveImagesNoTag(ctx, artifacts, output)
if err != nil {
panic(err)
}
}

197
attestation/layout.go Normal file
View File

@@ -0,0 +1,197 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation
import (
"context"
"encoding/json"
"fmt"
containerd "github.com/containerd/containerd/v2/core/images"
"github.com/distribution/reference"
"github.com/docker/attest/oci"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/layout"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// implementation of Resolver that closes over attestations from an oci layout.
var _ Resolver = (*LayoutResolver)(nil)
type LayoutResolver struct {
*Manifest
*oci.ImageSpec
}
func NewOCILayoutResolver(src *oci.ImageSpec) (*LayoutResolver, error) {
r := &LayoutResolver{
ImageSpec: src,
}
_, err := r.fetchManifest()
if err != nil {
return nil, err
}
return r, nil
}
func (r *LayoutResolver) fetchManifest() (*Manifest, error) {
if r.Manifest == nil {
m, err := manifestFromOCILayout(r.Identifier, r.ImageSpec.Platform)
if err != nil {
return nil, err
}
r.Manifest = m
}
return r.Manifest, nil
}
func (r *LayoutResolver) Attestations(_ context.Context, predicateType string) ([]*EnvelopeReference, error) {
var envs []*EnvelopeReference
dsseMediaType, err := DSSEMediaType(predicateType)
if err != nil {
return nil, fmt.Errorf("failed to get DSSE media type for predicate '%s': %w", predicateType, err)
}
for _, attestationLayer := range r.Manifest.OriginalLayers {
mt, err := attestationLayer.Layer.MediaType()
if err != nil {
return nil, fmt.Errorf("failed to get layer media type: %w", err)
}
mts := string(mt)
if mts != dsseMediaType {
continue
}
env := new(EnvelopeReference)
// parse layer blob as json
layer, err := attestationLayer.Layer.Uncompressed()
if err != nil {
return nil, fmt.Errorf("failed to get layer contents: %w", err)
}
defer layer.Close()
err = json.NewDecoder(layer).Decode(env)
if err != nil {
return nil, fmt.Errorf("failed to decode envelope: %w", err)
}
var uri string
if len(r.Manifest.OriginalDescriptor.URLs) > 0 {
uri = r.Manifest.OriginalDescriptor.URLs[0]
}
env.ResourceDescriptor = &ResourceDescriptor{
MediaType: string(mt),
Digest: map[string]string{r.Manifest.OriginalDescriptor.Digest.Algorithm: r.Manifest.OriginalDescriptor.Digest.Hex},
URI: uri,
}
envs = append(envs, env)
}
return envs, nil
}
func (r *LayoutResolver) ImageName(_ context.Context) (string, error) {
return r.SubjectName, nil
}
func (r *LayoutResolver) ImageDescriptor(_ context.Context) (*v1.Descriptor, error) {
return r.SubjectDescriptor, nil
}
func (r *LayoutResolver) ImagePlatform(_ context.Context) (*v1.Platform, error) {
return r.ImageSpec.Platform, nil
}
func manifestFromOCILayout(path string, platform *v1.Platform) (*Manifest, error) {
layoutIndex, err := layout.ImageIndexFromPath(path)
if err != nil {
return nil, err
}
layoutIndexManifest, err := layoutIndex.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to get digest: %w", err)
}
layoutDescriptor := layoutIndexManifest.Manifests[0]
layoutDescriptorDigest := layoutDescriptor.Digest
subjectName := layoutDescriptor.Annotations[ocispec.AnnotationRefName]
if _, err := reference.ParseNamed(subjectName); err != nil {
// try the containerd annotation if the org.opencontainers.image.ref.name is not a full name
subjectName = layoutDescriptor.Annotations[containerd.AnnotationImageName]
if _, err := reference.ParseNamed(subjectName); err != nil {
return nil, fmt.Errorf("failed to find subject name in annotations")
}
}
// check if digest refers to an image or an index
_, err = layoutIndex.Image(layoutDescriptorDigest)
if err == nil {
return &Manifest{
OriginalLayers: nil,
OriginalDescriptor: nil,
SubjectName: subjectName,
SubjectDescriptor: &layoutDescriptor,
}, nil
}
subjectIndex, err := layoutIndex.ImageIndex(layoutDescriptorDigest)
if err != nil {
return nil, fmt.Errorf("failed to extract ImageIndex for digest %s: %w", layoutDescriptorDigest.String(), err)
}
subjectIndexManifest, err := subjectIndex.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to extract IndexManifest from ImageIndex: %w", err)
}
var subjectDescriptor *v1.Descriptor
for i := range subjectIndexManifest.Manifests {
manifest := &subjectIndexManifest.Manifests[i]
if manifest.Platform != nil {
if manifest.Platform.Equals(*platform) {
subjectDescriptor = manifest
break
}
}
}
if subjectDescriptor == nil {
return nil, fmt.Errorf("platform not found in index")
}
for i := range subjectIndexManifest.Manifests {
mf := &subjectIndexManifest.Manifests[i]
if mf.Annotations[DockerReferenceType] != AttestationManifestType {
continue
}
if mf.Annotations[DockerReferenceDigest] != subjectDescriptor.Digest.String() {
continue
}
attestationImage, err := subjectIndex.Image(mf.Digest)
if err != nil {
return nil, fmt.Errorf("failed to extract attestation image with digest %s: %w", mf.Digest.String(), err)
}
layers, err := layersFromImage(attestationImage)
if err != nil {
return nil, fmt.Errorf("failed to get attestations from image: %w", err)
}
attest := &Manifest{
OriginalLayers: layers,
OriginalDescriptor: mf,
SubjectName: subjectName,
SubjectDescriptor: subjectDescriptor,
}
return attest, nil
}
return nil, fmt.Errorf("attestation manifest not found")
}

123
attestation/layout_test.go Normal file
View File

@@ -0,0 +1,123 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation_test
import (
"context"
"path/filepath"
"strings"
"testing"
"github.com/docker/attest"
"github.com/docker/attest/attestation"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/oci"
"github.com/docker/attest/policy"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAttestationFromOCILayout(t *testing.T) {
ctx, signer := test.Setup(t)
outputLayout := test.CreateTempDir(t, "", "attest-oci-layout")
invalidPlatform := &v1.Platform{
Architecture: "invalid",
OS: "invalid",
}
opts := &attestation.SigningOptions{}
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
signedManifests, err := attest.SignStatements(ctx, attIdx.Index, signer, opts)
require.NoError(t, err)
signedIndex := attIdx.Index
signedIndex, err = attestation.UpdateIndexImages(signedIndex, signedManifests)
require.NoError(t, err)
spec, err := oci.ParseImageSpec(oci.LocalPrefix + outputLayout)
require.NoError(t, err)
err = oci.SaveIndex(ctx, []*oci.ImageSpec{spec}, signedIndex, "docker.io/library/test-image:test")
require.NoError(t, err)
testCases := []struct {
name string
platform *v1.Platform
errorStr string
}{
{name: "nominal", platform: spec.Platform},
{name: "invalid platform", platform: invalidPlatform, errorStr: "platform not found in index"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
spec := &oci.ImageSpec{
Type: oci.OCI,
Identifier: outputLayout,
Platform: tc.platform,
}
resolver, err := policy.CreateImageDetailsResolver(spec)
if tc.errorStr != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tc.errorStr)
return
}
require.NoError(t, err)
desc, err := resolver.ImageDescriptor(ctx)
require.NoError(t, err)
digest := desc.Digest.String()
assert.True(t, strings.Contains(digest, "sha256:"))
})
}
}
func TestSubjectNameAnnotations(t *testing.T) {
testCases := []struct {
name string
ociLayoutPath string
errorStr string
}{
{name: "oci annotation", ociLayoutPath: test.UnsignedTestIndex("..")},
{name: "containerd annotation", ociLayoutPath: filepath.Join("..", "test", "testdata", "containerd-subject-layout")},
{name: "missing subject name", ociLayoutPath: filepath.Join("..", "test", "testdata", "missing-subject-layout"), errorStr: "failed to find subject name in annotations"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
spec, err := oci.ParseImageSpec(oci.LocalPrefix+tc.ociLayoutPath, oci.WithPlatform("linux/arm64"))
require.NoError(t, err)
_, err = policy.CreateImageDetailsResolver(spec)
if tc.errorStr != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tc.errorStr)
return
}
require.NoError(t, err)
})
}
}
func TestImageDetailsFromImageLayout(t *testing.T) {
spec, err := oci.ParseImageSpec(oci.LocalPrefix+test.UnsignedTestImage(".."), oci.WithPlatform("linux/arm64"))
require.NoError(t, err)
resolver, err := policy.CreateImageDetailsResolver(spec)
require.NoError(t, err)
desc, err := resolver.ImageDescriptor(context.Background())
require.NoError(t, err)
digest := desc.Digest.String()
assert.Equal(t, "sha256:7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390", digest)
}

86
attestation/mock.go Normal file
View File

@@ -0,0 +1,86 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation
import (
"context"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/oci"
v1 "github.com/google/go-containerregistry/pkg/v1"
)
// ensure MockResolver implements Resolver.
var _ oci.ImageDetailsResolver = MockResolver{}
type MockResolver struct {
Envs []*EnvelopeReference
Image string
PlatformFn func() (*v1.Platform, error)
DescriptorFn func() (*v1.Descriptor, error)
ImangeNameFn func() (string, error)
}
func (r MockResolver) Attestations(_ context.Context, _ string) ([]*EnvelopeReference, error) {
return r.Envs, nil
}
func (r MockResolver) ImageName(_ context.Context) (string, error) {
if r.Image != "" {
return r.Image, nil
}
if r.ImangeNameFn != nil {
return r.ImangeNameFn()
}
return "library/alpine:latest", nil
}
func (r MockResolver) ImageDescriptor(_ context.Context) (*v1.Descriptor, error) {
if r.DescriptorFn != nil {
return r.DescriptorFn()
}
digest, err := v1.NewHash(test.UnsignedLinuxAMD64ImageDigest)
if err != nil {
return nil, err
}
return &v1.Descriptor{
Digest: digest,
Size: 1234,
MediaType: "application/vnd.oci.image.manifest.v1+json",
}, nil
}
func (r MockResolver) ImagePlatform(_ context.Context) (*v1.Platform, error) {
if r.PlatformFn != nil {
return r.PlatformFn()
}
return oci.ParsePlatform("linux/amd64")
}
type MockRegistryResolver struct {
Subject *v1.Descriptor
ImageNameStr string
*MockResolver
}
func (r *MockRegistryResolver) ImageDescriptor(_ context.Context) (*v1.Descriptor, error) {
return r.Subject, nil
}
func (r *MockRegistryResolver) ImageName(_ context.Context) (string, error) {
return r.ImageNameStr, nil
}

142
attestation/referrers.go Normal file
View File

@@ -0,0 +1,142 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation
import (
"context"
"fmt"
"strings"
"github.com/docker/attest/oci"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
)
// ensure ReferrersResolver implements Resolver.
var _ Resolver = &ReferrersResolver{}
type ReferrersResolver struct {
referrersRepo string
oci.ImageDetailsResolver
}
func NewReferrersResolver(src oci.ImageDetailsResolver, options ...func(*ReferrersResolver) error) (*ReferrersResolver, error) {
res := &ReferrersResolver{
ImageDetailsResolver: src,
}
for _, opt := range options {
err := opt(res)
if err != nil {
return nil, err
}
}
return res, nil
}
func WithReferrersRepo(repo string) func(*ReferrersResolver) error {
return func(r *ReferrersResolver) error {
r.referrersRepo = repo
return nil
}
}
func (r *ReferrersResolver) resolveAttestations(ctx context.Context, predicateType string) ([]*Manifest, error) {
dsseMediaType, err := DSSEMediaType(predicateType)
if err != nil {
return nil, fmt.Errorf("failed to get DSSE media type for predicate '%s': %w", predicateType, err)
}
imageName, err := r.ImageName(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get image name: %w", err)
}
subjectRef, err := name.ParseReference(imageName)
if err != nil {
return nil, fmt.Errorf("failed to parse reference: %w", err)
}
desc, err := r.ImageDescriptor(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get descriptor: %w", err)
}
subjectDigest := desc.Digest.String()
var referrersSubjectRef name.Digest
if r.referrersRepo != "" {
referrersSubjectRef, err = name.NewDigest(fmt.Sprintf("%s@%s", strings.TrimPrefix(r.referrersRepo, oci.RegistryPrefix), subjectDigest))
if err != nil {
return nil, fmt.Errorf("failed to create referrers reference: %w", err)
}
} else {
referrersSubjectRef = subjectRef.Context().Digest(subjectDigest)
}
options := oci.WithOptions(ctx, nil)
options = append(options, remote.WithFilter("artifactType", dsseMediaType))
referrersIndex, err := remote.Referrers(referrersSubjectRef, options...)
if err != nil {
return nil, fmt.Errorf("failed to get referrers: %w", err)
}
referrersIndexManifest, err := referrersIndex.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to get index manifest: %w", err)
}
aManifests := make([]*Manifest, 0)
for i := range referrersIndexManifest.Manifests {
m := referrersIndexManifest.Manifests[i]
remoteRef := referrersSubjectRef.Context().Digest(m.Digest.String())
options = oci.WithOptions(ctx, nil)
attestationImage, err := remote.Image(remoteRef, options...)
if err != nil {
return nil, fmt.Errorf("failed to get referred image: %w", err)
}
layers, err := layersFromImage(attestationImage)
if err != nil {
return nil, fmt.Errorf("failed to get attestations from image: %w", err)
}
if len(layers) != 1 {
return nil, fmt.Errorf("expected exactly one layer, got %d", len(layers))
}
mt, err := layers[0].Layer.MediaType()
if err != nil {
return nil, fmt.Errorf("failed to get layer media type: %w", err)
}
if string(mt) != dsseMediaType {
return nil, fmt.Errorf("expected layer media type %s, got %s", dsseMediaType, mt)
}
attest := &Manifest{
SubjectName: imageName,
OriginalLayers: layers,
OriginalDescriptor: &m,
SubjectDescriptor: desc,
}
aManifests = append(aManifests, attest)
}
return aManifests, nil
}
func (r *ReferrersResolver) Attestations(ctx context.Context, predicateType string) ([]*EnvelopeReference, error) {
manifests, err := r.resolveAttestations(ctx, predicateType)
if err != nil {
return nil, fmt.Errorf("failed to resolve attestations: %w", err)
}
var envs []*EnvelopeReference
for _, attest := range manifests {
es, err := ExtractEnvelopes(attest, predicateType)
if err != nil {
return nil, fmt.Errorf("failed to extract envelopes: %w", err)
}
envs = append(envs, es...)
}
return envs, nil
}

View File

@@ -0,0 +1,337 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation_test
import (
"fmt"
"net/http/httptest"
"net/url"
"path/filepath"
"testing"
"github.com/docker/attest"
"github.com/docker/attest/attestation"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/mapping"
"github.com/docker/attest/oci"
"github.com/docker/attest/policy"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/registry"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
NoProvenanceImage = filepath.Join("..", "test", "testdata", "no-provenance-image")
PassPolicyDir = filepath.Join("..", "test", "testdata", "local-policy-pass")
LocalPolicy = filepath.Join("..", "test", "testdata", "local-policy")
LocalPolicyAttached = filepath.Join("..", "test", "testdata", "local-policy-attached")
PassNoTLPolicyDir = filepath.Join("..", "test", "testdata", "local-policy-no-tl")
FailPolicyDir = filepath.Join("..", "test", "testdata", "local-policy-fail")
TestTempDir = "attest-sign-test"
)
func TestAttestationReferenceTypes(t *testing.T) {
ctx, signer := test.Setup(t)
platforms := []string{"linux/amd64", "linux/arm64"}
for _, tc := range []struct {
name string
server *httptest.Server
referrersServer *httptest.Server
useDigest bool
referrersRepo string
attestationSource mapping.AttestationStyle
expectFailure bool
}{
{
name: "referrers support, defaults",
server: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(true)),
},
{
name: "use digest",
server: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(true)),
useDigest: true,
},
{
name: "attached attestations, referrers repo (mismatched args)",
server: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(true)),
expectFailure: true, // mismatched args
attestationSource: mapping.AttestationStyleAttached,
referrersRepo: "referrers",
},
{
name: "referrers attestations, referrers repo (no policy)",
server: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(true)),
expectFailure: true, // no policy
attestationSource: mapping.AttestationStyleReferrers,
referrersRepo: "referrers",
},
{
name: "referrers attestations",
server: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(true)),
attestationSource: mapping.AttestationStyleReferrers,
},
{
name: "referrers attestations, no referrers support on server",
server: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(false)),
attestationSource: mapping.AttestationStyleReferrers,
referrersServer: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(true)),
},
} {
t.Run(tc.name, func(t *testing.T) {
s := tc.server
defer s.Close()
if tc.referrersServer != nil {
defer tc.referrersServer.Close()
}
u, err := url.Parse(s.URL)
require.NoError(t, err)
opts := &attestation.SigningOptions{}
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
indexName := fmt.Sprintf("%s/repo:root", u.Host)
require.NoError(t, err)
outputRepo := indexName
if tc.referrersServer != nil {
ru, err := url.Parse(s.URL)
require.NoError(t, err)
tc.referrersRepo = fmt.Sprintf("%s/referrers", ru.Host)
outputRepo = tc.referrersRepo
}
// sign all the statements in the index
signedManifests, err := attest.SignStatements(ctx, attIdx.Index, signer, opts)
require.NoError(t, err)
// push subject image so that it can be resolved
require.NoError(t, err)
err = oci.PushIndexToRegistry(ctx, attIdx.Index, indexName)
require.NoError(t, err)
// upload referrers
output, err := oci.ParseImageSpec(outputRepo)
require.NoError(t, err)
for _, attIdx := range signedManifests {
images, err := attIdx.BuildReferringArtifacts()
require.NoError(t, err)
err = oci.SaveImagesNoTag(ctx, images, []*oci.ImageSpec{output})
require.NoError(t, err)
}
for _, platform := range platforms {
// can eval policy in the normal way
ref := indexName
if tc.useDigest {
options := oci.WithOptions(ctx, nil)
subjectRef, err := name.ParseReference(indexName)
require.NoError(t, err)
desc, err := remote.Index(subjectRef, options...)
require.NoError(t, err)
idxDigest, err := desc.Digest()
require.NoError(t, err)
ref = fmt.Sprintf("%s/repo@%s", u.Host, idxDigest.String())
}
policyOpts := &policy.Options{
LocalPolicyDir: LocalPolicy,
DisableTUF: true,
}
if tc.referrersRepo != "" {
policyOpts.ReferrersRepo = tc.referrersRepo
}
if tc.attestationSource != "" {
policyOpts.AttestationStyle = tc.attestationSource
}
src, err := oci.ParseImageSpec(ref, oci.WithPlatform(platform))
require.NoError(t, err)
results, err := attest.Verify(ctx, src, policyOpts)
if tc.expectFailure {
require.Error(t, err)
continue
}
require.NoError(t, err)
assert.Equal(t, attest.OutcomeSuccess, results.Outcome)
if tc.useDigest {
p, err := oci.ParsePlatform(platform)
require.NoError(t, err)
options := oci.WithOptions(ctx, p)
subjectRef, err := name.ParseReference(indexName)
require.NoError(t, err)
desc, err := remote.Image(subjectRef, options...)
require.NoError(t, err)
subjectDigest, err := desc.Digest()
require.NoError(t, err)
ref = fmt.Sprintf("%s/repo@%s", u.Host, subjectDigest.String())
}
src, err = oci.ParseImageSpec(ref, oci.WithPlatform(platform))
require.NoError(t, err)
results, err = attest.Verify(ctx, src, policyOpts)
require.NoError(t, err)
assert.Equal(t, attest.OutcomeSuccess, results.Outcome)
}
})
}
}
func TestReferencesInDifferentRepo(t *testing.T) {
ctx, signer := test.Setup(t)
repoName := "repo"
for _, tc := range []struct {
name string
server *httptest.Server
refServer *httptest.Server
}{
{
name: "referrers support",
server: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(true)),
refServer: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(true)),
},
{
name: "no referrers support",
server: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(false)),
refServer: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(true)),
},
} {
server := tc.server
defer server.Close()
serverURL, err := url.Parse(server.URL)
require.NoError(t, err)
refServer := tc.refServer
defer refServer.Close()
refServerURL, err := url.Parse(refServer.URL)
require.NoError(t, err)
opts := &attestation.SigningOptions{}
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
indexName := fmt.Sprintf("%s/%s:latest", serverURL.Host, repoName)
err = oci.PushIndexToRegistry(ctx, attIdx.Index, indexName)
require.NoError(t, err)
signedManifests, err := attest.SignStatements(ctx, attIdx.Index, signer, opts)
require.NoError(t, err)
// push signed attestation image to the ref server
for _, signedManifest := range signedManifests {
// push references using subject-digest.att convention
image, err := signedManifest.BuildImage()
require.NoError(t, err)
err = oci.PushImageToRegistry(ctx, image, fmt.Sprintf("%s/%s:tag-does-not-matter", refServerURL.Host, repoName))
require.NoError(t, err)
refServer := tc.refServer
defer refServer.Close()
refServerURL, err := url.Parse(refServer.URL)
require.NoError(t, err)
opts := &attestation.SigningOptions{}
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
indexName := fmt.Sprintf("%s/%s:latest", serverURL.Host, repoName)
err = oci.PushIndexToRegistry(ctx, attIdx.Index, indexName)
require.NoError(t, err)
signedManifests, err := attest.SignStatements(ctx, attIdx.Index, signer, opts)
require.NoError(t, err)
// push signed attestation image to the ref server
for _, mf := range signedManifests {
// push references using subject-digest.att convention
imgs, err := mf.BuildReferringArtifacts()
require.NoError(t, err)
for _, img := range imgs {
err = oci.PushImageToRegistry(ctx, img, fmt.Sprintf("%s/%s:tag-does-not-matter", refServerURL.Host, repoName))
require.NoError(t, err)
}
}
mfs2, err := attIdx.Index.IndexManifest()
require.NoError(t, err)
for _, mf := range mfs2.Manifests {
// skip signed/unsigned attestations
if mf.Annotations[attestation.DockerReferenceType] == attestation.AttestationManifestType {
continue
}
// can evaluate policy using referrers in a different repo
referencedImage := fmt.Sprintf("%s@%s", indexName, mf.Digest.String())
policyOpts := &policy.Options{
LocalPolicyDir: PassPolicyDir,
DisableTUF: true,
}
src, err := oci.ParseImageSpec(referencedImage)
require.NoError(t, err)
results, err := attest.Verify(ctx, src, policyOpts)
require.NoError(t, err)
assert.Equal(t, attest.OutcomeSuccess, results.Outcome)
}
}
}
}
func TestCorrectArtifactTypeInTagFallback(t *testing.T) {
ctx, signer := test.Setup(t)
regServer := test.NewLocalRegistry(ctx, registry.WithReferrersSupport(false))
defer regServer.Close()
serverURL, err := url.Parse(regServer.URL)
require.NoError(t, err)
repoName := "repo"
opts := &attestation.SigningOptions{}
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
indexName := fmt.Sprintf("%s/%s:latest", serverURL.Host, repoName)
err = oci.PushIndexToRegistry(ctx, attIdx.Index, indexName)
require.NoError(t, err)
signedManifests, err := attest.SignStatements(ctx, attIdx.Index, signer, opts)
require.NoError(t, err)
// this should create and maintain an index of referrers
for _, mf := range signedManifests {
imgs, err := mf.BuildReferringArtifacts()
require.NoError(t, err)
for _, img := range imgs {
err = oci.PushImageToRegistry(ctx, img, fmt.Sprintf("%s/%s:tag-does-not-matter", serverURL.Host, repoName))
require.NoError(t, err)
mf, err := img.Manifest()
require.NoError(t, err)
subject := mf.Subject
subjectRef, err := name.ParseReference(fmt.Sprintf("%s/%s:sha256-%s", serverURL.Host, repoName, subject.Digest.Hex))
require.NoError(t, err)
idx, err := remote.Index(subjectRef, oci.WithOptions(ctx, nil)...)
require.NoError(t, err)
imf, err := idx.IndexManifest()
require.NoError(t, err)
for _, m := range imf.Manifests {
assert.Contains(t, m.ArtifactType, "application/vnd.in-toto")
assert.Contains(t, m.ArtifactType, "+dsse")
}
}
}
}

120
attestation/registry.go Normal file
View File

@@ -0,0 +1,120 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation
import (
"context"
"fmt"
"github.com/docker/attest/oci"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
)
// ensure RegistryResolver implements Resolver.
var _ Resolver = &RegistryResolver{}
type RegistryResolver struct {
*oci.RegistryImageDetailsResolver
*Manifest
}
func NewRegistryResolver(src *oci.RegistryImageDetailsResolver) (*RegistryResolver, error) {
return &RegistryResolver{
RegistryImageDetailsResolver: src,
}, nil
}
func (r *RegistryResolver) Attestations(ctx context.Context, predicateType string) ([]*EnvelopeReference, error) {
if r.Manifest == nil {
attest, err := FetchManifest(ctx, r.Identifier, r.ImageSpec.Platform)
if err != nil {
return nil, err
}
r.Manifest = attest
}
return ExtractEnvelopes(r.Manifest, predicateType)
}
func attestationDigestForImage(ix *v1.IndexManifest, imageDigest string, attestType string) (string, error) {
for i := range ix.Manifests {
m := &ix.Manifests[i]
if v, ok := m.Annotations[DockerReferenceType]; ok && v == attestType {
if d, ok := m.Annotations[DockerReferenceDigest]; ok && d == imageDigest {
return m.Digest.String(), nil
}
}
}
return "", fmt.Errorf("no attestation found for image %s", imageDigest)
}
func FetchManifest(ctx context.Context, image string, platform *v1.Platform) (*Manifest, error) {
// we want to get to the image index, so ignoring platform for now
options := oci.WithOptions(ctx, nil)
ref, err := name.ParseReference(image)
if err != nil {
return nil, fmt.Errorf("failed to parse reference: %w", err)
}
index, err := remote.Index(ref, options...)
if err != nil {
return nil, fmt.Errorf("failed to get index: %w", err)
}
indexManifest, err := index.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to get index manifest: %w", err)
}
subjectDescriptor, err := oci.ImageDescriptor(indexManifest, platform)
if err != nil {
return nil, fmt.Errorf("failed to obtain image for platform: %w", err)
}
digest := subjectDescriptor.Digest.String()
ref, err = name.ParseReference(fmt.Sprintf("%s@%s", ref.Context().Name(), digest))
if err != nil {
return nil, fmt.Errorf("failed to parse attestation reference: %w", err)
}
attestationDigest, err := attestationDigestForImage(indexManifest, digest, "attestation-manifest")
if err != nil {
return nil, fmt.Errorf("failed to obtain attestation for image: %w", err)
}
ref, err = name.ParseReference(fmt.Sprintf("%s@%s", ref.Context().Name(), attestationDigest))
if err != nil {
return nil, fmt.Errorf("failed to parse attestation reference: %w", err)
}
remoteDescriptor, err := remote.Get(ref, options...)
if err != nil {
return nil, fmt.Errorf("failed to get attestation: %w", err)
}
attestationImage, err := remoteDescriptor.Image()
if err != nil {
return nil, fmt.Errorf("failed to get attestation image: %w", err)
}
layers, err := layersFromImage(attestationImage)
if err != nil {
return nil, fmt.Errorf("failed to get attestations from image: %w", err)
}
attest := &Manifest{
OriginalLayers: layers,
OriginalDescriptor: &remoteDescriptor.Descriptor,
SubjectName: image,
SubjectDescriptor: subjectDescriptor,
}
return attest, nil
}

View File

@@ -0,0 +1,75 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation_test
import (
"fmt"
"net/url"
"strings"
"testing"
"github.com/docker/attest"
"github.com/docker/attest/attestation"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/oci"
"github.com/docker/attest/policy"
"github.com/google/go-containerregistry/pkg/registry"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRegistry(t *testing.T) {
ctx, signer := test.Setup(t)
regServer := test.NewLocalRegistry(ctx, registry.WithReferrersSupport(false))
defer regServer.Close()
u, err := url.Parse(regServer.URL)
require.NoError(t, err)
opts := &attestation.SigningOptions{}
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
signedManifests, err := attest.SignStatements(ctx, attIdx.Index, signer, opts)
require.NoError(t, err)
signedIndex := attIdx.Index
signedIndex, err = attestation.UpdateIndexImages(signedIndex, signedManifests)
require.NoError(t, err)
indexName := fmt.Sprintf("%s/repo:root", u.Host)
require.NoError(t, err)
err = oci.PushIndexToRegistry(ctx, signedIndex, indexName)
require.NoError(t, err)
spec, err := oci.ParseImageSpec(indexName)
require.NoError(t, err)
resolver, err := policy.CreateImageDetailsResolver(spec)
require.NoError(t, err)
desc, err := resolver.ImageDescriptor(ctx)
require.NoError(t, err)
digest := desc.Digest.String()
assert.True(t, strings.Contains(digest, "sha256:"))
// resolver also works with platform specific digest
spec, err = oci.ParseImageSpec(fmt.Sprintf("%s@%s", indexName, digest))
require.NoError(t, err)
resolver, err = policy.CreateImageDetailsResolver(spec)
require.NoError(t, err)
desc, err = resolver.ImageDescriptor(ctx)
require.NoError(t, err)
assert.Equal(t, desc.Digest.String(), digest)
}

28
attestation/resolver.go Normal file
View File

@@ -0,0 +1,28 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation
import (
"context"
"github.com/docker/attest/oci"
)
type Resolver interface {
oci.ImageDetailsResolver
Attestations(ctx context.Context, mediaType string) ([]*EnvelopeReference, error)
}

87
attestation/sign.go Normal file
View File

@@ -0,0 +1,87 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation
import (
"context"
"fmt"
"github.com/docker/attest/internal/util"
"github.com/docker/attest/tlog"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
// SignDSSE signs a payload with a given signer and uploads the signature to the transparency log.
func SignDSSE(ctx context.Context, payload []byte, signer dsse.SignerVerifier, opts *SigningOptions) (*Envelope, error) {
payloadType := intoto.PayloadType
env := new(Envelope)
env.Payload = base64Encoding.EncodeToString(payload)
env.PayloadType = payloadType
encPayload := dsse.PAE(payloadType, payload)
// statement message digest
hash := util.SHA256(encPayload)
// sign message digest
sig, err := signer.Sign(ctx, hash)
if err != nil {
return nil, fmt.Errorf("error signing attestation: %w", err)
}
// get Key ID from signer
keyID, err := signer.KeyID()
if err != nil {
return nil, fmt.Errorf("error getting public key ID: %w", err)
}
dsseSig := &Signature{
KeyID: keyID,
Sig: base64Encoding.EncodeToString(sig),
}
if opts.TransparencyLog != nil {
ext, err := logSignature(ctx, opts.TransparencyLog, sig, encPayload, signer)
if err != nil {
return nil, fmt.Errorf("failed to log signature: %w", err)
}
dsseSig.Extension = ext
}
// add signature to dsse envelope
env.Signatures = []*Signature{dsseSig}
return env, nil
}
// returns a new envelope with the transparency log entry added to the signature extension.
func logSignature(ctx context.Context, t tlog.TransparencyLog, sig []byte, encPayload []byte, signer dsse.SignerVerifier) (*Extension, error) {
// get Key ID from signer
keyID, err := signer.KeyID()
if err != nil {
return nil, fmt.Errorf("error getting public key ID: %w", err)
}
entry, err := t.UploadEntry(ctx, keyID, encPayload, sig, signer)
if err != nil {
return nil, fmt.Errorf("error uploading TL entry: %w", err)
}
return &Extension{
Kind: DockerDSSEExtKind,
Ext: &DockerDSSEExtension{
TL: entry,
},
}, nil
}

329
attestation/sign_test.go Normal file
View File

@@ -0,0 +1,329 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation_test
import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/json"
"fmt"
"net/url"
"testing"
"time"
"github.com/docker/attest/attestation"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/oci"
"github.com/docker/attest/signerverifier"
"github.com/docker/attest/tlog"
"github.com/google/go-containerregistry/pkg/registry"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/static"
"github.com/google/go-containerregistry/pkg/v1/types"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSignVerifyAttestation(t *testing.T) {
ctx, signer := test.Setup(t)
stmt := &intoto.Statement{
StatementHeader: intoto.StatementHeader{
Type: intoto.StatementInTotoV01,
PredicateType: intoto.PredicateSPDX,
},
Predicate: "test",
}
payload, err := json.Marshal(stmt)
require.NoError(t, err)
tl := tlog.GetMockTL()
opts := &attestation.SigningOptions{
TransparencyLog: tl,
}
env, err := attestation.SignDSSE(ctx, payload, signer, opts)
require.NoError(t, err)
// marshal envelope to json to test for bugs when marshaling envelope data
serializedEnv, err := json.Marshal(env)
require.NoError(t, err)
deserializedEnv := new(attestation.Envelope)
err = json.Unmarshal(serializedEnv, deserializedEnv)
require.NoError(t, err)
// signer.Public() calls AWS API when using AWS signer, use attestation.GetPublicVerificationKey() to get key from TUF repo
// signer.Public() used here for test purposes
ecPub, ok := signer.Public().(*ecdsa.PublicKey)
assert.True(t, ok)
pem, err := signerverifier.ConvertToPEM(ecPub)
assert.NoError(t, err)
keyID, err := signerverifier.KeyID(ecPub)
assert.NoError(t, err)
badKeyPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
badKey := &badKeyPriv.PublicKey
badPEM, err := signerverifier.ConvertToPEM(badKey)
require.NoError(t, err)
testCases := []struct {
name string
keyID string
pem []byte
distrust bool
from time.Time
to *time.Time
status string
expectedError string
}{
{
name: "all OK",
keyID: keyID,
pem: pem,
distrust: false,
from: time.Time{},
to: nil,
status: "active",
expectedError: "",
},
{
name: "key not found",
keyID: "someotherkey",
pem: pem,
distrust: false,
from: time.Time{},
to: nil,
status: "active",
expectedError: fmt.Sprintf("key not found: %s", keyID),
},
{
name: "key distrusted",
keyID: keyID,
pem: pem,
distrust: true,
from: time.Time{},
to: nil,
status: "active",
expectedError: "distrusted",
},
{
name: "key not yet valid",
keyID: keyID,
pem: pem,
distrust: false,
from: time.Now().Add(time.Hour),
to: nil,
status: "active",
expectedError: "not yet valid",
},
{
name: "key already revoked",
keyID: keyID,
pem: pem,
distrust: false,
from: time.Time{},
to: new(time.Time),
status: "revoked",
expectedError: "already revoked",
},
{
name: "bad key",
keyID: keyID,
pem: badPEM,
distrust: false,
from: time.Time{},
to: nil,
status: "active",
expectedError: "signature is not valid",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
keyMeta := &attestation.KeyMetadata{
ID: tc.keyID,
PEM: string(tc.pem),
Distrust: tc.distrust,
From: tc.from,
To: tc.to,
Status: tc.status,
}
opts := &attestation.VerifyOptions{
Keys: attestation.Keys{keyMeta},
}
getTL := func(_ context.Context, opts *attestation.VerifyOptions) (tlog.TransparencyLog, error) {
if opts.SkipTL {
return nil, nil
}
return tl, nil
}
verifier, err := attestation.NewVerfier(attestation.WithLogVerifierFactory(getTL))
require.NoError(t, err)
_, err = attestation.VerifyDSSE(ctx, verifier, deserializedEnv, opts)
if tc.expectedError != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tc.expectedError)
} else {
assert.NoError(t, err)
}
})
}
}
func TestAddSignedLayerAnnotations(t *testing.T) {
ctx, signer := test.Setup(t)
testCases := []struct {
name string
replace bool
}{
{"replaced", true},
{"not replaced", false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
data := []byte("signed")
testLayer := static.NewLayer(data, types.MediaType(intoto.PayloadType))
mediaType := types.OCIManifestSchema1
opts := &attestation.SigningOptions{}
originalLayer := &attestation.Layer{
Layer: testLayer,
Statement: &intoto.Statement{
StatementHeader: intoto.StatementHeader{
PredicateType: attestation.VSAPredicateType,
},
},
Annotations: map[string]string{"test": "test"},
}
manifest := &attestation.Manifest{
OriginalDescriptor: &v1.Descriptor{
MediaType: mediaType,
},
OriginalLayers: []*attestation.Layer{
originalLayer,
},
SubjectDescriptor: &v1.Descriptor{},
}
err := manifest.Add(ctx, signer, originalLayer.Statement, opts)
require.NoError(t, err)
newImg, err := manifest.BuildImage(attestation.WithReplacedLayers(tc.replace))
require.NoError(t, err)
mf, _ := newImg.RawManifest()
type Annotations struct {
Annotations map[string]string `json:"annotations"`
}
type Layers struct {
Layers []Annotations `json:"layers"`
}
l := &Layers{}
err = json.Unmarshal(mf, l)
require.NoError(t, err)
_, ok := l.Layers[0].Annotations["test"]
assert.Truef(t, ok, "missing annotations")
})
}
}
func TestSimpleStatementSigning(t *testing.T) {
ctx, signer := test.Setup(t)
empty := types.MediaType("application/vnd.oci.empty.v1+json")
testCases := []struct {
name string
replace bool
}{
{"replaced", true},
{"not replaced", false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
opts := &attestation.SigningOptions{}
statement := &intoto.Statement{
StatementHeader: intoto.StatementHeader{
PredicateType: attestation.VSAPredicateType,
},
}
statement2 := &intoto.Statement{
StatementHeader: intoto.StatementHeader{
PredicateType: attestation.VSAPredicateType,
},
}
digest, err := v1.NewHash(test.UnsignedLinuxAMD64ImageDigest)
require.NoError(t, err)
subject := &v1.Descriptor{
MediaType: "application/vnd.oci.image.manifest.v1+json",
Digest: digest,
}
manifest, err := attestation.NewManifest(subject)
require.NoError(t, err)
err = manifest.Add(ctx, signer, statement, opts)
require.NoError(t, err)
err = manifest.Add(ctx, signer, statement2, opts)
require.NoError(t, err)
// fake that the manfifest was loaded from a real image
manifest.OriginalLayers = manifest.SignedLayers
envelopes, err := attestation.ExtractEnvelopes(manifest, attestation.VSAPredicateType)
require.NoError(t, err)
assert.Len(t, envelopes, 2)
newImg, err := manifest.BuildImage(attestation.WithReplacedLayers(tc.replace))
require.NoError(t, err)
layers, err := newImg.Layers()
require.NoError(t, err)
if tc.replace {
assert.Len(t, layers, 2)
} else {
assert.Len(t, layers, 4)
}
newImgs, err := manifest.BuildReferringArtifacts()
require.NoError(t, err)
assert.Len(t, newImgs, 2)
for _, img := range newImgs {
mf, err := img.Manifest()
require.NoError(t, err)
assert.Contains(t, mf.ArtifactType, "application/vnd.in-toto")
assert.Contains(t, mf.ArtifactType, "+dsse")
assert.Equal(t, subject.MediaType, mf.MediaType)
assert.Equal(t, empty, mf.Config.MediaType)
assert.Equal(t, int64(2), mf.Config.Size)
assert.Equal(t, "{}", string(mf.Config.Data))
layers, err := img.Layers()
require.NoError(t, err)
assert.Len(t, layers, 1)
}
regServer := test.NewLocalRegistry(ctx, registry.WithReferrersSupport(true))
defer regServer.Close()
u, err := url.Parse(regServer.URL)
require.NoError(t, err)
indexName := fmt.Sprintf("%s/repo:root", u.Host)
output, err := oci.ParseImageSpecs(indexName)
require.NoError(t, err)
artifacts, err := manifest.BuildReferringArtifacts()
require.NoError(t, err)
err = oci.SaveImagesNoTag(ctx, artifacts, output)
require.NoError(t, err)
})
}
}

163
attestation/types.go Normal file
View File

@@ -0,0 +1,163 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation
import (
"crypto"
"encoding/base64"
"fmt"
"time"
"github.com/docker/attest/tlog"
v1 "github.com/google/go-containerregistry/pkg/v1"
intoto "github.com/in-toto/in-toto-golang/in_toto"
v02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
slsav1 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
)
const (
DockerReferenceType = "vnd.docker.reference.type"
AttestationManifestType = "attestation-manifest"
InTotoPredicateType = "in-toto.io/predicate-type"
DockerReferenceDigest = "vnd.docker.reference.digest"
DockerDSSEExtKind = "application/vnd.docker.attestation-verification.v1+json"
OCIDescriptorDSSEMediaType = ociv1.MediaTypeDescriptor + "+dsse"
InTotoReferenceLifecycleStage = "vnd.docker.lifecycle-stage"
LifecycleStageExperimental = "experimental"
)
var base64Encoding = base64.StdEncoding.Strict()
type Layer struct {
Statement *intoto.Statement
Layer v1.Layer
Annotations map[string]string
}
type Manifest struct {
OriginalDescriptor *v1.Descriptor
OriginalLayers []*Layer
// accumulated during signing
SignedLayers []*Layer
// details of subject image
SubjectName string
SubjectDescriptor *v1.Descriptor
}
type ManifestImageOptions struct {
// how to output the image
skipSubject bool
replaceLayers bool
laxReferrers bool
}
// the following types are needed until https://github.com/secure-systems-lab/dsse/pull/61 is merged.
type Envelope struct {
PayloadType string `json:"payloadType"`
Payload string `json:"payload"`
Signatures []*Signature `json:"signatures"`
}
type Signature struct {
KeyID string `json:"keyid"`
Sig string `json:"sig"`
Extension *Extension `json:"extension,omitempty"`
}
type Extension struct {
Kind string `json:"kind"`
Ext *DockerDSSEExtension `json:"ext"`
}
type EnvelopeReference struct {
*Envelope
ResourceDescriptor *ResourceDescriptor `json:"resourceDescriptor"`
}
type ResourceDescriptor struct {
MediaType string `json:"mediaType"`
Digest map[string]string `json:"digest"`
URI string `json:"uri,omitempty"`
}
type AnnotatedStatement struct {
OCIDescriptor *v1.Descriptor
InTotoStatement *intoto.Statement
Annotations map[string]string
}
type DockerDSSEExtension struct {
TL *tlog.DockerTLExtension `json:"tl"`
}
type TransparencyLogKind string
const (
RekorTransparencyLogKind = "rekor"
)
type VerifyOptions struct {
Keys []*KeyMetadata `json:"keys"`
SkipTL bool `json:"skip_tl"`
TransparencyLog TransparencyLogKind `json:"tl"`
}
type KeyMetadata struct {
ID string `json:"id"`
PEM string `json:"key"`
From time.Time `json:"from"`
To *time.Time `json:"to"`
Status string `json:"status"`
SigningFormat string `json:"signing-format"`
Distrust bool `json:"distrust,omitempty"`
publicKey crypto.PublicKey
}
type (
Keys []*KeyMetadata
KeysMap map[string]*KeyMetadata
)
type SigningOptions struct {
// set this in order to log to a transparency log
TransparencyLog tlog.TransparencyLog
}
type Options struct {
NoReferrers bool
Attach bool
ReferrersRepo string
}
func DSSEMediaType(predicateType string) (string, error) {
var predicateName string
switch predicateType {
case slsav1.PredicateSLSAProvenance:
predicateName = "provenance"
case v02.PredicateSLSAProvenance:
predicateName = "provenance"
case intoto.PredicateSPDX:
predicateName = "spdx"
case VSAPredicateType:
predicateName = "verification_summary"
default:
return "", fmt.Errorf("unknown predicate type %q", predicateType)
}
return fmt.Sprintf("application/vnd.in-toto.%s+dsse", predicateName), nil
}

60
attestation/types_test.go Normal file
View File

@@ -0,0 +1,60 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation
import (
"fmt"
"testing"
intoto "github.com/in-toto/in-toto-golang/in_toto"
v02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
slsav1 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDSSEMediaType(t *testing.T) {
testcases := []struct {
name string
expected string
}{
{
name: slsav1.PredicateSLSAProvenance,
expected: "provenance",
},
{
name: v02.PredicateSLSAProvenance,
expected: "provenance",
},
{
name: intoto.PredicateSPDX,
expected: "spdx",
},
{
name: VSAPredicateType,
expected: "verification_summary",
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
mt, err := DSSEMediaType(tc.name)
require.NoError(t, err)
assert.Equal(t, fmt.Sprintf("application/vnd.in-toto.%s+dsse", tc.expected), mt)
})
}
}

159
attestation/verifier.go Normal file
View File

@@ -0,0 +1,159 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation
import (
"context"
"crypto"
"crypto/x509"
"fmt"
"github.com/docker/attest/signerverifier"
"github.com/docker/attest/tlog"
"github.com/docker/attest/tuf"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
func WithTUFDownloader(tufDownloader tuf.Downloader) func(*verifier) {
return func(r *verifier) {
r.tufDownloader = tufDownloader
}
}
type SignatureVerifierFactory func(ctx context.Context, publicKey crypto.PublicKey, opts *VerifyOptions) (dsse.Verifier, error)
func WithSignatureVerifierFactory(factory SignatureVerifierFactory) func(*verifier) {
return func(r *verifier) {
r.signatureVerifierFactory = factory
}
}
func WithLogVerifierFactory(factory LogVerifierFactory) func(*verifier) {
return func(r *verifier) {
r.logVerifierFactory = factory
}
}
type LogVerifierFactory func(ctx context.Context, opts *VerifyOptions) (tlog.TransparencyLog, error)
func NewVerfier(options ...func(*verifier)) (Verifier, error) {
verifier := &verifier{}
for _, opt := range options {
opt(verifier)
}
return verifier, nil
}
type Verifier interface {
GetSignatureVerifier(ctx context.Context, publicKey crypto.PublicKey, opts *VerifyOptions) (dsse.Verifier, error)
GetLogVerifier(ctx context.Context, opts *VerifyOptions) (tlog.TransparencyLog, error)
VerifySignature(ctx context.Context, publicKey crypto.PublicKey, data []byte, signature []byte, opts *VerifyOptions) error
VerifyLog(ctx context.Context, keyMeta *KeyMetadata, data []byte, sig *Signature, opts *VerifyOptions) error
}
// ensure it has all the necessary methods.
var _ Verifier = (*verifier)(nil)
type verifier struct {
tufDownloader tuf.Downloader
signatureVerifierFactory SignatureVerifierFactory
logVerifierFactory LogVerifierFactory
}
// GetLogVerifier implements Verifier.
func (v *verifier) GetLogVerifier(ctx context.Context, opts *VerifyOptions) (tlog.TransparencyLog, error) {
if v.logVerifierFactory != nil {
return v.logVerifierFactory(ctx, opts)
}
if opts.SkipTL {
return nil, nil
}
// TODO support other transparency logs
var transparencyLog tlog.TransparencyLog
switch opts.TransparencyLog {
case "", RekorTransparencyLogKind:
var err error
transparencyLog, err = tlog.NewRekorLog(tlog.WithTUFDownloader(v.tufDownloader))
if err != nil {
return nil, fmt.Errorf("error failed to create rekor verifier: %w", err)
}
default:
return nil, fmt.Errorf("unsupported transparency log: %s", opts.TransparencyLog)
}
return transparencyLog, nil
}
// GetSignatureVerifier implements Verifier.
func (v *verifier) GetSignatureVerifier(ctx context.Context, publicKey crypto.PublicKey, opts *VerifyOptions) (dsse.Verifier, error) {
if v.signatureVerifierFactory != nil {
return v.signatureVerifierFactory(ctx, publicKey, opts)
}
// TODO: use details from opts to decide which algorithm to use here
ecdsaVerifier, err := signerverifier.NewECDSAVerifier(publicKey)
if err != nil {
return nil, fmt.Errorf("error failed to create ecdsa verifier: %w", err)
}
return ecdsaVerifier, nil
}
func (v *verifier) VerifySignature(ctx context.Context, publicKey crypto.PublicKey, data []byte, signature []byte, opts *VerifyOptions) error {
sigVerifier, err := v.GetSignatureVerifier(ctx, publicKey, opts)
if err != nil {
return fmt.Errorf("error failed to get verifier: %w", err)
}
return sigVerifier.Verify(ctx, data, signature)
}
func (v *verifier) VerifyLog(ctx context.Context, keyMeta *KeyMetadata, encPayload []byte, sig *Signature, opts *VerifyOptions) error {
if opts.SkipTL {
return nil
}
if sig.Extension == nil || sig.Extension.Kind == "" {
return fmt.Errorf("error missing signature extension")
}
if sig.Extension.Kind != DockerDSSEExtKind {
return fmt.Errorf("error unsupported signature extension kind: %s", sig.Extension.Kind)
}
transparencyLog, err := v.GetLogVerifier(ctx, opts)
if err != nil {
return fmt.Errorf("error failed to get transparency log verifier: %w", err)
}
if transparencyLog == nil {
return fmt.Errorf("error missing transparency log verifier")
}
// verify TL entry payload
publicKey, err := keyMeta.ParsedKey()
if err != nil {
return fmt.Errorf("error failed to parse public key: %w", err)
}
encodedPub, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return fmt.Errorf("error failed to marshal public key: %w", err)
}
integratedTime, err := transparencyLog.VerifyEntry(ctx, sig.Extension.Ext.TL, encPayload, encodedPub)
if err != nil {
return fmt.Errorf("TL entry failed verification: %w", err)
}
if integratedTime.Before(keyMeta.From) {
return fmt.Errorf("key %s was not yet valid at TL log time %s (key valid from %s)", keyMeta.ID, integratedTime, keyMeta.From)
}
if keyMeta.To != nil && !integratedTime.Before(*keyMeta.To) {
return fmt.Errorf("key %s was already %s at TL log time %s (key %s at %s)", keyMeta.ID, keyMeta.Status, integratedTime, keyMeta.Status, *keyMeta.To)
}
return nil
}

View File

@@ -0,0 +1,71 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation
import (
"context"
"reflect"
"testing"
"github.com/docker/attest/tlog"
"github.com/docker/attest/tuf"
"github.com/stretchr/testify/require"
)
func Test_verifier_GetLogVerifier(t *testing.T) {
type fields struct {
tufDownloader tuf.Downloader
signatureVerifierFactory SignatureVerifierFactory
logVerifierFactory LogVerifierFactory
}
type args struct {
ctx context.Context
opts *VerifyOptions
}
rekor, err := tlog.NewRekorLog()
require.NoError(t, err)
tests := []struct {
name string
fields fields
args args
want tlog.TransparencyLog
wantErr bool
}{
{name: "skip_tl true", fields: fields{}, args: args{ctx: context.Background(), opts: &VerifyOptions{SkipTL: true}}},
{name: "skip_tl false", fields: fields{}, args: args{ctx: context.Background(), opts: &VerifyOptions{SkipTL: false}}, want: rekor},
{name: "tl: rekor", fields: fields{logVerifierFactory: func(_ context.Context, _ *VerifyOptions) (tlog.TransparencyLog, error) {
return &tlog.Rekor{}, nil
}}, args: args{ctx: context.Background(), opts: &VerifyOptions{}}, want: &tlog.Rekor{}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := &verifier{
tufDownloader: tt.fields.tufDownloader,
signatureVerifierFactory: tt.fields.signatureVerifierFactory,
logVerifierFactory: tt.fields.logVerifierFactory,
}
got, err := v.GetLogVerifier(tt.args.ctx, tt.args.opts)
if (err != nil) != tt.wantErr {
t.Errorf("verifier.GetLogVerifier() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("verifier.GetLogVerifier() = %v, want %v", got, tt.want)
}
})
}
}

99
attestation/verify.go Normal file
View File

@@ -0,0 +1,99 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation
import (
"context"
"crypto"
"encoding/base64"
"fmt"
"github.com/docker/attest/signerverifier"
intoto "github.com/in-toto/in-toto-golang/in_toto"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
func VerifyDSSE(ctx context.Context, verifier Verifier, env *Envelope, opts *VerifyOptions) ([]byte, error) {
// enforce payload type
if !ValidPayloadType(env.PayloadType) {
return nil, fmt.Errorf("unsupported payload type %s", env.PayloadType)
}
if len(env.Signatures) == 0 {
return nil, fmt.Errorf("no signatures found")
}
keys := make(map[string]*KeyMetadata, len(opts.Keys))
for _, key := range opts.Keys {
keys[key.ID] = key
}
payload, err := base64Encoding.DecodeString(env.Payload)
if err != nil {
return nil, fmt.Errorf("error failed to decode payload: %w", err)
}
encPayload := dsse.PAE(env.PayloadType, payload)
// verify signatures and transparency log entry
for _, sig := range env.Signatures {
// resolve public key used to sign
keyMeta, ok := keys[sig.KeyID]
if !ok {
return nil, fmt.Errorf("error key not found: %s", sig.KeyID)
}
if keyMeta.Distrust {
return nil, fmt.Errorf("key %s is distrusted", keyMeta.ID)
}
publicKey, err := keyMeta.ParsedKey()
if err != nil {
return nil, fmt.Errorf("failed to parse public key: %w", err)
}
// decode signature
signature, err := base64.StdEncoding.Strict().DecodeString(sig.Sig)
if err != nil {
return nil, fmt.Errorf("error failed to decode signature: %w", err)
}
err = verifier.VerifySignature(ctx, publicKey, encPayload, signature, opts)
if err != nil {
return nil, fmt.Errorf("error failed to verify signature: %w", err)
}
if err := verifier.VerifyLog(ctx, keyMeta, encPayload, sig, opts); err != nil {
return nil, fmt.Errorf("error failed to verify transparency log entry: %w", err)
}
}
return payload, nil
}
func ValidPayloadType(payloadType string) bool {
return payloadType == intoto.PayloadType || payloadType == ociv1.MediaTypeDescriptor
}
func (km *KeyMetadata) ParsedKey() (crypto.PublicKey, error) {
if km.publicKey != nil {
return km.publicKey, nil
}
publicKey, err := signerverifier.ParsePublicKey([]byte(km.PEM))
if err != nil {
return nil, fmt.Errorf("failed to parse public key: %w", err)
}
km.publicKey = publicKey
return publicKey, nil
}

View File

@@ -1,11 +1,27 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation_test
import (
"encoding/base64"
"testing"
"github.com/docker/attest/attestation"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/pkg/attestation"
intoto "github.com/in-toto/in-toto-golang/in_toto"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/stretchr/testify/assert"
@@ -35,12 +51,14 @@ func TestVerifyUnsignedAttestation(t *testing.T) {
payload := []byte("payload")
env := &attestation.Envelope{
// no signatures
Signatures: []attestation.Signature{},
Signatures: []*attestation.Signature{},
Payload: base64.StdEncoding.EncodeToString(payload),
PayloadType: intoto.PayloadType,
}
_, err := attestation.VerifyDSSE(ctx, env, attestation.KeysMap{})
opts := &attestation.VerifyOptions{
Keys: attestation.Keys{},
}
_, err := attestation.VerifyDSSE(ctx, nil, env, opts)
assert.Error(t, err)
assert.Contains(t, err.Error(), "no signatures")
}

79
attestation/vsa.go Normal file
View File

@@ -0,0 +1,79 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attestation
import (
"fmt"
"github.com/docker/attest/version"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/package-url/packageurl-go"
)
const (
VSAPredicateType = "https://slsa.dev/verification_summary/v1"
)
type VSAPredicate struct {
Verifier VSAVerifier `json:"verifier"`
TimeVerified string `json:"timeVerified"`
ResourceURI string `json:"resourceUri"`
Policy VSAPolicy `json:"policy"`
InputAttestations []ResourceDescriptor `json:"inputAttestations,omitempty"`
VerificationResult string `json:"verificationResult"`
VerifiedLevels []string `json:"verifiedLevels"`
}
type VSAVerifier struct {
ID string `json:"id"`
Version VerifierVersion `json:"version"`
}
type VerifierVersion map[string]string
type VSAPolicy struct {
URI string `json:"uri,omitempty"`
Digest map[string]string `json:"digest"`
DownloadLocation string `json:"downloadLocation,omitempty"`
}
func ToVSAResourceURI(sub intoto.Subject) (string, error) {
// parse purl
purl, err := packageurl.FromString(sub.Name)
if err != nil {
return "", fmt.Errorf("failed to parse package url: %w", err)
}
quals := purl.Qualifiers.Map()
if quals["digest"] == "" {
quals["digest"] = "sha256:" + sub.Digest["sha256"]
}
purl.Qualifiers = packageurl.QualifiersFromMap(quals)
return purl.String(), nil
}
func GetVerifierVersion(fetcher version.Fetcher) (VerifierVersion, error) {
attestVersion, err := fetcher.Get()
if err != nil {
return nil, fmt.Errorf("failed to get attest version: %w", err)
}
if attestVersion == nil {
return nil, nil
}
return VerifierVersion{
version.ThisModulePath: attestVersion.String(),
}, nil
}

5
codecov.yml Normal file
View File

@@ -0,0 +1,5 @@
ignore:
- "internal/test"
coverage:
status:
patch: false

99
example_sign_test.go Normal file
View File

@@ -0,0 +1,99 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attest_test
import (
"context"
"github.com/docker/attest"
"github.com/docker/attest/attestation"
"github.com/docker/attest/oci"
"github.com/docker/attest/signerverifier"
"github.com/docker/attest/tlog"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/mutate"
)
func ExampleSignStatements_remote() {
// configure signerverifier
// local signer (unsafe for production)
signer, err := signerverifier.GenKeyPair()
if err != nil {
panic(err)
}
// example using AWS KMS signer
// aws_arn := "arn:aws:kms:us-west-2:123456789012:key/12345678-1234-1234-1234-123456789012"
// aws_region := "us-west-2"
// signer, err := signerverifier.GetAWSSigner(cmd.Context(), aws_arn, aws_region)
// configure signing options
// use rekor transparency log wit static rekor public key (see options to use dynamic rekor public key)
rekor, err := tlog.NewRekorLog()
if err != nil {
panic(err)
}
opts := &attestation.SigningOptions{
TransparencyLog: rekor, // unset this to disable signature transparency logging
}
// load image index with unsigned attestation-manifests
ref := "docker/image-signer-verifier:latest"
attIdx, err := oci.IndexFromRemote(context.Background(), ref)
if err != nil {
panic(err)
}
// example for local image index
// path := "/myimage"
// attIdx, err = oci.IndexFromPath(path)
// if err != nil {
// panic(err)
// }
// sign all attestations in an image index
signedManifests, err := attest.SignStatements(context.Background(), attIdx.Index, signer, opts)
if err != nil {
panic(err)
}
signedIndex := attIdx.Index
signedIndex, err = attestation.UpdateIndexImages(signedIndex, signedManifests)
if err != nil {
panic(err)
}
// push image index with signed attestation-manifests
err = oci.PushIndexToRegistry(context.Background(), signedIndex, ref)
if err != nil {
panic(err)
}
// output image index to filesystem (optional)
path := "/myimage"
idx := v1.ImageIndex(empty.Index)
idx = mutate.AppendManifests(idx, mutate.IndexAddendum{
Add: signedIndex,
Descriptor: v1.Descriptor{
Annotations: map[string]string{
oci.OCIReferenceTarget: attIdx.Name,
},
},
})
err = oci.SaveIndexAsOCILayout(idx, path)
if err != nil {
panic(err)
}
}

70
example_verify_test.go Normal file
View File

@@ -0,0 +1,70 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package attest_test
import (
"context"
"fmt"
"os"
"path/filepath"
"github.com/docker/attest"
"github.com/docker/attest/oci"
"github.com/docker/attest/policy"
"github.com/docker/attest/tuf"
)
func ExampleVerify_remote() {
// create a tuf client
home, err := os.UserHomeDir()
if err != nil {
panic(err)
}
tufOutputPath := filepath.Join(home, ".docker", "tuf")
tufClientOpts := tuf.NewDockerDefaultClientOptions(tufOutputPath)
// create a resolver for remote attestations
image := "registry-1.docker.io/library/notary:server"
platform := "linux/amd64"
// configure policy options
opts := &policy.Options{
TUFClientOptions: tufClientOpts,
LocalTargetsDir: filepath.Join(home, ".docker", "policy"), // location to store policy files downloaded from TUF
LocalPolicyDir: "", // overrides TUF policy for local policy files if set
PolicyID: "", // set to ignore policy mapping and select a policy by id
DisableTUF: false, // set to disable TUF and rely on local policy files
}
src, err := oci.ParseImageSpec(image, oci.WithPlatform(platform))
if err != nil {
panic(err)
}
// verify attestations
result, err := attest.Verify(context.Background(), src, opts)
if err != nil {
panic(err)
}
switch result.Outcome {
case attest.OutcomeSuccess:
fmt.Println("policy passed")
case attest.OutcomeNoPolicy:
fmt.Println("no policy for image")
case attest.OutcomeFailure:
fmt.Println("policy failed")
}
}

211
go.mod
View File

@@ -1,80 +1,95 @@
module github.com/docker/attest
go 1.22.1
go 1.22.8
require (
github.com/aws/aws-sdk-go-v2/config v1.27.11
github.com/containerd/containerd v1.7.14
github.com/distribution/reference v0.5.0
github.com/Masterminds/semver/v3 v3.3.0
github.com/aws/aws-sdk-go-v2/config v1.28.5
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8
github.com/containerd/containerd/v2 v2.0.0
github.com/containerd/platforms v1.0.0-rc.0
github.com/distribution/reference v0.6.0
github.com/docker-library/bashbrew v0.1.12
github.com/go-git/go-git/v5 v5.12.0
github.com/go-openapi/runtime v0.28.0
github.com/go-openapi/strfmt v0.23.0
github.com/google/go-containerregistry v0.19.1
github.com/hashicorp/go-cleanhttp v0.5.2
github.com/google/go-containerregistry v0.20.2
github.com/in-toto/in-toto-golang v0.9.0
github.com/open-policy-agent/opa v0.63.0
github.com/open-policy-agent/opa v0.70.0
github.com/opencontainers/image-spec v1.1.0
github.com/package-url/packageurl-go v0.1.2
github.com/pkg/errors v0.9.1
github.com/package-url/packageurl-go v0.1.3
github.com/secure-systems-lab/go-securesystemslib v0.8.0
github.com/sigstore/cosign/v2 v2.2.4
github.com/sigstore/cosign/v2 v2.4.1
github.com/sigstore/rekor v1.3.6
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.3
github.com/sigstore/sigstore v1.8.10
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.10
github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.10
github.com/stretchr/testify v1.9.0
github.com/testcontainers/testcontainers-go v0.30.0
github.com/testcontainers/testcontainers-go/modules/registry v0.30.0
github.com/theupdateframework/go-tuf/v2 v2.0.0-20240402164131-b2e024ad4752
gopkg.in/yaml.v3 v3.0.1
github.com/theupdateframework/go-tuf/v2 v2.0.2
google.golang.org/api v0.205.0
sigs.k8s.io/yaml v1.4.0
)
replace github.com/theupdateframework/go-tuf/v2 => github.com/mrjoelkamp/go-tuf/v2 v2.0.1 // for https://github.com/theupdateframework/go-tuf/pull/632
// fork with changes to support ArtifactType (https://github.com/google/go-containerregistry/pull/1931)
replace github.com/google/go-containerregistry => github.com/docker/go-containerregistry v0.0.0-20240808132857-c8bfc44af7c8
require (
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.11.4 // indirect
cloud.google.com/go v0.116.0 // indirect
cloud.google.com/go/auth v0.10.1 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.5 // indirect
cloud.google.com/go/compute/metadata v0.5.2 // indirect
cloud.google.com/go/iam v1.2.1 // indirect
cloud.google.com/go/kms v1.20.0 // indirect
cloud.google.com/go/longrunning v0.6.1 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.12.9 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/agnivade/levenshtein v1.2.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2 v1.26.1 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.11 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect
github.com/aws/aws-sdk-go-v2/service/kms v1.30.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect
github.com/aws/smithy-go v1.20.2 // indirect
github.com/aws/aws-sdk-go-v2 v1.32.5 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.46 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ecr v1.29.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.24.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 // indirect
github.com/aws/aws-sdk-go-v2/service/kms v1.37.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 // indirect
github.com/aws/smithy-go v1.22.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.3.8 // indirect
github.com/containerd/containerd v1.7.23 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect
github.com/docker/cli v24.0.7+incompatible // indirect
github.com/docker/cli v27.1.1+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v25.0.5+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/docker/docker-credential-helpers v0.8.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/analysis v0.23.0 // indirect
github.com/go-openapi/errors v0.22.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
@@ -84,91 +99,101 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/validate v0.24.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/certificate-transparency-go v1.1.8 // indirect
github.com/google/certificate-transparency-go v1.2.1 // indirect
github.com/google/s2a-go v0.1.8 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect
github.com/jellydator/ttlcache/v3 v3.2.0 // indirect
github.com/jellydator/ttlcache/v3 v3.3.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/letsencrypt/boulder v0.0.0-20231026200631-000cd05d5491 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/user v0.1.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/client_golang v1.19.0 // indirect
github.com/prometheus/client_model v0.6.0 // indirect
github.com/prometheus/common v0.51.1 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sassoftware/relic v7.2.1+incompatible // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sigstore/sigstore v1.8.3 // indirect
github.com/sigstore/protobuf-specs v0.3.2 // indirect
github.com/sigstore/timestamp-authority v1.2.2 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.2.2 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cobra v1.8.0 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.18.2 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/theupdateframework/go-tuf v0.7.0 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/transparency-dev/merkle v0.0.2 // indirect
github.com/vbatts/tar-split v0.11.5 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yashtewari/glob-intersection v0.2.0 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.mongodb.org/mongo-driver v1.15.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
go.opentelemetry.io/otel v1.31.0 // indirect
go.opentelemetry.io/otel/metric v1.31.0 // indirect
go.opentelemetry.io/otel/sdk v1.31.0 // indirect
go.opentelemetry.io/otel/trace v1.31.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.19.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/term v0.25.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/time v0.7.0 // indirect
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
google.golang.org/grpc v1.67.1 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.5.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
pault.ag/go/debian v0.12.0 // indirect
pault.ag/go/topsort v0.1.1 // indirect
)

741
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -1,42 +1,42 @@
{
"signatures": [
{
"keyid": "b7474a42f2588fa92ed4a2ebea6047a7b1b2f7351f1cfe0912732c0d0fb0fc09",
"sig": "3064023037bbb03c3472b140572a7d5a2895bd80e74435bbcb7053949731f81b104c6d05a0876590cd6a2e94d7ed619426a2f6fa02303adc8c9006fa5506fdd7ea87d2960074a537ad8bf2459f2863e806b47682cbb2f9b01b7502eaf5437a1a68fdaaeac114"
"keyid": "76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221",
"sig": "3065023000f7d0a866576e94eaabc173b9233d4c8fcfa495527088f9022dff5a553f7a457da1015a6d0fc714f84848ec627387360231009fa70b2eebbe15241a2ec9b96a094ebd28661e30b8c3d1eab8d694df2b340bda511c489393630c9a9dacde42c99e9fa1"
}
],
"signed": {
"_type": "root",
"consistent_snapshot": true,
"expires": "2034-04-02T17:00:22Z",
"expires": "2034-05-29T20:14:11Z",
"keys": {
"198f00ff96ea7cbfa7eac480cc9bfc43ce13bb434b901011ab777856533997d3": {
"keytype": "ecdsa",
"keyval": {
"public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgDpP6O0sEt2R+l84WlfmqPBsFSby\nxJsJ6YmeUVgDk/wk9++8IAR6YBYewaKye56gMnIYjTFbyOI8WomA2NQFBw==\n-----END PUBLIC KEY-----\n"
},
"scheme": "ecdsa-sha2-nistp256",
"x-tuf-on-ci-online-uri": "awskms:arn:aws:kms:us-east-1:175142243308:key/fbd8dab6-5677-4b57-87e6-8369c45b3b61"
},
"b7474a42f2588fa92ed4a2ebea6047a7b1b2f7351f1cfe0912732c0d0fb0fc09": {
"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"
},
"bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5": {
"keytype": "ecdsa",
"keyval": {
"public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgDpP6O0sEt2R+l84WlfmqPBsFSby\nxJsJ6YmeUVgDk/wk9++8IAR6YBYewaKye56gMnIYjTFbyOI8WomA2NQFBw==\n-----END PUBLIC KEY-----\n"
},
"scheme": "ecdsa-sha2-nistp256",
"x-tuf-on-ci-online-uri": "awskms:arn:aws:kms:us-east-1:175142243308:key/fbd8dab6-5677-4b57-87e6-8369c45b3b61"
}
},
"roles": {
"root": {
"keyids": [
"b7474a42f2588fa92ed4a2ebea6047a7b1b2f7351f1cfe0912732c0d0fb0fc09"
"76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221"
],
"threshold": 1
},
"snapshot": {
"keyids": [
"198f00ff96ea7cbfa7eac480cc9bfc43ce13bb434b901011ab777856533997d3"
"bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5"
],
"threshold": 1,
"x-tuf-on-ci-expiry-period": 3650,
@@ -44,13 +44,13 @@
},
"targets": {
"keyids": [
"b7474a42f2588fa92ed4a2ebea6047a7b1b2f7351f1cfe0912732c0d0fb0fc09"
"76d0a7e1ff8617ce99627d0fa5c9809f2c0f0d52e0bf65c7b84c031608d25221"
],
"threshold": 1
},
"timestamp": {
"keyids": [
"198f00ff96ea7cbfa7eac480cc9bfc43ce13bb434b901011ab777856533997d3"
"bdd1703ecbde8812614b112a6551d58de3ad681048fd90fca2a3e491edd8afe5"
],
"threshold": 1,
"x-tuf-on-ci-expiry-period": 3650,

View File

@@ -0,0 +1,152 @@
{
"signatures": [
{
"keyid": "08d6f4ca1d0be93a6ceddca15051c0aeec6b98c73e29f3a714de301042d6eeee",
"sig": "306502307ddba543fbd1b9e2ccbee604349024e62bbb1a37906bbd5605a7403fbdb51b701b52f5fcd1b0a0ebfaeef97fa9c344f8023100c37ab675fe96b3976469a5e0cc8a5ffb5d8d6de15020f493d7cf28b0c7e60f450b65c02bfbac0e40642863a1ae3bfa4a"
},
{
"keyid": "3ebd40525193d7628d0b9eccd4771df7297bc87519ec6f312863bb4470966bea",
"sig": "3065023100bc963925fb139dd65653b5e9640572876c5bcd0a3f8bb81e4b0cbd397c10ec4fa0aed7942d77ec78b865e14c72e20e76023043ce7ff39067f054d6d2eaca5dd5176b2c25e27bd763b4ef873aaf4c75762bfb085bb766613692b68206ea0df2863426"
},
{
"keyid": "9c8e1be7d8d0e30656adc81ac201e05cb47a5a097d4d301fd121b77c320231c4",
"sig": "306502307e82d7bc0c66074b06cfc13bac3761c8f677eef252c08448eb33c0249569500e8be2a1ae78c87b5888ed80d088f97fbb023100c358c6ebe18d237bae9a9daeaf2db82297cda8eca635fc22719142740fb23b32eac0341754dd2a85b684c46e3a087ada"
},
{
"keyid": "373d0a38247919a78cf400cf9a90abb9aa23a3c3dce1deee995fdd6a81507117",
"sig": "306402305d9b5fdf3b24240b266a7ae7e02bbcadce8e06f8c111dcef03282faa0baaffb8114653cecda3da115d7859f657508d4f02304b5939fc4404f9e1e8b9d3eb49e195a779b501bd4000cef6cff7a8e657020176dae99cce2a7300b88e549d427278309c"
},
{
"keyid": "48a873aa6c4189804228590af4d48ee5ad3b76417592efdbcef2532401925669",
"sig": "306402306bc5f44621c0d6e18ce16155ebc7890def8fb283859175f7a8425190f0f233e4270b2688df05b017cfc852dee30f9f5b023016572d059d6f27968976df2aaff8238ee0970cea229e5ef30350f2c91347b04e794683da69cf6afe6cf9206dcebc81f4"
}
],
"signed": {
"_type": "root",
"consistent_snapshot": true,
"expires": "2025-06-04T15:05:22Z",
"keys": {
"08d6f4ca1d0be93a6ceddca15051c0aeec6b98c73e29f3a714de301042d6eeee": {
"keytype": "ecdsa",
"keyval": {
"public": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEC4ggHc/D9koyS1/AMNsMGiydM2jDzdsI\nrkC/nyZf8d4UtYJJRxuFRfmyKw9Mh0Ulw/IIyf8ZW2NsnkHgJwGre9/Ici6uomOX\n8yAOlX0Du/oAa7v4igCG7tsW0Z1ljAID\n-----END PUBLIC KEY-----\n"
},
"scheme": "ecdsa-sha2-nistp384",
"x-tuf-on-ci-keyowner": "@jeanlaurent"
},
"2ff207ae7d7b595ef69589622067ef5b6668e1a43081377d942ed8749fa919b4": {
"keytype": "ecdsa",
"keyval": {
"public": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE5pyJ/RXlRO/a2WBSAprikm+VVPqZGC1M\nqgVXE3avwqb9d9lPc9Cphfd4CIAzPCKgeUkGMzQWcC1OwVjOwiB+GRq2Owf7T8pa\nKUe/zRoLjAlUnzUITHP226L1DmQ6Swos\n-----END PUBLIC KEY-----\n"
},
"scheme": "ecdsa-sha2-nistp384",
"x-tuf-on-ci-keyowner": "@kipz"
},
"373d0a38247919a78cf400cf9a90abb9aa23a3c3dce1deee995fdd6a81507117": {
"keytype": "ecdsa",
"keyval": {
"public": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER2zST05lNvybLsSe4UA/hiUrJbA6aFyz\nDimwewwbHvw+gt29EHYtHPqTlO/hSZD5vqZ94Cga9rDsOm3eI5bPkPHApUjw4W7u\n5lDnxuuFKluQ7EiUbswUN0ONTPnmY7Wo\n-----END PUBLIC KEY-----\n"
},
"scheme": "ecdsa-sha2-nistp384",
"x-tuf-on-ci-keyowner": "@binman-docker"
},
"3ebd40525193d7628d0b9eccd4771df7297bc87519ec6f312863bb4470966bea": {
"keytype": "ecdsa",
"keyval": {
"public": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE9C53JKQtD1RYLiSwmR4XRhI7jf28W9TK\nhV3aXW0Z87JyJ4wGNOFnGRE6PuEh7Bbu4ecH0PpsEoirWzzRIgBMR3yHVCSkFBDu\nqfycsInCTAS1jvzLiDHciKXENxAWARHj\n-----END PUBLIC KEY-----\n"
},
"scheme": "ecdsa-sha2-nistp384",
"x-tuf-on-ci-keyowner": "@ingshtrom"
},
"48a873aa6c4189804228590af4d48ee5ad3b76417592efdbcef2532401925669": {
"keytype": "ecdsa",
"keyval": {
"public": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEpQrE8o+fz6kBrs3TD6zqcDPwRZf3FxOX\n+SiT0k3SL1JHsMbxwFAKq+wJzqpqbhzFySuO1VVT93xNDd/rmjEU6HSY7wvT0m/l\nZ0S7yIwl3UnlplzKUYg/8wWJM0C2Qdpj\n-----END PUBLIC KEY-----\n"
},
"scheme": "ecdsa-sha2-nistp384",
"x-tuf-on-ci-keyowner": "@cdupuis"
},
"6132f1f2dd14bf3e9ba1a8df4c8435a77d2fd57f4a99bbb699ae61f85907818e": {
"keytype": "ecdsa",
"keyval": {
"public": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEkFPn3WTH/xVIEFhdP/TCqtnuiOqdgb/v\nEIBjng1TBCVmr7NnW4y4bdZG4Tf9OVTSqlJzuUFThJT/JQR3M7xEzW9WJqUfBTS1\nUuF980elHtMpRkS3NtRp/T0IrkH7+COa\n-----END PUBLIC KEY-----\n"
},
"scheme": "ecdsa-sha2-nistp384",
"x-tuf-on-ci-keyowner": "@jonnystoten"
},
"9c8e1be7d8d0e30656adc81ac201e05cb47a5a097d4d301fd121b77c320231c4": {
"keytype": "ecdsa",
"keyval": {
"public": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEWDreR+iXRtTStv5zmCLGoSmvvfV9/agY\nkx4O1XpRinBwAAA/IO4MI+YCoY0EQpKlSxl0DoVe6hmiXq2ezjTbebGDO66+fTZH\nkrr4KiCsZ8QcdPAR2cUvXkgyBp0WtYYS\n-----END PUBLIC KEY-----\n"
},
"scheme": "ecdsa-sha2-nistp384",
"x-tuf-on-ci-keyowner": "@rachel-taylor-docker"
},
"aef160e03958d5346c903dda755c07e952127ef523df5ec33bd9b24d41fe1cf4": {
"keytype": "ecdsa",
"keyval": {
"public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5gH1kg/MZeiF/GO222hxMerv7MBC\nn91IJG8BbYWKmqZm2za+/QDyrMZExTguYlutu77jZqbkRZEFb/LbL4Ntuw==\n-----END PUBLIC KEY-----\n"
},
"scheme": "ecdsa-sha2-nistp256",
"x-tuf-on-ci-online-uri": "awskms:arn:aws:kms:us-east-1:654654578585:key/751429f1-0aea-4bd8-b450-bb1bce6b058f"
},
"cda750ab29ce33e19ad2fdee4204ad0190b0a33f79e1c5c18a38992d576143d7": {
"keytype": "ecdsa",
"keyval": {
"public": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEYTPARe9DPvvVVf7ch5fTVWXtS9FS97lh\nyZr3Pk33qRprnVB9u7BaEzvQtTYycPO7cmYW5yTOC5ZZa9p2B/v15bOK4NTU0WTT\nXTwSgKmJDh8CD/PBp386S8cwyyIp7NiR\n-----END PUBLIC KEY-----\n"
},
"scheme": "ecdsa-sha2-nistp384",
"x-tuf-on-ci-keyowner": "@whalelines"
},
"f2149d8b7c1ece56d87d81f27fa68b745efc841892b3acfa382ad7f611e612ec": {
"keytype": "ecdsa",
"keyval": {
"public": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEtWRLfl1pLhd5pn4gOmiCQwxE68U0+mIl\n1sU9ugeUz2aCZ9GcTjDNFE/7ZOat74ajeaFi9zmdeCi3UTYioLXNOXfbN6mxM9iQ\nGG3Z5OWYsZpeAv+5jhly2JeWUhFTuJpd\n-----END PUBLIC KEY-----\n"
},
"scheme": "ecdsa-sha2-nistp384",
"x-tuf-on-ci-keyowner": "@mrjoelkamp"
}
},
"roles": {
"root": {
"keyids": [
"08d6f4ca1d0be93a6ceddca15051c0aeec6b98c73e29f3a714de301042d6eeee",
"3ebd40525193d7628d0b9eccd4771df7297bc87519ec6f312863bb4470966bea",
"9c8e1be7d8d0e30656adc81ac201e05cb47a5a097d4d301fd121b77c320231c4",
"373d0a38247919a78cf400cf9a90abb9aa23a3c3dce1deee995fdd6a81507117",
"48a873aa6c4189804228590af4d48ee5ad3b76417592efdbcef2532401925669"
],
"threshold": 3
},
"snapshot": {
"keyids": [
"aef160e03958d5346c903dda755c07e952127ef523df5ec33bd9b24d41fe1cf4"
],
"threshold": 1,
"x-tuf-on-ci-expiry-period": 365,
"x-tuf-on-ci-signing-period": 60
},
"targets": {
"keyids": [
"f2149d8b7c1ece56d87d81f27fa68b745efc841892b3acfa382ad7f611e612ec",
"2ff207ae7d7b595ef69589622067ef5b6668e1a43081377d942ed8749fa919b4",
"6132f1f2dd14bf3e9ba1a8df4c8435a77d2fd57f4a99bbb699ae61f85907818e",
"cda750ab29ce33e19ad2fdee4204ad0190b0a33f79e1c5c18a38992d576143d7"
],
"threshold": 2
},
"timestamp": {
"keyids": [
"aef160e03958d5346c903dda755c07e952127ef523df5ec33bd9b24d41fe1cf4"
],
"threshold": 1,
"x-tuf-on-ci-expiry-period": 2,
"x-tuf-on-ci-signing-period": 1
}
},
"spec_version": "1.0.31",
"version": 1,
"x-tuf-on-ci-expiry-period": 365,
"x-tuf-on-ci-signing-period": 60
}
}

View File

@@ -1,13 +1,63 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package embed
import (
_ "embed"
"fmt"
)
//go:embed embedded-roots/1.root-dev.json
var DevRoot []byte
var devRoot []byte
//go:embed embedded-roots/1.root-staging.json
var StagingRoot []byte
var stagingRoot []byte
var DefaultRoot = StagingRoot
//go:embed embedded-roots/1.root.json
var prodRoot []byte
var defaultRoot = prodRoot
type (
RootName string
EmbeddedRoot struct {
Data []byte
Name RootName
}
)
var (
RootDev = EmbeddedRoot{Data: devRoot, Name: "dev"}
RootStaging = EmbeddedRoot{Data: stagingRoot, Name: "staging"}
RootProd = EmbeddedRoot{Data: prodRoot, Name: "prod"}
RootDefault = EmbeddedRoot{Data: defaultRoot, Name: ""}
)
func GetRootFromName(root string) (*EmbeddedRoot, error) {
switch root {
case string(RootDev.Name):
return &RootDev, nil
case string(RootStaging.Name):
return &RootStaging, nil
case string(RootProd.Name):
return &RootProd, nil
case string(RootDefault.Name):
return &RootDefault, nil
default:
return nil, fmt.Errorf("invalid tuf root: %s", root)
}
}

176
internal/git/git.go Normal file
View File

@@ -0,0 +1,176 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package git
import (
"archive/tar"
"bytes"
"context"
"fmt"
"io"
"os/exec"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
)
// GitCommand is the path to the git binary, overridden in tests to check behavior when git is not installed.
var GitCommand = "git"
func Clone(ctx context.Context, gitRepo string, gitCommit string, targetDir string) error {
const localBranch = "FETCH_HEAD"
repo, err := git.PlainInit(targetDir, false)
if err != nil {
return fmt.Errorf("failed to init: %w", err)
}
remote, err := repo.CreateRemote(&config.RemoteConfig{
Name: "origin",
URLs: []string{gitRepo},
Fetch: []config.RefSpec{
config.RefSpec(fmt.Sprintf("%s:%s", gitCommit, localBranch)),
},
})
if err != nil {
return fmt.Errorf("failed to add remote: %w", err)
}
err = remote.FetchContext(ctx, &git.FetchOptions{
Depth: 1,
})
if err != nil {
return fmt.Errorf("failed to fetch: %w", err)
}
wt, err := repo.Worktree()
if err != nil {
return fmt.Errorf("failed to get worktree: %w", err)
}
err = wt.Checkout(&git.CheckoutOptions{
Hash: plumbing.NewHash(gitCommit),
})
if err != nil {
return fmt.Errorf("failed to checkout: %w", err)
}
return nil
}
type execError struct {
*exec.ExitError
stderr []byte
}
func (e *execError) Error() string {
trimmed := bytes.TrimSpace(e.stderr)
if len(trimmed) == 0 {
return e.ExitError.Error()
}
return fmt.Sprintf("%s, %q", e.ExitError.Error(), string(bytes.TrimSpace(e.stderr)))
}
func (e *execError) Unwrap() error {
return e.ExitError
}
// Archive creates a tar archive of the files in the subdirectory given by subdir of the git repository at gitRepoDir.
// This is accomplished by running `git archive --format=tar HEAD:subdir` in the git repository directory.
//
// The archive is written to the returned io.Reader. It is not necessary to close the returned reader.
// Any error encountered while starting the command will be returned immediately.
// Any error encountered after the command is running will be returned on the next read from the returned io.Reader.
func Archive(ctx context.Context, gitRepoDir string, subdir string) (io.Reader, error) {
readPipe, writePipe := io.Pipe()
treeish := fmt.Sprintf("HEAD:%s", subdir)
cmd := exec.CommandContext(ctx, GitCommand, "archive", "--format=tar", treeish)
// run the command inside the git repo directory
cmd.Dir = gitRepoDir
// set the standard output to the write end of the pipe
cmd.Stdout = writePipe
// capture standard error so we can include it in the error message if the command fails
stderr := new(bytes.Buffer)
cmd.Stderr = stderr
err := cmd.Start()
if err != nil {
return nil, fmt.Errorf("failed to start command: %w", err)
}
// spawn a goroutine to wait for the command to finish and close the write pipe
go func() {
var err error // variable to hold any error
defer func() {
if p := recover(); p != nil {
// if we panic, set err to a new error wrapping the panic value
err = fmt.Errorf("panic: %v", p)
}
// send any error from the command (or the panic above) to the write pipe
// or nil if there was no error
// this will cause the error to be returned on the next read from the read pipe
writePipe.CloseWithError(err)
}()
// wait for the command to finish and capture any error
err = cmd.Wait()
if err != nil {
if ee, ok := err.(*exec.ExitError); ok {
err = &execError{ExitError: ee, stderr: stderr.Bytes()}
}
}
}()
return readPipe, nil
}
func TarScrub(in io.Reader, out io.Writer) error {
tr := tar.NewReader(in)
tw := tar.NewWriter(out)
defer tw.Flush() // note: flush instead of close to avoid the empty block at EOF
for {
hdr, err := tr.Next()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
newHdr := &tar.Header{
Typeflag: hdr.Typeflag,
Name: hdr.Name,
Linkname: hdr.Linkname,
Size: hdr.Size,
Mode: hdr.Mode,
Devmajor: hdr.Devmajor,
Devminor: hdr.Devminor,
}
if err := tw.WriteHeader(newHdr); err != nil {
return err
}
_, err = io.CopyN(tw, tr, hdr.Size)
if err != nil {
return err
}
}
}

42
internal/git/git_test.go Normal file
View File

@@ -0,0 +1,42 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package git
import (
"context"
"os"
"os/exec"
"testing"
"github.com/stretchr/testify/require"
)
func TestArchiveWithInvalidGitCommand(t *testing.T) {
originalGitCommand := GitCommand
GitCommand = "invalid-git-command"
defer func() { GitCommand = originalGitCommand }()
tempDir, err := os.MkdirTemp("", "gitrepo")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
}
defer os.RemoveAll(tempDir)
ctx := context.Background()
_, err = Archive(ctx, tempDir, "")
require.ErrorIs(t, err, exec.ErrNotFound)
}

View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIKZEqmmd++eAY3bmPoBdY6nC2wLy4da2yeVZNKCp6Oj2oAoGCCqGSM49
AwEHoUQDQgAEZmicqYSY38DprGr42jU0V3ND0ROjzSRH1+yjsxhh0bi52Hh/DuOh
rSq2KJ5a09lW3ybnDjljowbkof0Y1i9Oow==
-----END EC PRIVATE KEY-----

View File

@@ -1,26 +1,61 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package test
import (
"context"
"crypto"
"crypto/x509"
_ "embed"
"encoding/pem"
"fmt"
"io"
"log"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"github.com/docker/attest/pkg/oci"
"github.com/docker/attest/pkg/policy"
"github.com/docker/attest/pkg/signerverifier"
"github.com/docker/attest/pkg/tlog"
"github.com/docker/attest/signerverifier"
"github.com/docker/attest/useragent"
"github.com/google/go-containerregistry/pkg/registry"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
const (
USE_MOCK_TL = true
USE_MOCK_KMS = true
USE_MOCK_POLICY = true
AwsRegion = "us-east-1"
AwsKmsKeyArn = "arn:aws:kms:us-east-1:175142243308:alias/doi-signing" // sandbox
UseMockKMS = true
AWSRegion = "us-east-1"
AWSKMSKeyARN = "arn:aws:kms:us-east-1:175142243308:alias/doi-signing" // sandbox
UnsignedLinuxAMD64ImageDigest = "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620"
UnsignedLinuxArm64ImageDigest = "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e"
)
func UnsignedTestIndex(rel ...string) string {
rel = append(rel, "test", "testdata", "unsigned-index")
return filepath.Join(rel...)
}
func UnsignedTestImage(rel ...string) string {
rel = append(rel, "test", "testdata", "unsigned-image")
return filepath.Join(rel...)
}
func CreateTempDir(t *testing.T, dir, pattern string) string {
// Create a temporary directory for output oci layout
tempDir, err := os.MkdirTemp(dir, pattern)
@@ -37,34 +72,24 @@ func CreateTempDir(t *testing.T, dir, pattern string) string {
return tempDir
}
//go:embed test-signing-key.pem
var signingKey []byte
func GetMockSigner(_ context.Context) (dsse.SignerVerifier, error) {
return signerverifier.LoadKeyPair(signingKey)
}
func Setup(t *testing.T) (context.Context, dsse.SignerVerifier) {
var tl tlog.TL
if USE_MOCK_TL {
tl = tlog.GetMockTL()
} else {
tl = &tlog.RekorTL{}
}
ctx := tlog.WithTL(context.Background(), tl)
var policyEvaluator policy.PolicyEvaluator
if USE_MOCK_POLICY {
policyEvaluator = GetMockPolicy()
} else {
policyEvaluator = policy.NewRegoEvaluator(true)
}
ctx = policy.WithPolicyEvaluator(ctx, policyEvaluator)
ctx := context.Background()
var signer dsse.SignerVerifier
var err error
if USE_MOCK_KMS {
if UseMockKMS {
signer, err = GetMockSigner(ctx)
if err != nil {
t.Fatal(err)
}
} else {
signer, err = signerverifier.GetAWSSigner(ctx, AwsKmsKeyArn, AwsRegion)
signer, err = signerverifier.GetAWSSigner(ctx, AWSKMSKeyARN, AWSRegion)
if err != nil {
t.Fatal(err)
}
@@ -73,14 +98,30 @@ func Setup(t *testing.T) (context.Context, dsse.SignerVerifier) {
return ctx, signer
}
func GetMockSigner(ctx context.Context) (dsse.SignerVerifier, error) {
return signerverifier.GenKeyPair()
func NewLocalRegistry(ctx context.Context, options ...registry.Option) *httptest.Server {
options = append(options, registry.Logger(log.New(io.Discard, "", log.LstdFlags)))
regHandler := registry.New(options...)
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Check the user agent
ua := r.Header.Get("User-Agent")
userAgent := useragent.Get(ctx)
if !strings.HasPrefix(ua, userAgent) {
http.Error(w, fmt.Sprintf("expected user agent to contain %q, got %q", userAgent, ua), http.StatusForbidden)
}
regHandler.ServeHTTP(w, r)
}))
}
func GetMockPolicy() policy.PolicyEvaluator {
return &policy.MockPolicyEvaluator{
EvaluateFunc: func(ctx context.Context, resolver oci.AttestationResolver, policy []*policy.PolicyFile, input *policy.PolicyInput) error {
return nil
},
func PublicKeyToPEM(pubKey crypto.PublicKey) (string, error) {
derBytes, err := x509.MarshalPKIXPublicKey(pubKey)
if err != nil {
return "", err
}
pemBlock := &pem.Block{
Type: "PUBLIC KEY",
Bytes: derBytes,
}
return string(pem.EncodeToMemory(pemBlock)), nil
}

View File

@@ -1,3 +1,19 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
@@ -5,14 +21,11 @@ import (
"encoding/hex"
)
func HexHashBytes(input []byte) string {
s256 := sha256.New()
s256.Write(input)
hashSum := s256.Sum(nil)
return hex.EncodeToString(hashSum)
func SHA256Hex(input []byte) string {
return hex.EncodeToString(SHA256(input))
}
func S256(data []byte) []byte {
func SHA256(data []byte) []byte {
h := sha256.Sum256(data)
return h[:]
}

View File

@@ -1,10 +0,0 @@
package util
func StringInSlice(str string, list []string) bool {
for _, v := range list {
if v == str {
return true
}
}
return false
}

154
mapping/mapping.go Normal file
View File

@@ -0,0 +1,154 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mapping
import (
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"github.com/docker/attest/tuf"
v1 "github.com/google/go-containerregistry/pkg/v1"
"sigs.k8s.io/yaml"
)
const (
MappingFilename = "mapping.yaml"
)
func validateMappingsFile(mappings *policyMappingsFile) error {
var validationErrors []error
if mappings.Kind != "policy-mapping" {
validationErrors = append(validationErrors, fmt.Errorf("file is not of kind policy-mapping: %s", mappings.Kind))
}
if mappings.Version != "v1" {
validationErrors = append(validationErrors, fmt.Errorf("unsupported policy mapping file version: %s", mappings.Version))
}
for _, rule := range mappings.Rules {
if rule.Pattern == "" {
validationErrors = append(validationErrors, fmt.Errorf("rule missing pattern: %s", rule))
}
if rule.PolicyID == "" && rule.Replacement == "" {
validationErrors = append(validationErrors, fmt.Errorf("rule must have policy-id or replacement: %s", rule))
}
if rule.PolicyID != "" && rule.Replacement != "" {
validationErrors = append(validationErrors, fmt.Errorf("rule cannot have both policy-id and replacement: %s", rule))
}
if rule.Platforms != nil {
for _, platform := range rule.Platforms {
if platform == "" {
validationErrors = append(validationErrors, fmt.Errorf("rule has empty platform: %s", rule))
}
}
}
}
for _, policy := range mappings.Policies {
if policy.ID == "" {
validationErrors = append(validationErrors, fmt.Errorf("policy missing id: %s", policy.ID))
}
if len(policy.Files) == 0 {
validationErrors = append(validationErrors, fmt.Errorf("policy missing files: %v", policy))
}
for _, file := range policy.Files {
if file.Path == "" {
validationErrors = append(validationErrors, fmt.Errorf("file missing path: %s", file))
}
}
}
if len(validationErrors) > 0 {
return errors.Join(validationErrors...)
}
return nil
}
func parsePolicyMappingsFile(data []byte) (*PolicyMappings, error) {
mappings := &policyMappingsFile{}
err := yaml.Unmarshal(data, mappings)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal policy mapping file: %w", err)
}
err = validateMappingsFile(mappings)
if err != nil {
return nil, fmt.Errorf("invalid policy mapping file: %w", err)
}
return expandMappingFile(mappings)
}
func LoadLocalMappings(configDir string) (*PolicyMappings, error) {
if configDir == "" {
return nil, nil
}
path := filepath.Join(configDir, MappingFilename)
mappingFile, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read local policy mapping file %s: %w", path, err)
}
return parsePolicyMappingsFile(mappingFile)
}
func LoadTUFMappings(tufClient tuf.Downloader, localTargetsDir string) (*PolicyMappings, error) {
if tufClient == nil {
return nil, fmt.Errorf("tuf client not set")
}
filename := MappingFilename
file, err := tufClient.DownloadTarget(filename, filepath.Join(localTargetsDir, filename))
if err != nil {
return nil, fmt.Errorf("failed to download policy mapping file %s: %w", filename, err)
}
return parsePolicyMappingsFile(file.Data)
}
func expandMappingFile(mappingFile *policyMappingsFile) (*PolicyMappings, error) {
policies := make(map[string]*PolicyMapping)
for _, policy := range mappingFile.Policies {
policies[policy.ID] = policy
}
var rules []*PolicyRule
for _, rule := range mappingFile.Rules {
patternRegex, err := regexp.Compile(rule.Pattern)
if err != nil {
return nil, err
}
platforms := make([]*v1.Platform, 0, len(rule.Platforms))
for _, platform := range rule.Platforms {
parsedPlatform, err := v1.ParsePlatform(platform)
if err != nil {
return nil, fmt.Errorf("failed to parse platform %s: %w", platform, err)
}
platforms = append(platforms, parsedPlatform)
}
rules = append(rules, &PolicyRule{
Pattern: patternRegex,
PolicyID: rule.PolicyID,
Replacement: rule.Replacement,
Platforms: platforms,
})
}
return &PolicyMappings{
Version: mappingFile.Version,
Kind: mappingFile.Kind,
Policies: policies,
Rules: rules,
}, nil
}

98
mapping/mapping_test.go Normal file
View File

@@ -0,0 +1,98 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mapping
import (
"testing"
"github.com/stretchr/testify/require"
)
func newMapping() *policyMappingsFile {
return &policyMappingsFile{
Version: "v1",
Kind: "policy-mapping",
Policies: []*PolicyMapping{
{
ID: "docker-official-images",
Files: []PolicyMappingFile{
{
Path: "docker.io/library/alpine",
},
},
},
},
Rules: []*policyRuleFile{
{
Pattern: "docker.io/library/alpine",
PolicyID: "docker-official-images",
},
},
}
}
func TestMappingsFileValidation(t *testing.T) {
mappings := newMapping()
err := validateMappingsFile(mappings)
require.NoError(t, err)
mappings = newMapping()
mappings.Kind = "not-policy-mapping"
err = validateMappingsFile(mappings)
require.ErrorContains(t, err, "file is not of kind policy-mapping: not-policy-mapping")
mappings = newMapping()
mappings.Version = "v2"
err = validateMappingsFile(mappings)
require.ErrorContains(t, err, "unsupported policy mapping file version: v2")
mappings = newMapping()
mappings.Rules[0].Pattern = ""
err = validateMappingsFile(mappings)
require.ErrorContains(t, err, "rule missing pattern")
mappings = newMapping()
mappings.Rules[0].PolicyID = ""
err = validateMappingsFile(mappings)
require.ErrorContains(t, err, "rule must have policy-id or replacement")
mappings = newMapping()
mappings.Rules[0].PolicyID = "docker-official-images"
mappings.Rules[0].Replacement = "docker.io/library/alpine"
err = validateMappingsFile(mappings)
require.ErrorContains(t, err, "rule cannot have both policy-id and replacement")
mappings = newMapping()
mappings.Policies[0].ID = ""
err = validateMappingsFile(mappings)
require.ErrorContains(t, err, "policy missing id")
mappings = newMapping()
mappings.Policies[0].Files = nil
err = validateMappingsFile(mappings)
require.ErrorContains(t, err, "policy missing files")
mappings = newMapping()
mappings.Policies[0].Files[0].Path = ""
err = validateMappingsFile(mappings)
require.ErrorContains(t, err, "file missing path")
// multiple errors
mappings.Policies[0].ID = ""
err = validateMappingsFile(mappings)
require.ErrorContains(t, err, "policy missing id: \nfile missing path: {}")
}

96
mapping/match.go Normal file
View File

@@ -0,0 +1,96 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mapping
import (
"fmt"
v1 "github.com/google/go-containerregistry/pkg/v1"
)
type matchType string
const (
MatchTypePolicy matchType = "policy"
MatchTypeMatchNoPolicy matchType = "match_no_policy"
MatchTypeNoMatch matchType = "no_match"
)
type PolicyMatch struct {
MatchType matchType
Policy *PolicyMapping
Rule *PolicyRule
MatchedName string
}
func (mappings *PolicyMappings) FindPolicyMatch(imageName string, platform *v1.Platform) (*PolicyMatch, error) {
if mappings == nil {
return &PolicyMatch{MatchType: MatchTypeNoMatch, MatchedName: imageName}, nil
}
return mappings.findPolicyMatchImpl(imageName, platform, make(map[*PolicyRule]bool))
}
func (mappings *PolicyMappings) findPolicyMatchImpl(imageName string, platform *v1.Platform, matched map[*PolicyRule]bool) (*PolicyMatch, error) {
for _, rule := range mappings.Rules {
if !rule.matchesPlatform(platform) {
continue
}
if rule.Pattern.MatchString(imageName) {
switch {
case rule.PolicyID == "" && rule.Replacement == "":
return nil, fmt.Errorf("rule %s has neither policy-id nor rewrite", rule.Pattern)
case rule.PolicyID != "" && rule.Replacement != "":
return nil, fmt.Errorf("rule %s has both policy-id and rewrite", rule.Pattern)
case rule.PolicyID != "":
policy := mappings.Policies[rule.PolicyID]
if policy != nil {
return &PolicyMatch{
MatchType: MatchTypePolicy,
Policy: policy,
Rule: rule,
MatchedName: imageName,
}, nil
}
return &PolicyMatch{
MatchType: MatchTypeMatchNoPolicy,
Rule: rule,
MatchedName: imageName,
}, nil
case rule.Replacement != "":
if matched[rule] {
return nil, fmt.Errorf("rewrite loop detected")
}
matched[rule] = true
imageName = rule.Pattern.ReplaceAllString(imageName, rule.Replacement)
return mappings.findPolicyMatchImpl(imageName, platform, matched)
}
}
}
return &PolicyMatch{MatchType: MatchTypeNoMatch}, nil
}
func (rule *PolicyRule) matchesPlatform(platform *v1.Platform) bool {
if len(rule.Platforms) == 0 {
return true
}
for i := range rule.Platforms {
if rule.Platforms[i].Equals(*platform) {
return true
}
}
return false
}

216
mapping/match_test.go Normal file
View File

@@ -0,0 +1,216 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mapping
import (
"path/filepath"
"testing"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFindPolicyMatch(t *testing.T) {
defaultPlatform, err := v1.ParsePlatform("linux/amd64")
require.NoError(t, err)
testCases := []struct {
name string
imageName string
mappingDir string
expectError bool
expectLoadingError bool
expectedMatchType matchType
expectedPolicyID string
expectedImageName string
platform string
}{
{
name: "alpine",
mappingDir: "doi",
imageName: "docker.io/library/alpine",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "no match",
mappingDir: "doi",
imageName: "docker.io/something/else",
expectedMatchType: MatchTypeNoMatch,
},
{
name: "match, no policy",
mappingDir: "local",
imageName: "docker.io/library/alpine",
expectedMatchType: MatchTypeMatchNoPolicy,
expectedImageName: "docker.io/library/alpine",
},
{
name: "simple rewrite",
mappingDir: "simple-rewrite",
imageName: "mycoolmirror.org/library/alpine",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "rewrite no match",
mappingDir: "rewrite-to-no-match",
imageName: "mycoolmirror.org/library/alpine",
expectedMatchType: MatchTypeNoMatch,
},
{
name: "rewrite to match, no policy",
mappingDir: "rewrite-to-local",
imageName: "mycoolmirror.org/library/alpine",
expectedMatchType: MatchTypeMatchNoPolicy,
expectedImageName: "docker.io/library/alpine",
},
{
name: "multiple rewrites",
mappingDir: "rewrite-multiple",
imageName: "myevencoolermirror.org/library/alpine",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "rewrite loop",
mappingDir: "rewrite-loop",
imageName: "yin/alpine",
expectError: true,
},
{
name: "alpine with platform",
mappingDir: "doi",
imageName: "docker.io/library/alpine",
platform: "linux/amd64",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "alpine with platform",
mappingDir: "doi-platform",
imageName: "docker.io/library/alpine",
platform: "linux/amd64",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "alpine with no matching platform",
mappingDir: "doi-platform",
imageName: "docker.io/library/alpine",
platform: "linux/arm64",
expectedMatchType: MatchTypeNoMatch,
expectedPolicyID: "docker-official-images",
},
{
name: "alpine with platform",
mappingDir: "doi-platform",
imageName: "docker.io/library/alpine",
platform: "linux/amd64",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "alpine with invalid platform in mapping",
mappingDir: "doi-platform-broken",
imageName: "docker.io/library/alpine",
platform: "linux/amd64",
expectLoadingError: true,
},
{
name: "firefox with > 1 platforms in policy",
mappingDir: "doi-platform",
imageName: "docker.io/mozilla/firefox",
platform: "linux/arm64",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/mozilla/firefox",
},
{
name: "firefox with > 1 platforms in policy (no match)",
mappingDir: "doi-platform",
imageName: "docker.io/mozilla/firefox",
platform: "macOs/arm64",
expectedMatchType: MatchTypeNoMatch,
expectedPolicyID: "docker-official-images",
},
{
name: "rewrite and platform",
mappingDir: "doi-platform",
imageName: "mycoolmirror.org/library/alpine",
platform: "linux/amd64",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "rewrite and platform mismatch",
mappingDir: "doi-platform",
imageName: "mycoolmirror.org/library/alpine",
platform: "macOs/amd64",
expectedMatchType: MatchTypeNoMatch,
expectedPolicyID: "docker-official-images",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mappings, err := LoadLocalMappings(filepath.Join("testdata", "mappings", tc.mappingDir))
if tc.expectLoadingError {
require.Error(t, err)
return
}
require.NoError(t, err)
platform := defaultPlatform
if tc.platform != "" {
platform, err = v1.ParsePlatform(tc.platform)
require.NoError(t, err)
}
match, err := mappings.FindPolicyMatch(tc.imageName, platform)
if tc.expectError {
require.Error(t, err)
// TODO: check error matches expected error message
return
}
require.NoError(t, err)
assert.Equal(t, tc.expectedMatchType, match.MatchType)
if match.MatchType == MatchTypePolicy {
if assert.NotNil(t, match.Policy) {
assert.Equal(t, tc.expectedPolicyID, match.Policy.ID)
}
}
if match.MatchType == MatchTypeMatchNoPolicy || match.MatchType == MatchTypePolicy {
assert.Equal(t, tc.expectedImageName, match.MatchedName)
}
})
}
}

View File

@@ -0,0 +1,11 @@
version: v1
kind: policy-mapping
policies:
- id: docker-official-images
description: Docker Official Images
files:
- path: doi/policy.rego
rules:
- pattern: "^docker[.]io/library/(.*)$"
platforms: ["linux/amd64/broken/platform/spec/1.0:foobar"]
policy-id: docker-official-images

View File

@@ -0,0 +1,17 @@
version: v1
kind: policy-mapping
policies:
- id: docker-official-images
description: Docker Official Images
files:
- path: doi/policy.rego
rules:
- pattern: "^docker[.]io/library/(.*)$"
platforms: ["linux/amd64"]
policy-id: docker-official-images
- pattern: "^docker.io/mozilla/(.*)$"
platforms: ["linux/amd64", "linux/arm64"]
policy-id: docker-official-images
- pattern: "^mycoolmirror[.]org/library/(.*)$"
platforms: ["linux/amd64"]
rewrite: "docker.io/library/$1"

View File

@@ -0,0 +1,10 @@
version: v1
kind: policy-mapping
policies:
- id: docker-official-images
description: Docker Official Images
files:
- path: doi/policy.rego
rules:
- pattern: "^docker[.]io/library/(.*)$"
policy-id: docker-official-images

View File

@@ -0,0 +1,10 @@
version: v1
kind: policy-mapping
policies:
- id: local-policy
description: Local Policy
files:
- path: local-policy.rego
rules:
- pattern: "^docker[.]io/library/(.*)$"
policy-id: docker-official-images # note this policy does not exist in this file

View File

@@ -0,0 +1,13 @@
version: v1
kind: policy-mapping
policies:
- id: docker-official-images
description: Docker Official Images
files:
- path: doi/policy.rego
rules:
- pattern: "^docker[.]io/library/(.*)$"
policy-id: docker-official-images
- pattern: "^mycoolmirror[.]org/library/(.*)$"
rewrite: "docker.io/library/$1"
policy-id: docker-official-images # invalid to specify both rewrite and policy-id

View File

@@ -0,0 +1,14 @@
version: v1
kind: policy-mapping
policies:
- id: docker-official-images
description: Docker Official Images
files:
- path: doi/policy.rego
rules:
- pattern: "^docker[.]io/library/(.*)$"
policy-id: docker-official-images
- pattern: "^yin/(.*)$"
rewrite: "yang/$1"
- pattern: "^yang/(.*)$"
rewrite: "yin/$1"

View File

@@ -0,0 +1,14 @@
version: v1
kind: policy-mapping
policies:
- id: docker-official-images
description: Docker Official Images
files:
- path: doi/policy.rego
rules:
- pattern: "^docker[.]io/library/(.*)$"
policy-id: docker-official-images
- pattern: "^mycoolmirror[.]org/library/(.*)$"
rewrite: "docker.io/library/$1"
- pattern: "^myevencoolermirror[.]org/library/(.*)$"
rewrite: "mycoolmirror.org/library/$1"

View File

@@ -0,0 +1,12 @@
version: v1
kind: policy-mapping
policies:
- id: local-policy
description: Local Policy
files:
- path: local-policy.rego
rules:
- pattern: "^docker[.]io/library/(.*)$"
policy-id: docker-official-images # note this policy does not exist in this file
- pattern: "^mycoolmirror[.]org/library/(.*)$"
rewrite: "docker.io/library/$1"

View File

@@ -0,0 +1,12 @@
version: v1
kind: policy-mapping
policies:
- id: docker-official-images
description: Docker Official Images
files:
- path: doi/policy.rego
rules:
- pattern: "^docker[.]io/library/(.*)$"
policy-id: docker-official-images
- pattern: "^mycoolmirror[.]org/library/(.*)$"
rewrite: "badredirect.org/$1" # no matching rule for this rewrite

View File

@@ -0,0 +1,12 @@
version: v1
kind: policy-mapping
policies:
- id: docker-official-images
description: Docker Official Images
files:
- path: doi/policy.rego
rules:
- pattern: "^docker[.]io/library/(.*)$"
policy-id: docker-official-images
- pattern: "^mycoolmirror[.]org/library/(.*)$"
rewrite: "docker.io/library/$1"

74
mapping/types.go Normal file
View File

@@ -0,0 +1,74 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mapping
import (
"regexp"
v1 "github.com/google/go-containerregistry/pkg/v1"
)
type policyMappingsFile struct {
Version string `json:"version"`
Kind string `json:"kind"`
Policies []*PolicyMapping `json:"policies"`
Rules []*policyRuleFile `json:"rules"`
}
type policyRuleFile struct {
Pattern string `json:"pattern"`
Platforms []string `json:"platforms"`
PolicyID string `json:"policy-id"`
Replacement string `json:"rewrite"`
}
type PolicyMappings struct {
Version string
Kind string
Policies map[string]*PolicyMapping
Rules []*PolicyRule
}
type AttestationStyle string
const (
AttestationStyleAttached AttestationStyle = "attached"
AttestationStyleReferrers AttestationStyle = "referrers"
)
type PolicyMapping struct {
ID string `json:"id"`
Description string `json:"description"`
Files []PolicyMappingFile `json:"files"`
Attestations *AttestationConfig `json:"attestations"`
}
type AttestationConfig struct {
Style AttestationStyle `json:"style"`
Repo string `json:"repo"`
}
type PolicyMappingFile struct {
Path string `json:"path"`
}
type PolicyRule struct {
Pattern *regexp.Regexp
PolicyID string
Replacement string
Platforms []*v1.Platform
}

2
mirror/README.md Normal file
View File

@@ -0,0 +1,2 @@
## mirror
This package contains components to mirror TUF metadata and targets to OCI.

View File

@@ -0,0 +1,170 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mirror_test
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/docker/attest/mirror"
"github.com/docker/attest/oci"
"github.com/docker/attest/tuf"
v1 "github.com/google/go-containerregistry/pkg/v1"
)
type TufMirrorOutput struct {
metadata v1.Image
delegatedMetadata []*mirror.Image
targets []*mirror.Image
delegatedTargets []*mirror.Index
}
func ExampleNewTUFMirror() {
home, err := os.UserHomeDir()
if err != nil {
panic(err)
}
tufOutputPath := filepath.Join(home, ".docker", "tuf")
// configure TUF mirror
metadataURI := "https://docker.github.io/tuf-staging/metadata"
targetsURI := "https://docker.github.io/tuf-staging/targets"
ctx := context.Background()
m, err := mirror.NewTUFMirror(ctx, tuf.DockerTUFRootStaging.Data, tufOutputPath, metadataURI, targetsURI, tuf.NewMockVersionChecker())
if err != nil {
panic(err)
}
// create metadata manifest
metadataManifest, err := m.GetMetadataManifest(metadataURI)
if err != nil {
panic(err)
}
// create delegated targets metadata manifests
delegatedMetadata, err := m.GetDelegatedMetadataMirrors()
if err != nil {
panic(err)
}
// create targets manifest
targets, err := m.GetTUFTargetMirrors()
if err != nil {
panic(err)
}
// create delegated targets manifests
delegatedTargets, err := m.GetDelegatedTargetMirrors()
if err != nil {
panic(err)
}
mirrorOutput := &TufMirrorOutput{
metadata: metadataManifest,
delegatedMetadata: delegatedMetadata,
targets: targets,
delegatedTargets: delegatedTargets,
}
// push metadata and targets to registry (optional)
err = mirrorToRegistry(ctx, mirrorOutput)
if err != nil {
panic(err)
}
// save metadata and targets to local directory (optional)
mirrorOutputPath := filepath.Join(home, ".docker", "tuf", "mirror")
err = mirrorToLocal(mirrorOutput, mirrorOutputPath)
if err != nil {
panic(err)
}
}
func mirrorToRegistry(ctx context.Context, o *TufMirrorOutput) error {
// push metadata to registry
metadataRepo := "registry-1.docker.io/docker/tuf-metadata:latest"
err := oci.PushImageToRegistry(ctx, o.metadata, metadataRepo)
if err != nil {
return err
}
// push delegated metadata to registry
for _, metadata := range o.delegatedMetadata {
repo, _, ok := strings.Cut(metadataRepo, ":")
if !ok {
return fmt.Errorf("failed to get repo without tag: %s", metadataRepo)
}
imageName := fmt.Sprintf("%s:%s", repo, metadata.Tag)
err = oci.PushImageToRegistry(ctx, metadata.Image, imageName)
if err != nil {
return err
}
}
// push top-level targets to registry
targetsRepo := "registry-1.docker.io/docker/tuf-targets"
for _, target := range o.targets {
imageName := fmt.Sprintf("%s:%s", targetsRepo, target.Tag)
err = oci.PushImageToRegistry(ctx, target.Image, imageName)
if err != nil {
return err
}
}
// push delegated targets to registry
for _, target := range o.delegatedTargets {
imageName := fmt.Sprintf("%s:%s", targetsRepo, target.Tag)
err = oci.PushIndexToRegistry(ctx, target.Index, imageName)
if err != nil {
return err
}
}
return nil
}
func mirrorToLocal(o *TufMirrorOutput, outputPath string) error {
// output metadata to local directory
err := oci.SaveImageAsOCILayout(o.metadata, outputPath)
if err != nil {
return err
}
// output delegated metadata to local directory
for _, metadata := range o.delegatedMetadata {
path := filepath.Join(outputPath, metadata.Tag)
err = oci.SaveImageAsOCILayout(metadata.Image, path)
if err != nil {
return err
}
}
// output top-level targets to local directory
for _, target := range o.targets {
path := filepath.Join(outputPath, target.Tag)
err = oci.SaveImageAsOCILayout(target.Image, path)
if err != nil {
return err
}
}
// output delegated targets to local directory
for _, target := range o.delegatedTargets {
path := filepath.Join(outputPath, target.Tag)
err = oci.SaveIndexAsOCILayout(target.Index, path)
if err != nil {
return err
}
}
return nil
}

View File

@@ -1,9 +1,26 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mirror
import (
"fmt"
"strconv"
"github.com/docker/attest/oci"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/mutate"
@@ -16,9 +33,9 @@ import (
// TUF root metadata
// -----------------
// GetMetadataManifest returns an image with TUF root metadata as layers
func (m *TufMirror) GetMetadataManifest(metadataURL string) (*v1.Image, error) {
metadata, err := m.getTufMetadataMirror(metadataURL)
// GetMetadataManifest returns an image with TUF root metadata as layers.
func (m *TUFMirror) GetMetadataManifest(metadataURL string) (*oci.EmptyConfigImage, error) {
metadata, err := m.getMetadataMirror(metadataURL)
if err != nil {
return nil, fmt.Errorf("failed to get metadata: %w", err)
}
@@ -26,19 +43,19 @@ func (m *TufMirror) GetMetadataManifest(metadataURL string) (*v1.Image, error) {
if err != nil {
return nil, fmt.Errorf("failed to build metadata manifest: %w", err)
}
return manifest, nil
return &oci.EmptyConfigImage{Image: manifest}, nil
}
// getTufMetadataMirror returns a TufMetadata struct with TUF metadata as map of file names to bytes
func (m *TufMirror) getTufMetadataMirror(metadataURL string) (*TufMetadata, error) {
trustedMetadata := m.TufClient.GetMetadata()
// getMetadataMirror returns a TufMetadata struct with TUF metadata as map of file names to bytes.
func (m *TUFMirror) getMetadataMirror(metadataURL string) (*TUFMetadata, error) {
trustedMetadata := m.TUFClient.GetMetadata()
rootMetadata := map[string][]byte{}
rootVersion := trustedMetadata.Root.Signed.Version
// get the previous versions of root metadata if any
if rootVersion != 1 {
var err error
rootMetadata, err = m.TufClient.GetPriorRoots(metadataURL)
rootMetadata, err = m.TUFClient.GetPriorRoots(metadataURL)
if err != nil {
return nil, fmt.Errorf("failed to get prior root metadata: %w", err)
}
@@ -69,7 +86,7 @@ func (m *TufMirror) getTufMetadataMirror(metadataURL string) (*TufMetadata, erro
snapshotVersion = strconv.FormatInt(trustedMetadata.Snapshot.Signed.Version, 10)
targetsVersion = strconv.FormatInt(trustedMetadata.Targets[metadata.TARGETS].Signed.Version, 10)
}
return &TufMetadata{
return &TUFMetadata{
Root: rootMetadata,
Snapshot: map[string][]byte{nameFromRole(metadata.SNAPSHOT, snapshotVersion): snapshotBytes},
Targets: map[string][]byte{nameFromRole(metadata.TARGETS, targetsVersion): targetsBytes},
@@ -77,27 +94,27 @@ func (m *TufMirror) getTufMetadataMirror(metadataURL string) (*TufMetadata, erro
}, nil
}
// buildMetadataManifest returns an OCI image with TUF metadata as layers with annotations
func (m *TufMirror) buildMetadataManifest(metadata *TufMetadata) (*v1.Image, error) {
// buildMetadataManifest returns an OCI image with TUF metadata as layers with annotations.
func (m *TUFMirror) buildMetadataManifest(metadata *TUFMetadata) (v1.Image, error) {
img := empty.Image
img = mutate.MediaType(img, types.OCIManifestSchema1)
img = mutate.ConfigMediaType(img, types.OCIConfigJSON)
for _, role := range TufRoles {
for _, role := range TUFRoles {
layers, err := m.makeRoleLayers(role, metadata)
if err != nil {
return nil, fmt.Errorf("failed to make role layer: %w", err)
}
img, err = mutate.Append(img, *layers...)
img, err = mutate.Append(img, layers...)
if err != nil {
return nil, fmt.Errorf("failed to append role layer to image: %w", err)
}
}
return &img, nil
return img, nil
}
// makeRoleLayers returns a list of layers for a given TUF role
func (m *TufMirror) makeRoleLayers(role TufRole, tufMetadata *TufMetadata) (*[]mutate.Addendum, error) {
layers := new([]mutate.Addendum)
// makeRoleLayers returns a list of layers for a given TUF role.
func (m *TUFMirror) makeRoleLayers(role TUFRole, tufMetadata *TUFMetadata) ([]mutate.Addendum, error) {
var layers []mutate.Addendum
ann := map[string]string{tufFileAnnotation: ""}
switch role {
case metadata.ROOT:
@@ -108,19 +125,19 @@ func (m *TufMirror) makeRoleLayers(role TufRole, tufMetadata *TufMetadata) (*[]m
layers = m.annotatedMetaLayers(tufMetadata.Targets)
case metadata.TIMESTAMP:
ann[tufFileAnnotation] = fmt.Sprintf("%s.json", role)
*layers = append(*layers, mutate.Addendum{Layer: static.NewLayer(tufMetadata.Timestamp, tufMetadataMediaType), Annotations: ann})
layers = append(layers, mutate.Addendum{Layer: static.NewLayer(tufMetadata.Timestamp, tufMetadataMediaType), Annotations: ann})
default:
return nil, fmt.Errorf("unsupported TUF role: %s", role)
}
return layers, nil
}
// annotatedMetaLayers returns a list of layers with annotations for each TUF metadata file
func (m *TufMirror) annotatedMetaLayers(meta map[string][]byte) *[]mutate.Addendum {
layers := new([]mutate.Addendum)
// annotatedMetaLayers returns a list of layers with annotations for each TUF metadata file.
func (m *TUFMirror) annotatedMetaLayers(meta map[string][]byte) []mutate.Addendum {
var layers []mutate.Addendum
for name, data := range meta {
ann := map[string]string{tufFileAnnotation: name}
*layers = append(*layers, mutate.Addendum{Layer: static.NewLayer(data, tufMetadataMediaType), Annotations: ann})
layers = append(layers, mutate.Addendum{Layer: static.NewLayer(data, tufMetadataMediaType), Annotations: ann})
}
return layers
}
@@ -129,8 +146,8 @@ func (m *TufMirror) annotatedMetaLayers(meta map[string][]byte) *[]mutate.Addend
// TUF delegated targets metadata
// ------------------------------
// GetDelegatedMetadataMirrors returns a list of mirrors (image/tag pairs) for each delegated targets role metadata
func (m *TufMirror) GetDelegatedMetadataMirrors() ([]*MirrorImage, error) {
// GetDelegatedMetadataMirrors returns a list of mirrors (image/tag pairs) for each delegated targets role metadata.
func (m *TUFMirror) GetDelegatedMetadataMirrors() ([]*Image, error) {
// get current delegated targets metadata
delegatedTargets, err := m.getDelegatedTargetsMetadata()
if err != nil {
@@ -143,12 +160,12 @@ func (m *TufMirror) GetDelegatedMetadataMirrors() ([]*MirrorImage, error) {
return mirror, nil
}
// getDelegatedTargetsMetadata returns delegated targets metadata as a list of DelegatedTargetMetadata (role name and data)
func (m *TufMirror) getDelegatedTargetsMetadata() (*[]DelegatedTargetMetadata, error) {
delegatedTargets := new([]DelegatedTargetMetadata)
md := m.TufClient.GetMetadata()
// getDelegatedTargetsMetadata returns delegated targets metadata as a list of DelegatedTargetMetadata (role name and data).
func (m *TUFMirror) getDelegatedTargetsMetadata() ([]DelegatedTargetMetadata, error) {
var delegatedTargets []DelegatedTargetMetadata
md := m.TUFClient.GetMetadata()
for _, role := range md.Targets[metadata.TARGETS].Signed.Delegations.Roles {
roleMetadata, err := m.TufClient.LoadDelegatedTargets(role.Name, metadata.TARGETS)
roleMetadata, err := m.TUFClient.LoadDelegatedTargets(role.Name, metadata.TARGETS)
if err != nil {
return nil, fmt.Errorf("failed to get delegated role metadata: %w", err)
}
@@ -165,15 +182,15 @@ func (m *TufMirror) getDelegatedTargetsMetadata() (*[]DelegatedTargetMetadata, e
if md.Root.Signed.ConsistentSnapshot {
version = strconv.FormatInt(meta.Version, 10)
}
*delegatedTargets = append(*delegatedTargets, DelegatedTargetMetadata{Name: role.Name, Version: version, Data: roleBytes})
delegatedTargets = append(delegatedTargets, DelegatedTargetMetadata{Name: role.Name, Version: version, Data: roleBytes})
}
return delegatedTargets, nil
}
// buildDelegatedMetadataManifests returns a list of mirrors (image/tag pairs) for each delegated target role metadata
func (m *TufMirror) buildDelegatedMetadataManifests(delegated *[]DelegatedTargetMetadata) ([]*MirrorImage, error) {
manifests := []*MirrorImage{}
for _, role := range *delegated {
// buildDelegatedMetadataManifests returns a list of mirrors (image/tag pairs) for each delegated target role metadata.
func (m *TUFMirror) buildDelegatedMetadataManifests(delegated []DelegatedTargetMetadata) ([]*Image, error) {
manifests := []*Image{}
for _, role := range delegated {
img := empty.Image
img = mutate.MediaType(img, types.OCIManifestSchema1)
img = mutate.ConfigMediaType(img, types.OCIConfigJSON)
@@ -183,7 +200,7 @@ func (m *TufMirror) buildDelegatedMetadataManifests(delegated *[]DelegatedTarget
if err != nil {
return nil, fmt.Errorf("failed to append delegated targets layer to image: %w", err)
}
manifests = append(manifests, &MirrorImage{Image: &img, Tag: role.Name})
manifests = append(manifests, &Image{Image: &oci.EmptyConfigImage{Image: img}, Tag: role.Name})
}
return manifests, nil
}

View File

@@ -1,6 +1,23 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mirror
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
@@ -9,22 +26,27 @@ import (
"strings"
"testing"
"github.com/docker/attest/internal/embed"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/tuf"
"github.com/stretchr/testify/assert"
"github.com/theupdateframework/go-tuf/v2/metadata"
)
const (
metadataPath = "/metadata"
targetsPath = "/targets"
)
func TestGetTufMetadataMirror(t *testing.T) {
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo"))))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "test", "testdata", "tuf", "test-repo"))))
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
m, err := NewTufMirror(embed.DevRoot, path, server.URL+"/metadata", server.URL+"/targets")
assert.Nil(t, err)
m, err := NewTUFMirror(context.Background(), tuf.DockerTUFRootDev.Data, path, server.URL+metadataPath, server.URL+targetsPath, tuf.NewMockVersionChecker())
assert.NoError(t, err)
tufMetadata, err := m.getTufMetadataMirror(server.URL + "/metadata")
assert.Nil(t, err)
tufMetadata, err := m.getMetadataMirror(server.URL + metadataPath)
assert.NoError(t, err)
// check that all roles are not empty
assert.Greater(t, len(tufMetadata.Root), 0)
@@ -34,20 +56,19 @@ func TestGetTufMetadataMirror(t *testing.T) {
}
func TestGetMetadataManifest(t *testing.T) {
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo"))))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "test", "testdata", "tuf", "test-repo"))))
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
m, err := NewTufMirror(embed.DevRoot, path, server.URL+"/metadata", server.URL+"/targets")
assert.Nil(t, err)
m, err := NewTUFMirror(context.Background(), tuf.DockerTUFRootDev.Data, path, server.URL+metadataPath, server.URL+targetsPath, tuf.NewMockVersionChecker())
assert.NoError(t, err)
img, err := m.GetMetadataManifest(server.URL + "/metadata")
assert.Nil(t, err)
img, err := m.GetMetadataManifest(server.URL + metadataPath)
assert.NoError(t, err)
assert.NotNil(t, img)
image := *img
mf, err := image.RawManifest()
assert.Nil(t, err)
mf, err := img.RawManifest()
assert.NoError(t, err)
type Annotations struct {
Annotations map[string]string `json:"annotations"`
@@ -57,7 +78,7 @@ func TestGetMetadataManifest(t *testing.T) {
}
l := &Layers{}
err = json.Unmarshal(mf, l)
assert.Nil(t, err)
assert.NoError(t, err)
// check that layers are annotated and use consistent snapshot naming
for _, layer := range l.Layers {
@@ -69,20 +90,20 @@ func TestGetMetadataManifest(t *testing.T) {
continue
}
_, err := strconv.Atoi(parts[0])
assert.Nil(t, err)
assert.NoError(t, err)
}
}
func TestGetDelegatedMetadataMirrors(t *testing.T) {
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo"))))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "test", "testdata", "tuf", "test-repo"))))
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
m, err := NewTufMirror(embed.DevRoot, path, server.URL+"/metadata", server.URL+"/targets")
assert.Nil(t, err)
m, err := NewTUFMirror(context.Background(), tuf.DockerTUFRootDev.Data, path, server.URL+metadataPath, server.URL+targetsPath, tuf.NewMockVersionChecker())
assert.NoError(t, err)
delegations, err := m.GetDelegatedMetadataMirrors()
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, delegations)
assert.Greater(t, len(delegations), 0)

35
mirror/mirror.go Normal file
View File

@@ -0,0 +1,35 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mirror
import (
"context"
"fmt"
"github.com/docker/attest/tuf"
)
func NewTUFMirror(ctx context.Context, root []byte, tufPath, metadataURL, targetsURL string, versionChecker tuf.VersionChecker) (*TUFMirror, error) {
if root == nil {
root = tuf.DockerTUFRootDefault.Data
}
tufClient, err := tuf.NewClient(ctx, &tuf.ClientOptions{InitialRoot: root, LocalStorageDir: tufPath, MetadataSource: metadataURL, TargetsSource: targetsURL, VersionChecker: versionChecker})
if err != nil {
return nil, fmt.Errorf("failed to create TUF client: %w", err)
}
return &TUFMirror{TUFClient: tufClient, tufPath: tufPath, metadataURL: metadataURL, targetsURL: targetsURL}, nil
}

View File

@@ -1,3 +1,19 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mirror
import (
@@ -5,6 +21,7 @@ import (
"path/filepath"
"strings"
"github.com/docker/attest/oci"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/mutate"
@@ -13,16 +30,16 @@ import (
"github.com/theupdateframework/go-tuf/v2/metadata"
)
// GetTufTargetMirrors returns a list of top-level target files as MirrorImages (image with tag)
func (m *TufMirror) GetTufTargetMirrors() ([]*MirrorImage, error) {
targetMirrors := []*MirrorImage{}
md := m.TufClient.GetMetadata()
// GetTUFTargetMirrors returns a list of top-level target files as MirrorImages (image with tag).
func (m *TUFMirror) GetTUFTargetMirrors() ([]*Image, error) {
targetMirrors := []*Image{}
md := m.TUFClient.GetMetadata()
// for each top-level target file, create an image with the target file as a layer
targets := md.Targets[metadata.TARGETS].Signed.Targets
for _, t := range targets {
// download target file
_, data, err := m.TufClient.DownloadTarget(t.Path, filepath.Join(m.tufPath, "download"))
file, err := m.TUFClient.DownloadTarget(t.Path, filepath.Join(m.tufPath, "download"))
if err != nil {
return nil, fmt.Errorf("failed to download target %s: %w", t.Path, err)
}
@@ -35,23 +52,23 @@ func (m *TufMirror) GetTufTargetMirrors() ([]*MirrorImage, error) {
if !ok {
return nil, fmt.Errorf("missing sha256 hash for target %s", t.Path)
}
name := strings.Join([]string{hash.String(), t.Path}, ".")
name := hash.String() + "." + t.Path
ann := map[string]string{tufFileAnnotation: name}
layer := mutate.Addendum{Layer: static.NewLayer(data, tufTargetMediaType), Annotations: ann}
layer := mutate.Addendum{Layer: static.NewLayer(file.Data, tufTargetMediaType), Annotations: ann}
img, err = mutate.Append(img, layer)
if err != nil {
return nil, fmt.Errorf("failed to append role layer to image: %w", err)
}
targetMirrors = append(targetMirrors, &MirrorImage{Image: &img, Tag: name})
targetMirrors = append(targetMirrors, &Image{Image: &oci.EmptyConfigImage{Image: img}, Tag: name})
}
return targetMirrors, nil
}
// GetDelegatedTargetMirrors returns a list of delegated target files as MirrorIndexes (image index with tag)
// each image in the index contains a delegated target file
func (m *TufMirror) GetDelegatedTargetMirrors() ([]*MirrorIndex, error) {
mirror := []*MirrorIndex{}
md := m.TufClient.GetMetadata()
// each image in the index contains a delegated target file.
func (m *TUFMirror) GetDelegatedTargetMirrors() ([]*Index, error) {
mirror := []*Index{}
md := m.TUFClient.GetMetadata()
// for each delegated role, create an image index with target files as images
roles := md.Targets[metadata.TARGETS].Signed.Delegations.Roles
@@ -60,7 +77,7 @@ func (m *TufMirror) GetDelegatedTargetMirrors() ([]*MirrorIndex, error) {
index := v1.ImageIndex(empty.Index)
// get delegated targets metadata for role
roleMeta, err := m.TufClient.LoadDelegatedTargets(role.Name, metadata.TARGETS)
roleMeta, err := m.TUFClient.LoadDelegatedTargets(role.Name, metadata.TARGETS)
if err != nil {
return nil, fmt.Errorf("failed to load delegated targets metadata: %w", err)
}
@@ -68,7 +85,7 @@ func (m *TufMirror) GetDelegatedTargetMirrors() ([]*MirrorIndex, error) {
// for each target file, create an image with the target file as a layer
for _, target := range roleMeta.Signed.Targets {
// download target file
_, data, err := m.TufClient.DownloadTarget(target.Path, filepath.Join(m.tufPath, "download"))
file, err := m.TUFClient.DownloadTarget(target.Path, filepath.Join(m.tufPath, "download"))
if err != nil {
return nil, fmt.Errorf("failed to download target %s: %w", target.Path, err)
}
@@ -86,16 +103,17 @@ func (m *TufMirror) GetDelegatedTargetMirrors() ([]*MirrorIndex, error) {
if !ok {
return nil, fmt.Errorf("failed to find target subdirectory [%s] in path: %s", subdir, target.Path)
}
name := strings.Join([]string{hash.String(), filename}, ".")
name := hash.String() + "." + filename
ann := map[string]string{tufFileAnnotation: name}
layer := mutate.Addendum{Layer: static.NewLayer(data, tufTargetMediaType), Annotations: ann}
layer := mutate.Addendum{Layer: static.NewLayer(file.Data, tufTargetMediaType), Annotations: ann}
img, err = mutate.Append(img, layer)
if err != nil {
return nil, fmt.Errorf("failed to append role layer to image: %w", err)
}
emptyConfigImage := &oci.EmptyConfigImage{Image: img}
// append image to index with annotation
index = mutate.AppendManifests(index, mutate.IndexAddendum{
Add: img,
Add: emptyConfigImage,
Descriptor: v1.Descriptor{
Annotations: map[string]string{
tufFileAnnotation: fmt.Sprintf("%s/%s", subdir, name),
@@ -103,7 +121,7 @@ func (m *TufMirror) GetDelegatedTargetMirrors() ([]*MirrorIndex, error) {
},
})
}
mirror = append(mirror, &MirrorIndex{Index: &index, Tag: role.Name})
mirror = append(mirror, &Index{Index: index, Tag: role.Name})
}
return mirror, nil
}

View File

@@ -1,6 +1,23 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mirror
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
@@ -8,8 +25,8 @@ import (
"strings"
"testing"
"github.com/docker/attest/internal/embed"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/tuf"
"github.com/stretchr/testify/assert"
)
@@ -22,82 +39,82 @@ type Layers struct {
}
func TestGetTufTargetsMirror(t *testing.T) {
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo"))))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "test", "testdata", "tuf", "test-repo"))))
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
m, err := NewTufMirror(embed.DevRoot, path, server.URL+"/metadata", server.URL+"/targets")
assert.Nil(t, err)
m, err := NewTUFMirror(context.Background(), tuf.DockerTUFRootDev.Data, path, server.URL+metadataPath, server.URL+targetsPath, tuf.NewMockVersionChecker())
assert.NoError(t, err)
targets, err := m.GetTufTargetMirrors()
assert.Nil(t, err)
targets, err := m.GetTUFTargetMirrors()
assert.NoError(t, err)
assert.Greater(t, len(targets), 0)
// check for image layer annotations
for _, target := range targets {
img := *target.Image
img := target.Image
mf, err := img.RawManifest()
assert.Nil(t, err)
assert.NoError(t, err)
// unmarshal manifest with annotations
l := &Layers{}
err = json.Unmarshal(mf, l)
assert.Nil(t, err)
assert.NoError(t, err)
// check that layers are annotated
for _, layer := range l.Layers {
ann, ok := layer.Annotations[tufFileAnnotation]
assert.True(t, ok)
parts := strings.Split(ann, ".")
// <digest>.filename.json
assert.Equal(t, len(parts), 3)
// <digest>.filename.<ext|optional>
assert.GreaterOrEqual(t, len(parts), 2)
}
}
}
func TestTargetDelegationMetadata(t *testing.T) {
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo"))))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "test", "testdata", "tuf", "test-repo"))))
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
tm, err := NewTufMirror(embed.DevRoot, path, server.URL+"/metadata", server.URL+"/targets")
assert.Nil(t, err)
tm, err := NewTUFMirror(context.Background(), tuf.DockerTUFRootDev.Data, path, server.URL+metadataPath, server.URL+targetsPath, tuf.NewMockVersionChecker())
assert.NoError(t, err)
targets, err := tm.TufClient.LoadDelegatedTargets("test-role", "targets")
assert.Nil(t, err)
targets, err := tm.TUFClient.LoadDelegatedTargets("test-role", "targets")
assert.NoError(t, err)
assert.Greater(t, len(targets.Signed.Targets), 0)
}
func TestGetDelegatedTargetMirrors(t *testing.T) {
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "..", "test", "testdata", "tuf", "test-repo"))))
server := httptest.NewServer(http.FileServer(http.Dir(filepath.Join("..", "test", "testdata", "tuf", "test-repo"))))
defer server.Close()
path := test.CreateTempDir(t, "", "tuf_temp")
m, err := NewTufMirror(embed.DevRoot, path, server.URL+"/metadata", server.URL+"/targets")
assert.Nil(t, err)
m, err := NewTUFMirror(context.Background(), tuf.DockerTUFRootDev.Data, path, server.URL+metadataPath, server.URL+targetsPath, tuf.NewMockVersionChecker())
assert.NoError(t, err)
mirrors, err := m.GetDelegatedTargetMirrors()
assert.Nil(t, err)
assert.NoError(t, err)
assert.Greater(t, len(mirrors), 0)
// check for index image annotations
for _, mirror := range mirrors {
idx := *mirror.Index
idx := mirror.Index
mf, err := idx.RawManifest()
assert.Nil(t, err)
assert.NoError(t, err)
// unmarshal manifest with annotations
l := &Layers{}
err = json.Unmarshal(mf, l)
assert.Nil(t, err)
assert.NoError(t, err)
// check that layers are annotated
for _, layer := range l.Layers {
ann, ok := layer.Annotations[tufFileAnnotation]
assert.True(t, ok)
parts := strings.Split(ann, ".")
// <subdir>/<digest>.filename.json
assert.Equal(t, len(parts), 3)
// <subdir>/<digest>.filename.<ext|optional>
assert.GreaterOrEqual(t, len(parts), 2)
}
}
}

66
mirror/types.go Normal file
View File

@@ -0,0 +1,66 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mirror
import (
"github.com/docker/attest/oci"
"github.com/docker/attest/tuf"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/theupdateframework/go-tuf/v2/metadata"
)
const (
DefaultMetadataURL = "https://docker.github.io/tuf/metadata"
DefaultTargetsURL = "https://docker.github.io/tuf/targets"
tufMetadataMediaType = "application/vnd.tuf.metadata+json"
tufTargetMediaType = "application/vnd.tuf.target"
tufFileAnnotation = "tuf.io/filename"
)
type TUFRole string
var TUFRoles = []TUFRole{metadata.ROOT, metadata.SNAPSHOT, metadata.TARGETS, metadata.TIMESTAMP}
type TUFMetadata struct {
Root map[string][]byte
Snapshot map[string][]byte
Targets map[string][]byte
Timestamp []byte
}
type DelegatedTargetMetadata struct {
Name string
Version string
Data []byte
}
type Image struct {
Image *oci.EmptyConfigImage
Tag string
}
type Index struct {
Index v1.ImageIndex
Tag string
}
type TUFMirror struct {
TUFClient *tuf.Client
tufPath string
metadataURL string
targetsURL string
}

2
oci/README.md Normal file
View File

@@ -0,0 +1,2 @@
## oci
This package is for generic OCI components. For attestation specific components see the `attestation` package.

39
oci/authn.go Normal file
View File

@@ -0,0 +1,39 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oci
import (
"io"
ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/v1/google"
"github.com/google/go-containerregistry/pkg/v1/remote"
)
func MultiKeychainOption() remote.Option {
return remote.WithAuthFromKeychain(MultiKeychainAll())
}
func MultiKeychainAll() authn.Keychain {
// Create a multi-keychain that will use the default Docker, Google, or ECR keychain
return authn.NewMultiKeychain(
authn.DefaultKeychain,
google.Keychain,
authn.NewKeychainFromHelper(ecr.NewECRHelper(ecr.WithLogger(io.Discard))),
)
}

49
oci/authn_test.go Normal file
View File

@@ -0,0 +1,49 @@
//go:build e2e
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oci_test
import (
"context"
"testing"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/oci"
"github.com/stretchr/testify/require"
)
func TestRegistryAuth(t *testing.T) {
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
// test cases for ecr, gcr and dockerhub
testCases := []struct {
Image string
}{
{Image: "175142243308.dkr.ecr.us-east-1.amazonaws.com/e2e-test-image:latest"},
{Image: "docker/image-signer-verifier-test:latest"},
}
ctx := context.Background()
for _, tc := range testCases {
t.Run(tc.Image, func(t *testing.T) {
err := oci.PushIndexToRegistry(ctx, attIdx.Index, tc.Image)
require.NoError(t, err)
_, err = oci.IndexFromRemote(ctx, tc.Image)
require.NoError(t, err)
})
}
}

170
oci/oci.go Normal file
View File

@@ -0,0 +1,170 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oci
import (
"context"
"fmt"
"strings"
"github.com/containerd/platforms"
"github.com/distribution/reference"
"github.com/docker/attest/useragent"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/package-url/packageurl-go"
)
// ParsePlatform parses the provided platform string or attempts to obtain
// the platform of the current host system.
func ParsePlatform(platformStr string) (*v1.Platform, error) {
if platformStr == "" {
cdp := platforms.Normalize(platforms.DefaultSpec())
if cdp.OS != "windows" {
cdp.OS = "linux"
}
return &v1.Platform{
OS: cdp.OS,
Architecture: cdp.Architecture,
Variant: cdp.Variant,
}, nil
}
return v1.ParsePlatform(platformStr)
}
func WithOptions(ctx context.Context, platform *v1.Platform) []remote.Option {
// prepare options
options := []remote.Option{
MultiKeychainOption(),
remote.WithContext(ctx),
remote.WithUserAgent(useragent.Get(ctx)),
}
// add in platform into remote Get operation; this might conflict with an explicit digest, but we are trying anyway
if platform != nil {
options = append(options, remote.WithPlatform(*platform))
}
return options
}
func ImageDescriptor(ix *v1.IndexManifest, platform *v1.Platform) (*v1.Descriptor, error) {
for i := range ix.Manifests {
m := &ix.Manifests[i]
if (m.MediaType == ocispec.MediaTypeImageManifest || m.MediaType == "application/vnd.docker.distribution.manifest.v2+json") && m.Platform.Equals(*platform) {
return m, nil
}
}
return nil, fmt.Errorf("no image found for platform %v", platform)
}
func RefToPURL(named reference.Named, platform *v1.Platform) (string, bool, error) {
var isCanonical bool
var qualifiers []packageurl.Qualifier
if canonical, ok := named.(reference.Canonical); ok {
qualifiers = append(qualifiers, packageurl.Qualifier{
Key: "digest",
Value: canonical.Digest().String(),
})
isCanonical = true
} else {
named = reference.TagNameOnly(named)
}
version := ""
if tagged, ok := named.(reference.Tagged); ok {
version = tagged.Tag()
}
name := reference.FamiliarName(named)
ns := ""
parts := strings.Split(name, "/")
if len(parts) > 1 {
ns = strings.Join(parts[:len(parts)-1], "/")
}
name = parts[len(parts)-1]
if platform != nil {
qualifiers = append(qualifiers, packageurl.Qualifier{
Key: "platform",
Value: platform.String(),
})
}
p := packageurl.NewPackageURL(packageurl.TypeDocker, ns, name, version, qualifiers, "")
return p.ToString(), isCanonical, nil
}
func SplitDigest(digest string) (common.DigestSet, error) {
parts := strings.SplitN(digest, ":", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("invalid digest %q", digest)
}
return common.DigestSet{
parts[0]: parts[1],
}, nil
}
func ReplaceTagInSpec(src *ImageSpec, digest v1.Hash) (*ImageSpec, error) {
newName, err := ReplaceTag(src.Identifier, digest)
if err != nil {
return nil, fmt.Errorf("failed to parse repo name: %w", err)
}
return &ImageSpec{
Identifier: newName,
Type: src.Type,
Platform: src.Platform,
}, nil
}
// so that the index tag is replaced with a tag unique to the image digest and doesn't overwrite it.
func ReplaceTag(image string, digest v1.Hash) (string, error) {
if strings.HasPrefix(image, LocalPrefix) {
return image, nil
}
notag, err := WithoutTag(image)
if err != nil {
return "", nil
}
return fmt.Sprintf("%s:%s-%s.att", notag, digest.Algorithm, digest.Hex), nil
}
func ReplaceDigestInSpec(src *ImageSpec, digest v1.Hash) (*ImageSpec, error) {
newName, err := replaceDigest(src.Identifier, digest)
if err != nil {
return nil, fmt.Errorf("failed to parse repo name: %w", err)
}
return &ImageSpec{
Identifier: newName,
Type: src.Type,
Platform: src.Platform,
}, nil
}
func replaceDigest(image string, digest v1.Hash) (string, error) {
if strings.HasPrefix(image, LocalPrefix) {
return image, nil
}
notag, err := WithoutTag(image)
if err != nil {
return "", nil
}
return fmt.Sprintf("%s@%s:%s", notag, digest.Algorithm, digest.Hex), nil
}

162
oci/oci_test.go Normal file
View File

@@ -0,0 +1,162 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oci_test
import (
"testing"
"github.com/distribution/reference"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/oci"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRefToPurl(t *testing.T) {
arm, err := oci.ParsePlatform("arm64/linux")
require.NoError(t, err)
ref, err := reference.ParseNormalizedNamed("alpine")
require.NoError(t, err)
purl, canonical, err := oci.RefToPURL(ref, arm)
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/alpine@latest?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
ref, err = reference.ParseNormalizedNamed("alpine:123")
require.NoError(t, err)
purl, canonical, err = oci.RefToPURL(ref, arm)
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
ref, err = reference.ParseNormalizedNamed("google/alpine:123")
require.NoError(t, err)
purl, canonical, err = oci.RefToPURL(ref, arm)
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/google/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
ref, err = reference.ParseNormalizedNamed("library/alpine:123")
require.NoError(t, err)
purl, canonical, err = oci.RefToPURL(ref, arm)
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
ref, err = reference.ParseNormalizedNamed("docker.io/library/alpine:123")
require.NoError(t, err)
purl, canonical, err = oci.RefToPURL(ref, arm)
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
ref, err = reference.ParseNormalizedNamed("localhost:5001/library/alpine:123")
require.NoError(t, err)
purl, canonical, err = oci.RefToPURL(ref, arm)
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/localhost%3A5001/library/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
ref, err = reference.ParseNormalizedNamed("localhost:5001/alpine:123")
require.NoError(t, err)
purl, canonical, err = oci.RefToPURL(ref, arm)
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/localhost%3A5001/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
ref, err = reference.ParseNormalizedNamed("localhost:5001/alpine@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b")
require.NoError(t, err)
purl, canonical, err = oci.RefToPURL(ref, arm)
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/localhost%3A5001/alpine?digest=sha256%3Ac5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b&platform=arm64%2Flinux", purl)
assert.True(t, canonical)
}
// Test fix for https://github.com/docker/secure-artifacts-team-issues/issues/202
func TestImageDigestForPlatform(t *testing.T) {
idx, err := layout.ImageIndexFromPath(test.UnsignedTestIndex(".."))
assert.NoError(t, err)
idxm, err := idx.IndexManifest()
assert.NoError(t, err)
idxDescriptor := idxm.Manifests[0]
idxDigest := idxDescriptor.Digest
mfs, err := idx.ImageIndex(idxDigest)
assert.NoError(t, err)
mfs2, err := mfs.IndexManifest()
assert.NoError(t, err)
p, err := oci.ParsePlatform("linux/amd64")
assert.NoError(t, err)
desc, err := oci.ImageDescriptor(mfs2, p)
assert.NoError(t, err)
digest := desc.Digest.String()
assert.Equal(t, test.UnsignedLinuxAMD64ImageDigest, digest)
p, err = oci.ParsePlatform("linux/arm64")
assert.NoError(t, err)
desc, err = oci.ImageDescriptor(mfs2, p)
assert.NoError(t, err)
digest = desc.Digest.String()
assert.Equal(t, test.UnsignedLinuxArm64ImageDigest, digest)
}
func TestWithoutTag(t *testing.T) {
tc := []struct {
name string
expected string
}{
{name: "image:tag", expected: "index.docker.io/library/image"},
{name: "image", expected: "index.docker.io/library/image"},
{name: "image:sha256-digest.att", expected: "index.docker.io/library/image"},
{name: oci.RegistryPrefix + "image:tag", expected: oci.RegistryPrefix + "index.docker.io/library/image"},
{name: "image@sha256:166710df254975d4a6c4c407c315951c22753dcaa829e020a3fd5d18fff70dd2", expected: "index.docker.io/library/image"},
{name: oci.RegistryPrefix + "image@sha256:166710df254975d4a6c4c407c315951c22753dcaa829e020a3fd5d18fff70dd2", expected: oci.RegistryPrefix + "index.docker.io/library/image"},
{name: oci.RegistryPrefix + "127.0.0.1:36555/repo:latest", expected: oci.RegistryPrefix + "127.0.0.1:36555/repo"},
}
for _, c := range tc {
t.Run(c.name, func(t *testing.T) {
notag, _ := oci.WithoutTag(c.name)
assert.Equal(t, c.expected, notag)
})
}
}
func TestReplaceTag(t *testing.T) {
tc := []struct {
name string
expected string
}{
{name: "image:tag", expected: "index.docker.io/library/image:sha256-digest.att"},
{name: "image", expected: "index.docker.io/library/image:sha256-digest.att"},
{name: "image:sha256-digest.att", expected: "index.docker.io/library/image:sha256-digest.att"},
{name: oci.RegistryPrefix + "image:tag", expected: oci.RegistryPrefix + "index.docker.io/library/image:sha256-digest.att"},
{name: "image@sha256:166710df254975d4a6c4c407c315951c22753dcaa829e020a3fd5d18fff70dd2", expected: "index.docker.io/library/image:sha256-digest.att"},
{name: oci.LocalPrefix + "foobar", expected: oci.LocalPrefix + "foobar"},
{name: oci.RegistryPrefix + "image@sha256:166710df254975d4a6c4c407c315951c22753dcaa829e020a3fd5d18fff70dd2", expected: oci.RegistryPrefix + "index.docker.io/library/image:sha256-digest.att"},
{name: oci.RegistryPrefix + "127.0.0.1:36555/repo:latest", expected: oci.RegistryPrefix + "127.0.0.1:36555/repo:sha256-digest.att"},
}
digest := v1.Hash{
Algorithm: "sha256",
Hex: "digest",
}
for _, c := range tc {
t.Run(c.name, func(t *testing.T) {
replaced, err := oci.ReplaceTag(c.name, digest)
require.NoError(t, err)
assert.Equal(t, c.expected, replaced)
})
}
}

161
oci/output.go Normal file
View File

@@ -0,0 +1,161 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oci
import (
"context"
"fmt"
"os"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/remote"
)
// PushImageToRegistry pushes an image to the registry with the specified name.
func PushImageToRegistry(ctx context.Context, image v1.Image, imageName string) error {
ref, err := name.ParseReference(imageName)
if err != nil {
return fmt.Errorf("Failed to parse image name '%s': %w", imageName, err)
}
// Push the image to the registry
return remote.Write(ref, image, WithOptions(ctx, nil)...)
}
// PushIndexToRegistry pushes an index to the registry with the specified name.
func PushIndexToRegistry(ctx context.Context, index v1.ImageIndex, imageName string) error {
// Parse the index name
ref, err := name.ParseReference(imageName)
if err != nil {
return fmt.Errorf("Failed to parse image name: %w", err)
}
// Push the index to the registry
return remote.WriteIndex(ref, index, WithOptions(ctx, nil)...)
}
// SaveIndexAsOCILayout saves an image as an OCI layout to the specified path.
func SaveImageAsOCILayout(image v1.Image, path string) error {
// Save the image to the local filesystem
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
index := empty.Index
l, err := layout.Write(path, index)
if err != nil {
return fmt.Errorf("failed to create index: %w", err)
}
return l.AppendImage(image)
}
// SaveIndexAsOCILayout saves an index as an OCI layout to the specified path.
func SaveIndexAsOCILayout(image v1.ImageIndex, path string) error {
// Save the index to the local filesystem
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
_, err = layout.Write(path, image)
if err != nil {
return fmt.Errorf("failed to create index: %w", err)
}
return nil
}
// SaveIndex saves an index to the specified outputs.
func SaveIndex(ctx context.Context, outputs []*ImageSpec, index v1.ImageIndex, indexName string) error {
// split output by comma and write or push each one
for _, output := range outputs {
if output.Type == OCI {
idx := v1.ImageIndex(empty.Index)
idx = mutate.AppendManifests(idx, mutate.IndexAddendum{
Add: index,
Descriptor: v1.Descriptor{
Annotations: map[string]string{
OCIReferenceTarget: indexName,
},
},
})
err := SaveIndexAsOCILayout(idx, output.Identifier)
if err != nil {
return fmt.Errorf("failed to write signed image: %w", err)
}
} else {
err := PushIndexToRegistry(ctx, index, output.Identifier)
if err != nil {
return fmt.Errorf("failed to push signed image: %w", err)
}
}
}
return nil
}
// SaveImage saves an image to the specified output.
func SaveImage(ctx context.Context, output *ImageSpec, image v1.Image, imageName string) error {
if output.Type == OCI {
idx := v1.ImageIndex(empty.Index)
idx = mutate.AppendManifests(idx, mutate.IndexAddendum{
Add: image,
Descriptor: v1.Descriptor{
Annotations: map[string]string{
OCIReferenceTarget: imageName,
},
},
})
err := SaveIndexAsOCILayout(idx, output.Identifier)
if err != nil {
return fmt.Errorf("failed to write signed image: %w", err)
}
} else {
err := PushImageToRegistry(ctx, image, output.Identifier)
if err != nil {
return fmt.Errorf("failed to push signed image: %w", err)
}
}
return nil
}
// SaveImagesNoTag saves a list of images by digest to the specified outputs.
func SaveImagesNoTag(ctx context.Context, images []v1.Image, outputs []*ImageSpec) error {
for _, output := range outputs {
// OCI layout output not supported
if output.Type == OCI {
continue
}
for _, image := range images {
digest, err := image.Digest()
if err != nil {
return fmt.Errorf("failed to get image digest: %w", err)
}
spec, err := ReplaceDigestInSpec(output, digest)
if err != nil {
return fmt.Errorf("failed to create image spec: %w", err)
}
err = PushImageToRegistry(ctx, image, spec.Identifier)
if err != nil {
return fmt.Errorf("failed to push image: %w", err)
}
}
}
return nil
}

127
oci/output_test.go Normal file
View File

@@ -0,0 +1,127 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oci_test
import (
"context"
"fmt"
"net/url"
"testing"
"github.com/docker/attest/attestation"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/oci"
"github.com/google/go-containerregistry/pkg/registry"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/stretchr/testify/require"
)
func TestSavingIndex(t *testing.T) {
outputLayout := test.CreateTempDir(t, "", "mirror-test")
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
ctx := context.Background()
regServer := test.NewLocalRegistry(ctx)
defer regServer.Close()
u, err := url.Parse(regServer.URL)
require.NoError(t, err)
indexName := fmt.Sprintf("%s/repo:root", u.Host)
output, err := oci.ParseImageSpecs(indexName)
require.NoError(t, err)
err = oci.SaveIndex(ctx, output, attIdx.Index, indexName)
require.NoError(t, err)
ociOutput, err := oci.ParseImageSpecs(oci.LocalPrefix + outputLayout)
require.NoError(t, err)
err = oci.SaveIndex(ctx, ociOutput, attIdx.Index, indexName)
require.NoError(t, err)
}
func TestSavingImage(t *testing.T) {
outputLayout := test.CreateTempDir(t, "", "mirror-test")
img := empty.Image
ctx := context.Background()
regServer := test.NewLocalRegistry(ctx)
defer regServer.Close()
u, err := url.Parse(regServer.URL)
require.NoError(t, err)
indexName := fmt.Sprintf("%s/repo:root", u.Host)
output, err := oci.ParseImageSpec(indexName)
require.NoError(t, err)
err = oci.SaveImage(ctx, output, img, indexName)
require.NoError(t, err)
ociOutput, err := oci.ParseImageSpec(oci.LocalPrefix + outputLayout)
require.NoError(t, err)
err = oci.SaveImage(ctx, ociOutput, img, indexName)
require.NoError(t, err)
}
func TestSavingReferrers(t *testing.T) {
ctx, signer := test.Setup(t)
opts := &attestation.SigningOptions{}
statement := &intoto.Statement{
StatementHeader: intoto.StatementHeader{
PredicateType: attestation.VSAPredicateType,
},
}
digest, err := v1.NewHash(test.UnsignedLinuxAMD64ImageDigest)
require.NoError(t, err)
subject := &v1.Descriptor{
MediaType: "application/vnd.oci.image.manifest.v1+json",
Digest: digest,
}
manifest, err := attestation.NewManifest(subject)
require.NoError(t, err)
err = manifest.Add(ctx, signer, statement, opts)
require.NoError(t, err)
regServer := test.NewLocalRegistry(ctx, registry.WithReferrersSupport(true))
defer regServer.Close()
u, err := url.Parse(regServer.URL)
require.NoError(t, err)
indexName := fmt.Sprintf("%s/repo:root", u.Host)
output, err := oci.ParseImageSpecs(indexName)
require.NoError(t, err)
artifacts, err := manifest.BuildReferringArtifacts()
require.NoError(t, err)
err = oci.SaveImagesNoTag(ctx, artifacts, output)
require.NoError(t, err)
reg := &attestation.MockRegistryResolver{
Subject: subject,
MockResolver: &attestation.MockResolver{},
ImageNameStr: indexName,
}
require.NoError(t, err)
refResolver, err := attestation.NewReferrersResolver(reg)
require.NoError(t, err)
attestations, err := refResolver.Attestations(ctx, attestation.VSAPredicateType)
require.NoError(t, err)
require.Len(t, attestations, 1)
}

80
oci/registry.go Normal file
View File

@@ -0,0 +1,80 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oci
import (
"context"
"fmt"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
)
// ensure RegistryImageDetailsResolver implements ImageDetailsResolver.
var _ ImageDetailsResolver = &RegistryImageDetailsResolver{}
type RegistryImageDetailsResolver struct {
*ImageSpec
descriptor *v1.Descriptor
}
func NewRegistryImageDetailsResolver(src *ImageSpec) (*RegistryImageDetailsResolver, error) {
return &RegistryImageDetailsResolver{
ImageSpec: src,
}, nil
}
func (r *RegistryImageDetailsResolver) ImageName(_ context.Context) (string, error) {
return r.Identifier, nil
}
func (r *RegistryImageDetailsResolver) ImagePlatform(_ context.Context) (*v1.Platform, error) {
return r.Platform, nil
}
func (r *RegistryImageDetailsResolver) ImageDescriptor(ctx context.Context) (*v1.Descriptor, error) {
if r.descriptor == nil {
subjectRef, err := name.ParseReference(r.Identifier)
if err != nil {
return nil, fmt.Errorf("failed to parse reference: %w", err)
}
options := WithOptions(ctx, r.Platform)
image, err := remote.Image(subjectRef, options...)
if err != nil {
return nil, fmt.Errorf("failed to get image manifest: %w", err)
}
digest, err := image.Digest()
if err != nil {
return nil, fmt.Errorf("failed to get image digest: %w", err)
}
size, err := image.Size()
if err != nil {
return nil, fmt.Errorf("failed to get image size: %w", err)
}
mediaType, err := image.MediaType()
if err != nil {
return nil, fmt.Errorf("failed to get image media type: %w", err)
}
r.descriptor = &v1.Descriptor{
Digest: digest,
Size: size,
MediaType: mediaType,
}
}
return r.descriptor, nil
}

29
oci/resolver.go Normal file
View File

@@ -0,0 +1,29 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oci
import (
"context"
v1 "github.com/google/go-containerregistry/pkg/v1"
)
type ImageDetailsResolver interface {
ImageName(ctx context.Context) (string, error)
ImagePlatform(ctx context.Context) (*v1.Platform, error)
ImageDescriptor(ctx context.Context) (*v1.Descriptor, error)
}

234
oci/types.go Normal file
View File

@@ -0,0 +1,234 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oci
import (
"bytes"
"context"
"encoding/json"
"fmt"
"strings"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/google/go-containerregistry/pkg/v1/remote"
)
const (
OCIReferenceTarget = "org.opencontainers.image.ref.name"
LocalPrefix = "oci://"
RegistryPrefix = "docker://"
OCI SourceType = "OCI"
Docker SourceType = "Docker"
)
type (
SourceType string
NamedIndex struct {
Index v1.ImageIndex
Name string
}
)
type ImageSpecOption func(*ImageSpec) error
type ImageSpec struct {
// OCI or Docker
Type SourceType
// without oci:// or docker:// (name or path)
Identifier string
Platform *v1.Platform
}
func IndexFromPath(path string) (*NamedIndex, error) {
wrapperIdx, err := layout.ImageIndexFromPath(path)
if err != nil {
return nil, fmt.Errorf("failed to load image index: %w", err)
}
idxm, err := wrapperIdx.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to get digest: %w", err)
}
imageName := idxm.Manifests[0].Annotations[OCIReferenceTarget]
idxDigest := idxm.Manifests[0].Digest
idx, err := wrapperIdx.ImageIndex(idxDigest)
if err != nil {
return nil, fmt.Errorf("failed to extract ImageIndex for digest %s: %w", idxDigest.String(), err)
}
return &NamedIndex{
Index: idx,
Name: imageName,
}, nil
}
func IndexFromRemote(ctx context.Context, image string) (*NamedIndex, error) {
ref, err := name.ParseReference(image)
if err != nil {
return nil, fmt.Errorf("failed to parse image reference %s: %w", image, err)
}
// Pull the image from the registry
idx, err := remote.Index(ref, WithOptions(ctx, nil)...)
if err != nil {
return nil, fmt.Errorf("failed to pull image %s: %w", image, err)
}
return &NamedIndex{
Index: idx,
Name: image,
}, nil
}
func LoadIndex(ctx context.Context, input *ImageSpec) (*NamedIndex, error) {
if input.Type == OCI {
return IndexFromPath(input.Identifier)
}
return IndexFromRemote(ctx, input.Identifier)
}
func (i *ImageSpec) ForPlatforms(platform string) ([]*ImageSpec, error) {
platforms := strings.Split(platform, ",")
var specs []*ImageSpec
for _, pStr := range platforms {
p, err := ParsePlatform(pStr)
if err != nil {
return nil, err
}
spec := &ImageSpec{
Type: i.Type,
Identifier: i.Identifier,
Platform: p,
}
specs = append(specs, spec)
}
return specs, nil
}
func ParseImageSpec(img string, options ...ImageSpecOption) (*ImageSpec, error) {
img = strings.TrimSpace(img)
if strings.Contains(img, ",") {
return nil, fmt.Errorf("only one image is supported")
}
withoutPrefix := strings.TrimPrefix(strings.TrimPrefix(img, LocalPrefix), RegistryPrefix)
src := &ImageSpec{
Identifier: withoutPrefix,
}
if strings.HasPrefix(img, LocalPrefix) {
src.Type = OCI
} else {
src.Type = Docker
}
for _, option := range options {
err := option(src)
if err != nil {
return nil, err
}
}
if src.Platform == nil {
platform, err := ParsePlatform("")
if err != nil {
return nil, err
}
src.Platform = platform
}
return src, nil
}
func WithPlatform(platform string) ImageSpecOption {
return func(i *ImageSpec) error {
if strings.Contains(platform, ",") {
return fmt.Errorf("only one platform is supported")
}
p, err := ParsePlatform(platform)
if err != nil {
return err
}
i.Platform = p
return nil
}
}
func ParseImageSpecs(img string) ([]*ImageSpec, error) {
outputs := strings.Split(img, ",")
var sources []*ImageSpec
for _, output := range outputs {
src, err := ParseImageSpec(output)
if err != nil {
return nil, err
}
sources = append(sources, src)
}
return sources, nil
}
func WithoutTag(image string) (string, error) {
if strings.HasPrefix(image, LocalPrefix) {
return image, nil
}
prefix := ""
if strings.HasPrefix(image, RegistryPrefix) {
image = strings.TrimPrefix(image, RegistryPrefix)
prefix = RegistryPrefix
}
ref, err := name.ParseReference(image)
if err != nil {
return "", err
}
repo := ref.Context().Name()
return prefix + repo, nil
}
type EmptyConfigImage struct {
v1.Image
}
func (i *EmptyConfigImage) RawConfigFile() ([]byte, error) {
return []byte("{}"), nil
}
func (i *EmptyConfigImage) Manifest() (*v1.Manifest, error) {
mf, err := i.Image.Manifest()
if err != nil {
return nil, fmt.Errorf("failed to get manifest: %w", err)
}
mf.Config = v1.Descriptor{
MediaType: "application/vnd.oci.empty.v1+json",
Size: 2,
Digest: v1.Hash{Algorithm: "sha256", Hex: "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"},
Data: []byte("{}"),
}
return mf, nil
}
func (i *EmptyConfigImage) RawManifest() ([]byte, error) {
mf, err := i.Manifest()
if err != nil {
return nil, fmt.Errorf("failed to get manifest: %w", err)
}
return json.Marshal(mf)
}
func (i *EmptyConfigImage) Digest() (v1.Hash, error) {
mb, err := i.RawManifest()
if err != nil {
return v1.Hash{}, err
}
digest, _, err := v1.SHA256(bytes.NewReader(mb))
return digest, err
}

37
oci/types_test.go Normal file
View File

@@ -0,0 +1,37 @@
/*
Copyright Docker attest authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oci
import (
"testing"
"github.com/docker/attest/internal/util"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestEmptyConfigImageDigest(t *testing.T) {
empty := empty.Image
img := EmptyConfigImage{Image: empty}
mf, err := img.RawManifest()
require.NoError(t, err)
hash := util.SHA256Hex(mf)
digest, err := img.Digest()
require.NoError(t, err)
assert.Equal(t, digest.Hex, hash)
}

View File

@@ -1,62 +0,0 @@
package attestation
import (
"context"
"fmt"
"github.com/docker/attest/internal/util"
"github.com/docker/attest/pkg/tlog"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
// SignDSSE signs a payload with a given signer and uploads the signature to the transparency log
func SignDSSE(ctx context.Context, payload []byte, payloadType string, signer dsse.SignerVerifier) (*Envelope, error) {
t := tlog.GetTL(ctx)
env := new(Envelope)
env.Payload = base64Encoding.EncodeToString(payload)
env.PayloadType = payloadType
encPayload := dsse.PAE(payloadType, payload)
// statement message digest
hash := util.S256(encPayload)
// sign message digest
sig, err := signer.Sign(ctx, hash)
if err != nil {
return nil, fmt.Errorf("error signing attestation: %w", err)
}
// get Key ID from signer
keyId, err := signer.KeyID()
if err != nil {
return nil, fmt.Errorf("error getting public key ID: %w", err)
}
// upload to TL
entry, err := t.UploadLogEntry(ctx, keyId, encPayload, sig, signer)
if err != nil {
return nil, fmt.Errorf("error uploading TL entry: %w", err)
}
entryObj, err := t.UnmarshalEntry(entry)
if err != nil {
return nil, fmt.Errorf("error unmarshaling tl entry: %w", err)
}
// add signature w/ tl extension to dsse envelope
env.Signatures = append(env.Signatures, Signature{
KeyID: keyId,
Sig: base64Encoding.EncodeToString(sig),
Extension: Extension{
Kind: DockerDsseExtKind,
Ext: DockerDsseExtension{
Tl: DockerTlExtension{
Kind: RekorTlExtKind,
Data: entryObj, // transparency log entry metadata
},
},
},
})
return env, nil
}

View File

@@ -1,147 +0,0 @@
package attestation_test
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/json"
"fmt"
"testing"
"time"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/pkg/attestation"
"github.com/docker/attest/pkg/signerverifier"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/stretchr/testify/assert"
)
func TestSignVerifyAttestation(t *testing.T) {
ctx, signer := test.Setup(t)
stmt := &intoto.Statement{
StatementHeader: intoto.StatementHeader{
Type: intoto.StatementInTotoV01,
PredicateType: intoto.PredicateSPDX,
},
Predicate: "test",
}
payload, err := json.Marshal(stmt)
assert.NoError(t, err)
env, err := attestation.SignDSSE(ctx, payload, intoto.PayloadType, signer)
assert.NoError(t, err)
// marshal envelope to json to test for bugs when marshaling envelope data
serializedEnv, err := json.Marshal(env)
assert.NoError(t, err)
deserializedEnv := new(attestation.Envelope)
err = json.Unmarshal(serializedEnv, deserializedEnv)
assert.NoError(t, err)
// signer.Public() calls AWS API when using AWS signer, use attestation.GetPublicVerificationKey() to get key from TUF repo
// signer.Public() used here for test purposes
ecPub, ok := signer.Public().(*ecdsa.PublicKey)
assert.True(t, ok)
pem, err := signerverifier.ToPEM(ecPub)
assert.NoError(t, err)
keyId, err := signerverifier.KeyID(ecPub)
assert.NoError(t, err)
badKeyPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.NoError(t, err)
badKey := &badKeyPriv.PublicKey
badPEM, err := signerverifier.ToPEM(badKey)
assert.NoError(t, err)
testCases := []struct {
name string
keyId string
pem []byte
distrust bool
from time.Time
to *time.Time
status string
expectedError string
}{
{
name: "all OK",
keyId: keyId,
pem: pem,
distrust: false,
from: time.Time{},
to: nil,
status: "active",
expectedError: "",
},
{
name: "key not found",
keyId: "someotherkey",
pem: pem,
distrust: false,
from: time.Time{},
to: nil,
status: "active",
expectedError: fmt.Sprintf("key not found: %s", keyId),
},
{
name: "key distrusted",
keyId: keyId,
pem: pem,
distrust: true,
from: time.Time{},
to: nil,
status: "active",
expectedError: "distrusted",
},
{
name: "key not yet valid",
keyId: keyId,
pem: pem,
distrust: false,
from: time.Now().Add(time.Hour),
to: nil,
status: "active",
expectedError: "not yet valid",
},
{
name: "key already revoked",
keyId: keyId,
pem: pem,
distrust: false,
from: time.Time{},
to: new(time.Time),
status: "revoked",
expectedError: "already revoked",
},
{
name: "bad key",
keyId: keyId,
pem: badPEM,
distrust: false,
from: time.Time{},
to: nil,
status: "active",
expectedError: "signature is not valid",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
keyMeta := attestation.KeyMetadata{
ID: tc.keyId,
PEM: string(tc.pem),
Distrust: tc.distrust,
From: tc.from,
To: tc.to,
Status: tc.status,
}
_, err = attestation.VerifyDSSE(ctx, deserializedEnv, attestation.KeysMap{tc.keyId: keyMeta})
if tc.expectedError != "" {
assert.Contains(t, err.Error(), tc.expectedError)
} else {
assert.NoError(t, err)
}
})
}
}

View File

@@ -1,35 +0,0 @@
package attestation
import "encoding/base64"
const (
DockerDsseExtKind = "application/vnd.docker.attestation-verification.v1+json"
RekorTlExtKind = "Rekor"
)
var base64Encoding = base64.StdEncoding.Strict()
// the following types are needed until https://github.com/secure-systems-lab/dsse/pull/61 is merged
type Envelope struct {
PayloadType string `json:"payloadType"`
Payload string `json:"payload"`
Signatures []Signature `json:"signatures"`
}
type Signature struct {
KeyID string `json:"keyid"`
Sig string `json:"sig"`
Extension Extension `json:"extension"`
}
type Extension struct {
Kind string `json:"kind"`
Ext DockerDsseExtension `json:"ext"`
}
type DockerDsseExtension struct {
Tl DockerTlExtension `json:"tl"`
}
type DockerTlExtension struct {
Kind string `json:"kind"`
Data any `json:"data"`
}

View File

@@ -1,134 +0,0 @@
package attestation
import (
"context"
"crypto/ecdsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"fmt"
"time"
"github.com/docker/attest/internal/util"
"github.com/docker/attest/pkg/signerverifier"
"github.com/docker/attest/pkg/tlog"
intoto "github.com/in-toto/in-toto-golang/in_toto"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
type KeyMetadata struct {
ID string `json:"id"`
PEM string `json:"key"`
From time.Time `json:"from"`
To *time.Time `json:"to"`
Status string `json:"status"`
SigningFormat string `json:"signing-format"`
Distrust bool `json:"distrust,omitempty"`
}
type Keys []KeyMetadata
type KeysMap map[string]KeyMetadata
func VerifyDSSE(ctx context.Context, env *Envelope, keys KeysMap) ([]byte, error) {
// enforce payload type
if !ValidPayloadType(env.PayloadType) {
return nil, fmt.Errorf("unsupported payload type %s", env.PayloadType)
}
if len(env.Signatures) == 0 {
return nil, fmt.Errorf("no signatures found")
}
payload, err := base64Encoding.DecodeString(env.Payload)
if err != nil {
return nil, fmt.Errorf("error failed to decode payload: %w", err)
}
encPayload := dsse.PAE(env.PayloadType, payload)
// verify signatures and transparency log entry
for _, sig := range env.Signatures {
err := verifySignature(ctx, sig, encPayload, keys)
if err != nil {
return nil, err
}
}
return payload, nil
}
func verifySignature(ctx context.Context, sig Signature, payload []byte, keys KeysMap) error {
t := tlog.GetTL(ctx)
if sig.Extension.Kind == "" {
return fmt.Errorf("error missing signature extension kind")
}
if sig.Extension.Kind != DockerDsseExtKind {
return fmt.Errorf("error unsupported signature extension kind: %s", sig.Extension.Kind)
}
// verify TL entry
if sig.Extension.Ext.Tl.Kind != RekorTlExtKind {
return fmt.Errorf("error unsupported TL extension kind: %s", sig.Extension.Ext.Tl.Kind)
}
entry := sig.Extension.Ext.Tl.Data
entryBytes, err := json.Marshal(entry)
if err != nil {
return fmt.Errorf("failed to marshal TL entry: %w", err)
}
integratedTime, err := t.VerifyLogEntry(ctx, entryBytes)
if err != nil {
return fmt.Errorf("TL entry failed verification: %w", err)
}
keyMeta, ok := keys[sig.KeyID]
if !ok {
return fmt.Errorf("error key not found: %s", sig.KeyID)
}
if keyMeta.Distrust {
return fmt.Errorf("key %s is distrusted", keyMeta.ID)
}
if integratedTime.Before(keyMeta.From) {
return fmt.Errorf("key %s was not yet valid at TL log time %s (key valid from %s)", keyMeta.ID, integratedTime, keyMeta.From)
}
if keyMeta.To != nil && !integratedTime.Before(*keyMeta.To) {
return fmt.Errorf("key %s was already %s at TL log time %s (key %s at %s)", keyMeta.ID, keyMeta.Status, integratedTime, keyMeta.Status, *keyMeta.To)
}
// TODO: this is unmarshalling with MarshalPKIXPublicKey only for us to marshal it again
publicKey, err := signerverifier.Parse([]byte(keyMeta.PEM))
if err != nil {
return fmt.Errorf("failed to parse public key: %w", err)
}
// verify TL entry payload
encodedPub, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return fmt.Errorf("error failed to marshal public key: %w", err)
}
err = t.VerifyEntryPayload(entryBytes, payload, encodedPub)
if err != nil {
return fmt.Errorf("TL entry failed payload verification: %w", err)
}
// decode signature
signature, err := base64.StdEncoding.Strict().DecodeString(sig.Sig)
if err != nil {
return fmt.Errorf("error failed to decode signature: %w", err)
}
// verify payload ecdsa signature
ok = ecdsa.VerifyASN1(publicKey, util.S256(payload), signature)
if !ok {
return fmt.Errorf("payload signature is not valid")
}
return nil
}
func ValidPayloadType(payloadType string) bool {
return payloadType == intoto.PayloadType || payloadType == ociv1.MediaTypeDescriptor
}

View File

@@ -1,82 +0,0 @@
package mirror
import (
"fmt"
"log"
"os"
"github.com/docker/attest/internal/embed"
"github.com/docker/attest/pkg/tuf"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/google/go-containerregistry/pkg/v1/remote"
)
func NewTufMirror(root []byte, tufPath, metadataURL, targetsURL string) (*TufMirror, error) {
if root == nil {
root = embed.DefaultRoot
}
tufClient, err := tuf.NewTufClient(root, tufPath, metadataURL, targetsURL)
if err != nil {
return nil, fmt.Errorf("failed to create TUF client: %w", err)
}
return &TufMirror{TufClient: tufClient, tufPath: tufPath, metadataURL: metadataURL, targetsURL: targetsURL}, nil
}
func PushToRegistry(image any, imageName string) error {
// Parse the image name
ref, err := name.ParseReference(imageName)
if err != nil {
log.Fatalf("Failed to parse image name: %v", err)
}
// Get the authenticator from the default Docker keychain
auth, err := authn.DefaultKeychain.Resolve(ref.Context())
if err != nil {
log.Fatalf("Failed to get authenticator: %v", err)
}
// Push the image to the registry
switch image := image.(type) {
case *v1.Image:
if err := remote.Write(ref, *image, remote.WithAuth(auth)); err != nil {
return fmt.Errorf("failed to push image %s: %w", imageName, err)
}
case *v1.ImageIndex:
if err := remote.WriteIndex(ref, *image, remote.WithAuth(auth)); err != nil {
return fmt.Errorf("failed to push image index %s: %w", imageName, err)
}
default:
return fmt.Errorf("unknown image type: %T", image)
}
return nil
}
func SaveAsOCILayout(image any, path string) error {
// Save the image to the local filesystem
err := os.MkdirAll(path, os.FileMode(0744))
if err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
switch image := image.(type) {
case *v1.Image:
index := empty.Index
l, err := layout.Write(path, index)
if err != nil {
return fmt.Errorf("failed to create index: %w", err)
}
err = l.AppendImage(*image)
if err != nil {
return fmt.Errorf("failed to append image to index: %w", err)
}
case *v1.ImageIndex:
_, err := layout.Write(path, *image)
if err != nil {
return fmt.Errorf("failed to create index: %w", err)
}
default:
return fmt.Errorf("unknown image type: %T", image)
}
return nil
}

View File

@@ -1,49 +0,0 @@
package mirror
import (
"github.com/docker/attest/pkg/tuf"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/theupdateframework/go-tuf/v2/metadata"
)
const (
DefaultMetadataURL = "https://docker.github.io/tuf-staging/metadata"
DefaultTargetsURL = "https://docker.github.io/tuf-staging/targets"
tufMetadataMediaType = "application/vnd.tuf.metadata+json"
tufTargetMediaType = "application/vnd.tuf.target"
tufFileAnnotation = "tuf.io/filename"
)
type TufRole string
var TufRoles = []TufRole{metadata.ROOT, metadata.SNAPSHOT, metadata.TARGETS, metadata.TIMESTAMP}
type TufMetadata struct {
Root map[string][]byte
Snapshot map[string][]byte
Targets map[string][]byte
Timestamp []byte
}
type DelegatedTargetMetadata struct {
Name string
Version string
Data []byte
}
type MirrorImage struct {
Image *v1.Image
Tag string
}
type MirrorIndex struct {
Index *v1.ImageIndex
Tag string
}
type TufMirror struct {
TufClient *tuf.TufClient
tufPath string
metadataURL string
targetsURL string
}

View File

@@ -1,27 +0,0 @@
package oci
import (
"net/http"
"github.com/hashicorp/go-cleanhttp"
)
type userAgentTransporter struct {
ua string
rt http.RoundTripper
}
type Option = func(*http.Client)
func (u *userAgentTransporter) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Set("User-Agent", u.ua)
return u.rt.RoundTrip(req)
}
func HttpTransport() http.RoundTripper {
return &userAgentTransporter{
ua: "Docker-Client",
rt: cleanhttp.DefaultTransport(),
}
}

View File

@@ -1,395 +0,0 @@
package oci
import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/containerd/containerd/platforms"
"github.com/distribution/reference"
att "github.com/docker/attest/pkg/attestation"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/google/go-containerregistry/pkg/v1/remote"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/package-url/packageurl-go"
"github.com/pkg/errors"
)
// parsePlatform parses the provided platform string or attempts to obtain
// the platform of the current host system
func parsePlatform(platformStr string) (*v1.Platform, error) {
if platformStr == "" {
cdp := platforms.Normalize(platforms.DefaultSpec())
if cdp.OS != "windows" {
cdp.OS = "linux"
}
return &v1.Platform{
OS: cdp.OS,
Architecture: cdp.Architecture,
Variant: cdp.Variant,
}, nil
} else {
return v1.ParsePlatform(platformStr)
}
}
func attestationManifestFromOCILayout(path string, platformStr string) (*AttestationManifest, error) {
idx, err := layout.ImageIndexFromPath(path)
if err != nil {
return nil, fmt.Errorf("failed to load image index: %w", err)
}
idxm, err := idx.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to get digest: %w", err)
}
idxDescriptor := idxm.Manifests[0]
name := idxDescriptor.Annotations["org.opencontainers.image.ref.name"]
idxDigest := idxDescriptor.Digest
mfs, err := idx.ImageIndex(idxDigest)
if err != nil {
return nil, fmt.Errorf("failed to extract ImageIndex for digest %s: %w", idxDigest.String(), err)
}
mfs2, err := mfs.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to extract IndexManifest from ImageIndex: %w", err)
}
platform, err := parsePlatform(platformStr)
if err != nil {
return nil, fmt.Errorf("failed to parse platform: %w", err)
}
var imageDigest string
for _, mf := range mfs2.Manifests {
if mf.Platform.Equals(*platform) {
imageDigest = mf.Digest.String()
}
}
for _, mf := range mfs2.Manifests {
if mf.Annotations[DockerReferenceType] != AttestationManifestType {
continue
}
if mf.Annotations[DockerReferenceDigest] != imageDigest {
continue
}
attestationImage, err := mfs.Image(mf.Digest)
if err != nil {
return nil, fmt.Errorf("failed to extract attestation image with digest %s: %w", mf.Digest.String(), err)
}
manifest, err := attestationImage.Manifest()
if err != nil {
return nil, fmt.Errorf("failed to get manifest: %w", err)
}
attest := &AttestationManifest{
Name: name,
Image: attestationImage,
Manifest: manifest,
Descriptor: &mf,
Digest: imageDigest,
Platform: platform,
}
return attest, nil
}
return nil, errors.New("attestation manifest not found")
}
// implementation of AttestationResolver that closes over attestations from an oci layout
type OCILayoutResolver struct {
Path string
Platform string
*AttestationManifest
}
func (r *OCILayoutResolver) ImagePlatformStr() string {
return r.Platform
}
func (r *OCILayoutResolver) fetchAttestationManifest() (*AttestationManifest, error) {
if r.AttestationManifest == nil {
m, err := attestationManifestFromOCILayout(r.Path, r.Platform)
if err != nil {
return nil, fmt.Errorf("failed to get attestation manifest: %w", err)
}
r.AttestationManifest = m
}
return r.AttestationManifest, nil
}
func (r *OCILayoutResolver) Attestations(ctx context.Context, predicateType string) ([]*att.Envelope, error) {
if r.AttestationManifest == nil {
_, err := r.fetchAttestationManifest()
if err != nil {
return nil, fmt.Errorf("failed to get attestation manifest: %w", err)
}
}
attestationImage := r.AttestationManifest.Image
layers, err := attestationImage.Layers()
if err != nil {
return nil, fmt.Errorf("failed to extract layers from attestation image: %w", err)
}
var envs []*att.Envelope
manifest := r.AttestationManifest.Manifest
for i, l := range manifest.Layers {
if l.Annotations[InTotoPredicateType] != predicateType {
continue
}
layer := layers[i]
mt, err := layer.MediaType()
if err != nil {
return nil, fmt.Errorf("failed to get layer media type: %w", err)
}
mts := string(mt)
if !strings.HasSuffix(mts, "+dsse") {
continue
}
var env = new(att.Envelope)
// parse layer blob as json
r, err := layer.Uncompressed()
if err != nil {
return nil, fmt.Errorf("failed to get layer contents: %w", err)
}
defer r.Close()
err = json.NewDecoder(r).Decode(env)
if err != nil {
return nil, fmt.Errorf("failed to decode envelope: %w", err)
}
envs = append(envs, env)
}
return envs, nil
}
func (r *OCILayoutResolver) ImageName(ctx context.Context) (string, error) {
if r.AttestationManifest == nil {
_, err := r.fetchAttestationManifest()
if err != nil {
return "", fmt.Errorf("failed to get attestation manifest: %w", err)
}
}
return r.Name, nil
}
func (r *OCILayoutResolver) ImageDigest(ctx context.Context) (string, error) {
if r.AttestationManifest == nil {
_, err := r.fetchAttestationManifest()
if err != nil {
return "", fmt.Errorf("failed to get attestation manifest: %w", err)
}
}
return r.Digest, nil
}
type RegistryResolver struct {
Image string
Platform string
*AttestationManifest
}
func (r *RegistryResolver) ImageName(ctx context.Context) (string, error) {
return r.Image, nil
}
func (r *RegistryResolver) ImagePlatformStr() string {
return r.Platform
}
func (r *RegistryResolver) ImageDigest(ctx context.Context) (string, error) {
if r.AttestationManifest == nil {
attest, err := FetchAttestationManifest(ctx, r.Image, r.Platform)
if err != nil {
return "", fmt.Errorf("failed to get attestation manifest: %w", err)
}
r.AttestationManifest = attest
}
return r.Digest, nil
}
func (r *RegistryResolver) Attestations(ctx context.Context, predicateType string) ([]*att.Envelope, error) {
if r.AttestationManifest == nil {
attest, err := FetchAttestationManifest(ctx, r.Image, r.Platform)
if err != nil {
return nil, fmt.Errorf("failed to get attestation manifest: %w", err)
}
r.AttestationManifest = attest
}
return ExtractEnvelopes(r.AttestationManifest, predicateType)
}
func FetchAttestationManifest(ctx context.Context, image, platformStr string) (*AttestationManifest, error) {
platform, err := parsePlatform(platformStr)
if err != nil {
return nil, fmt.Errorf("failed to parse platform %s: %w", platform, err)
}
// we want to get to the image index, so ignoring platform for now
options := withOptions(ctx, nil)
ref, err := name.ParseReference(image)
if err != nil {
return nil, fmt.Errorf("failed to parse reference: %w", err)
}
desc, err := remote.Index(ref, options...)
if err != nil {
return nil, fmt.Errorf("failed to obtain index manifest: %w", err)
}
ix, err := desc.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to obtain index manifest: %w", err)
}
digest, err := imageDigestForPlatform(ix, platform)
if err != nil {
return nil, fmt.Errorf("failed to obtain image for platform: %w", err)
}
ref, err = name.ParseReference(fmt.Sprintf("%s@%s", ref.Context().Name(), digest))
if err != nil {
return nil, fmt.Errorf("failed to parse attestation reference: %w", err)
}
attestationDigest, err := attestationDigestForDigest(ix, digest, "attestation-manifest")
if err != nil {
return nil, fmt.Errorf("failed to obtain attestation for image: %w", err)
}
ref, err = name.ParseReference(fmt.Sprintf("%s@%s", ref.Context().Name(), attestationDigest))
if err != nil {
return nil, fmt.Errorf("failed to parse attestation reference: %w", err)
}
remoteDescriptor, err := remote.Get(ref, options...)
if err != nil {
return nil, fmt.Errorf("failed to get attestation: %w", err)
}
manifest := new(v1.Manifest)
err = json.Unmarshal(remoteDescriptor.Manifest, manifest)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal attestation: %w", err)
}
attestationImage, err := remoteDescriptor.Image()
if err != nil {
return nil, fmt.Errorf("failed to get attestation image: %w", err)
}
attest := &AttestationManifest{
Name: image,
Image: attestationImage,
Manifest: manifest,
Descriptor: &remoteDescriptor.Descriptor,
Digest: digest,
Platform: platform,
}
return attest, nil
}
func withOptions(ctx context.Context, platform *v1.Platform) []remote.Option {
// prepare options
options := []remote.Option{remote.WithAuthFromKeychain(authn.DefaultKeychain), remote.WithTransport(HttpTransport()), remote.WithContext(ctx)}
// add in platform into remote Get operation; this might conflict with an explicit digest, but we are trying anyway
if platform != nil {
options = append(options, remote.WithPlatform(*platform))
}
return options
}
func ExtractEnvelopes(ia *AttestationManifest, predicateType string) ([]*att.Envelope, error) {
manifest := ia.Manifest
im := ia.Image
var envs []*att.Envelope
ls, err := im.Layers()
if err != nil {
return nil, fmt.Errorf("failed to get layers: %w", err)
}
for i, l := range manifest.Layers {
if (strings.HasPrefix(string(l.MediaType), "application/vnd.in-toto.")) &&
strings.HasSuffix(string(l.MediaType), "+dsse") &&
l.Annotations[InTotoPredicateType] == predicateType {
reader, err := ls[i].Uncompressed()
if err != nil {
return nil, fmt.Errorf("failed to get layer contents: %w", err)
}
defer reader.Close()
var env = new(att.Envelope)
err = json.NewDecoder(reader).Decode(&env)
if err != nil {
return nil, fmt.Errorf("failed to decode envelope: %w", err)
}
envs = append(envs, env)
}
}
return envs, nil
}
func imageDigestForPlatform(ix *v1.IndexManifest, platform *v1.Platform) (string, error) {
for _, m := range ix.Manifests {
if m.MediaType == ocispec.MediaTypeImageManifest || m.MediaType == "application/vnd.docker.distribution.manifest.v2+json" && m.Platform.Equals(*platform) {
return m.Digest.String(), nil
}
}
return "", errors.New(fmt.Sprintf("no image found for platform %v", platform))
}
func attestationDigestForDigest(ix *v1.IndexManifest, imageDigest string, attestType string) (string, error) {
for _, m := range ix.Manifests {
if v, ok := m.Annotations["vnd.docker.reference.type"]; ok && v == attestType {
if d, ok := m.Annotations["vnd.docker.reference.digest"]; ok && d == imageDigest {
return m.Digest.String(), nil
}
}
}
return "", errors.New(fmt.Sprintf("no attestation found for image %s", imageDigest))
}
func RefToPURL(ref string, platform string) (string, bool, error) {
var isCanonical bool
named, err := reference.ParseNormalizedNamed(ref)
if err != nil {
return "", false, fmt.Errorf("failed to parse ref %q: %w", ref, err)
}
var qualifiers []packageurl.Qualifier
if canonical, ok := named.(reference.Canonical); ok {
qualifiers = append(qualifiers, packageurl.Qualifier{
Key: "digest",
Value: canonical.Digest().String(),
})
isCanonical = true
} else {
named = reference.TagNameOnly(named)
}
version := ""
if tagged, ok := named.(reference.Tagged); ok {
version = tagged.Tag()
}
name := reference.FamiliarName(named)
ns := ""
parts := strings.Split(name, "/")
if len(parts) > 1 {
ns = strings.Join(parts[:len(parts)-1], "/")
}
name = parts[len(parts)-1]
pf, err := parsePlatform(platform)
if err != nil {
return "", false, fmt.Errorf("failed to parse platform %q: %w", platform, err)
}
if pf != nil {
qualifiers = append(qualifiers, packageurl.Qualifier{
Key: "platform",
Value: pf.String(),
})
}
p := packageurl.NewPackageURL("docker", ns, name, version, qualifiers, "")
return p.ToString(), isCanonical, nil
}

View File

@@ -1,49 +0,0 @@
package oci
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRefToPurl(t *testing.T) {
purl, canonical, err := RefToPURL("alpine", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/alpine@latest?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
purl, canonical, err = RefToPURL("alpine:123", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
purl, canonical, err = RefToPURL("google/alpine:123", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/google/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
purl, canonical, err = RefToPURL("library/alpine:123", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
purl, canonical, err = RefToPURL("docker.io/library/alpine:123", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
purl, canonical, err = RefToPURL("localhost:5001/library/alpine:123", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/localhost%3A5001/library/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
purl, canonical, err = RefToPURL("localhost:5001/alpine:123", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/localhost%3A5001/alpine@123?platform=arm64%2Flinux", purl)
assert.False(t, canonical)
purl, canonical, err = RefToPURL("localhost:5001/alpine@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b", "arm64/linux")
assert.NoError(t, err)
assert.Equal(t, "pkg:docker/localhost%3A5001/alpine?digest=sha256%3Ac5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b&platform=arm64%2Flinux", purl)
assert.True(t, canonical)
}

View File

@@ -1,46 +0,0 @@
package oci
import (
"context"
att "github.com/docker/attest/pkg/attestation"
v1 "github.com/google/go-containerregistry/pkg/v1"
)
type AttestationManifest struct {
// attestation image details
Image v1.Image
Manifest *v1.Manifest
Descriptor *v1.Descriptor
// details of subect image
Name string
Digest string
Platform *v1.Platform
}
type AttestationResolver interface {
ImageName(ctx context.Context) (string, error)
ImagePlatformStr() string
ImageDigest(ctx context.Context) (string, error)
Attestations(ctx context.Context, mediaType string) ([]*att.Envelope, error)
}
type MockResolver struct {
Envs []*att.Envelope
}
func (r MockResolver) Attestations(ctx context.Context, mediaType string) ([]*att.Envelope, error) {
return r.Envs, nil
}
func (r MockResolver) ImageName(ctx context.Context) (string, error) {
return "library/alpine:latest", nil
}
func (r MockResolver) ImageDigest(ctx context.Context) (string, error) {
return "sha256:test-digest", nil
}
func (r MockResolver) ImagePlatformStr() string {
return "linux/amd64"
}

View File

@@ -1,8 +0,0 @@
package oci
const (
DockerReferenceType = "vnd.docker.reference.type"
DockerReferenceDigest = "vnd.docker.reference.digest"
AttestationManifestType = "attestation-manifest"
InTotoPredicateType = "in-toto.io/predicate-type"
)

View File

@@ -1,41 +0,0 @@
package policy
import (
"context"
"fmt"
"github.com/docker/attest/pkg/oci"
)
type policyEvaluatorCtxKeyType struct{}
var PolicyEvaluatorCtxKey policyEvaluatorCtxKeyType
// sets PolicyEvaluator in context
func WithPolicyEvaluator(ctx context.Context, pe PolicyEvaluator) context.Context {
return context.WithValue(ctx, PolicyEvaluatorCtxKey, pe)
}
// gets PolicyEvaluator from context, defaults to Rego PolicyEvaluator if not set
func GetPolicyEvaluator(ctx context.Context) (PolicyEvaluator, error) {
t, ok := ctx.Value(PolicyEvaluatorCtxKey).(PolicyEvaluator)
if !ok {
return nil, fmt.Errorf("no policy evaluator client set on context (set one with policy.WithPolicyEvaluator)")
}
return t, nil
}
type PolicyEvaluator interface {
Evaluate(ctx context.Context, resolver oci.AttestationResolver, policy []*PolicyFile, input *PolicyInput) error
}
type MockPolicyEvaluator struct {
EvaluateFunc func(ctx context.Context, resolver oci.AttestationResolver, policy []*PolicyFile, input *PolicyInput) error
}
func (pe *MockPolicyEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, policy []*PolicyFile, input *PolicyInput) error {
if pe.EvaluateFunc != nil {
return pe.EvaluateFunc(ctx, resolver, policy, input)
}
return nil
}

View File

@@ -1,203 +0,0 @@
package policy
import (
"context"
"fmt"
"os"
"path"
"path/filepath"
"strings"
"github.com/distribution/reference"
"github.com/docker/attest/internal/util"
"github.com/docker/attest/pkg/oci"
"github.com/docker/attest/pkg/tuf"
goyaml "gopkg.in/yaml.v3"
)
const (
PolicyMappingFileName = "mapping.yaml"
)
var (
PolicyFileNames = []string{"data.yaml", "policy.rego"}
)
type PolicyMappings struct {
Version string `json:"version"`
Kind string `json:"kind"`
Policies []PolicyMapping `json:"policies"`
Mirrors []PolicyMirror `json:"mirrors"`
}
type PolicyMapping struct {
Name string `json:"namespace"`
Location string `json:"location"`
Description string `json:"description"`
Origin PolicyOrigin `json:"origin"`
}
type PolicyMirror struct {
Name string `json:"name"`
Mirror MirrorSpec `json:"mirror"`
}
type MirrorSpec struct {
Domains []string `json:"domains"`
Prefix string `json:"prefix"`
}
type PolicyOrigin struct {
Name string `json:"name"`
Prefix string `json:"prefix"`
Domain string `json:"domain"`
}
type PolicyOptions struct {
TufClient tuf.TUFClient
LocalTargetsDir string
LocalPolicyDir string
}
type PolicyInput struct {
Digest string `json:"digest"`
Purl string `json:"purl"`
IsCanonical bool `json:"isCanonical"`
}
type PolicyFile struct {
Path string
Content []byte
}
func resolveLocalPolicy(opts *PolicyOptions, mapping *PolicyMapping) ([]*PolicyFile, error) {
if opts.LocalPolicyDir == "" {
return nil, fmt.Errorf("local policy dir not set")
}
files := make([]*PolicyFile, 0, len(PolicyFileNames))
for _, filename := range PolicyFileNames {
filePath := path.Join(opts.LocalPolicyDir, mapping.Location, filename)
fileContents, err := os.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("failed to read policy file %s: %w", filename, err)
}
files = append(files, &PolicyFile{
Path: filename,
Content: fileContents,
})
}
return files, nil
}
func loadLocalMappings(opts *PolicyOptions) (*PolicyMappings, error) {
if opts.LocalPolicyDir == "" {
return nil, nil
}
mappings := &PolicyMappings{}
path := path.Join(opts.LocalPolicyDir, PolicyMappingFileName)
mappingFile, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read policy mapping file %s: %w", path, err)
}
err = goyaml.Unmarshal(mappingFile, mappings)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal policy mapping file %s: %w", path, err)
}
return mappings, nil
}
func resolveTufPolicy(opts *PolicyOptions, mapping *PolicyMapping) ([]*PolicyFile, error) {
files := make([]*PolicyFile, 0, len(PolicyFileNames))
for _, filename := range PolicyFileNames {
filePath := path.Join(mapping.Location, filename)
_, fileContents, err := opts.TufClient.DownloadTarget(filePath, filepath.Join(opts.LocalTargetsDir, filePath))
if err != nil {
return nil, fmt.Errorf("failed to download policy file %s: %w", filename, err)
}
files = append(files, &PolicyFile{
Path: filename,
Content: fileContents,
})
}
return files, nil
}
func loadTufMappings(tufClient tuf.TUFClient, localTargetsDir string) (*PolicyMappings, error) {
filename := PolicyMappingFileName
_, fileContents, err := tufClient.DownloadTarget(filename, filepath.Join(localTargetsDir, filename))
if err != nil {
return nil, fmt.Errorf("failed to download policy file %s: %w", filename, err)
}
mappings := &PolicyMappings{}
err = goyaml.Unmarshal(fileContents, mappings)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal policy mapping file %s: %w", filename, err)
}
return mappings, nil
}
func findPolicyMatch(named reference.Named, mappings *PolicyMappings) (*PolicyMapping, *PolicyMirror) {
if mappings != nil {
for _, mapping := range mappings.Policies {
if mapping.Origin.Domain == reference.Domain(named) &&
strings.HasPrefix(reference.Path(named), mapping.Origin.Prefix) {
return &mapping, nil
}
}
// now search mirrors
for _, mirror := range mappings.Mirrors {
if util.StringInSlice(reference.Domain(named), mirror.Mirror.Domains) &&
strings.HasPrefix(reference.Path(named), mirror.Mirror.Prefix) {
for _, mapping := range mappings.Policies {
if mapping.Name == mirror.Name {
return &mapping, nil
}
}
return nil, &mirror
}
}
}
return nil, nil
}
func ResolvePolicy(ctx context.Context, resolver oci.AttestationResolver, opts *PolicyOptions) ([]*PolicyFile, error) {
imageName, err := resolver.ImageName(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get image name: %w", err)
}
named, err := reference.ParseNormalizedNamed(imageName)
if err != nil {
return nil, fmt.Errorf("failed to parse image name: %w", err)
}
localMappings, err := loadLocalMappings(opts)
if err != nil {
return nil, fmt.Errorf("failed to load local policy mappings: %w", err)
}
mapping, mirror := findPolicyMatch(named, localMappings)
if mapping != nil {
return resolveLocalPolicy(opts, mapping)
}
// must check tuf
tufMappings, err := loadTufMappings(opts.TufClient, opts.LocalTargetsDir)
if err != nil {
return nil, fmt.Errorf("failed to load tuf policy mappings: %w", err)
}
// it's a mirror of a tuf policy
if mirror != nil {
for _, mapping := range tufMappings.Policies {
if mapping.Name == mirror.Name {
return resolveTufPolicy(opts, &mapping)
}
}
}
// try to resolve a tuf policy directly
mapping, _ = findPolicyMatch(named, tufMappings)
if mapping == nil {
return nil, nil
}
return resolveTufPolicy(opts, mapping)
}

View File

@@ -1,110 +0,0 @@
package policy_test
import (
"encoding/json"
"os"
"path/filepath"
"testing"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/pkg/attestation"
"github.com/docker/attest/pkg/oci"
"github.com/docker/attest/pkg/policy"
"github.com/docker/attest/pkg/tuf"
"github.com/stretchr/testify/assert"
)
func loadAttestation(t *testing.T, path string) *attestation.Envelope {
ex, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
var env = new(attestation.Envelope)
err = json.Unmarshal(ex, env)
if err != nil {
t.Fatal(err)
}
return env
}
func TestRegoEvaluator_Evaluate(t *testing.T) {
ctx, _ := test.Setup(t)
TestDataPath := filepath.Join("..", "..", "test", "testdata")
MockTufRepo := filepath.Join(TestDataPath, "local-policy")
ExampleAttestation := filepath.Join(TestDataPath, "example_attestation.json")
VSA := filepath.Join(TestDataPath, "vsa.json")
re := policy.NewRegoEvaluator(true)
defaultInput := &policy.PolicyInput{
Digest: "sha256:test-digest",
Purl: "test-purl",
IsCanonical: true,
}
defaultResolver := oci.MockResolver{
Envs: []*attestation.Envelope{loadAttestation(t, ExampleAttestation)},
}
vsaResolver := oci.MockResolver{
Envs: []*attestation.Envelope{loadAttestation(t, ExampleAttestation), loadAttestation(t, VSA)},
}
testCases := []struct {
repo string
expectSuccess bool
input *policy.PolicyInput
resolver oci.AttestationResolver
policy *policy.PolicyOptions
}{
{repo: "testdata/mock-tuf-allow", expectSuccess: true, input: defaultInput, resolver: defaultResolver},
{repo: "testdata/mock-tuf-deny", expectSuccess: false, input: defaultInput, resolver: defaultResolver},
{repo: "testdata/mock-tuf-verify-sig", expectSuccess: true, input: defaultInput, resolver: defaultResolver},
{repo: "testdata/mock-tuf-wrong-key", expectSuccess: false, input: defaultInput, resolver: defaultResolver},
{repo: MockTufRepo, expectSuccess: true, input: &policy.PolicyInput{
Digest: "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
Purl: "pkg:docker/test-image@test?platform=linux%2Famd64",
IsCanonical: true,
}, resolver: vsaResolver},
{repo: MockTufRepo, expectSuccess: true, input: &policy.PolicyInput{
Digest: "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
Purl: "pkg:docker/test-image@test?platform=linux%2Famd64",
IsCanonical: false,
}, resolver: vsaResolver},
// not a doi
{repo: MockTufRepo, expectSuccess: false, input: defaultInput, resolver: vsaResolver, policy: &policy.PolicyOptions{
LocalPolicyDir: "testdata/mock-tuf-deny",
}},
// digest mismatch
{repo: MockTufRepo, expectSuccess: false, input: &policy.PolicyInput{
Digest: "sha256:test-digest-wrong",
Purl: "test-purl",
IsCanonical: false,
}, resolver: vsaResolver},
}
for _, tc := range testCases {
t.Run(tc.repo, func(t *testing.T) {
tufClient := tuf.NewMockTufClient(tc.repo, test.CreateTempDir(t, "", "tuf-dest"))
if tc.policy == nil {
tc.policy = &policy.PolicyOptions{
TufClient: tufClient,
LocalTargetsDir: test.CreateTempDir(t, "", "tuf-targets"),
}
}
policyFiles, err := policy.ResolvePolicy(ctx, tc.resolver, tc.policy)
assert.NoErrorf(t, err, "failed to resolve policy")
err = re.Evaluate(ctx, tc.resolver, policyFiles, tc.input)
if tc.expectSuccess {
assert.NoErrorf(t, err, "Evaluate failed")
} else {
assert.Errorf(t, err, "Evaluate should have failed")
}
})
}
}

View File

@@ -1,220 +0,0 @@
package policy
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
att "github.com/docker/attest/pkg/attestation"
"github.com/docker/attest/pkg/oci"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/rego"
"github.com/open-policy-agent/opa/storage"
"github.com/open-policy-agent/opa/storage/inmem"
"github.com/open-policy-agent/opa/tester"
"github.com/open-policy-agent/opa/topdown"
"github.com/open-policy-agent/opa/types"
opa "github.com/open-policy-agent/opa/util"
"sigs.k8s.io/yaml"
)
type regoEvaluator struct {
debug bool
}
func NewRegoEvaluator(debug bool) PolicyEvaluator {
return &regoEvaluator{
debug: debug,
}
}
func (re *regoEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, files []*PolicyFile, input *PolicyInput) error {
var regoOpts []func(*rego.Rego)
// Create a new in-memory store
store := inmem.New()
params := storage.TransactionParams{}
params.Write = true
txn, err := store.NewTransaction(ctx, params)
if err != nil {
return err
}
for _, target := range files {
// load yaml as data (no rego opt for this!?)
if filepath.Ext(target.Path) == ".yaml" {
yamlData, err := loadYAML(target.Path, target.Content)
if err != nil {
return err
}
err = store.Write(ctx, txn, storage.AddOp, storage.Path{}, yamlData)
if err != nil {
return err
}
} else {
regoOpts = append(regoOpts, rego.Module(target.Path, string(target.Content)))
}
}
err = store.Commit(ctx, txn)
if err != nil {
store.Abort(ctx, txn)
return err
}
if re.debug {
regoOpts = append(regoOpts,
rego.EnablePrintStatements(true),
rego.PrintHook(topdown.NewPrintHook(os.Stderr)),
rego.Dump(os.Stderr),
)
}
regoOpts = append(regoOpts,
rego.Query("data.docker.allow"),
rego.StrictBuiltinErrors(true),
rego.Input(input),
rego.Store(store),
)
for _, custom := range RegoFunctions(resolver) {
regoOpts = append(regoOpts, custom.Func)
}
r := rego.New(regoOpts...)
rs, err := r.Eval(ctx)
if err != nil {
return fmt.Errorf("error from Eval: %w", err)
}
if !rs.Allowed() {
return fmt.Errorf("policy evaluation failed")
}
return nil
}
var dynamicObj = types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))
var arrayObj = types.NewArray(nil, dynamicObj)
var verifyDecl = &ast.Builtin{
Name: "attestations.verify_envelope",
Decl: types.NewFunction(types.Args(dynamicObj, arrayObj), dynamicObj),
Nondeterministic: true,
}
var attestDecl = &ast.Builtin{
Name: "attestations.attestation",
Decl: types.NewFunction(types.Args(types.S), dynamicObj),
Nondeterministic: true,
}
func RegoFunctions(resolver oci.AttestationResolver) []*tester.Builtin {
return []*tester.Builtin{
{
Decl: verifyDecl,
Func: rego.Function2(
&rego.Function{
Name: verifyDecl.Name,
Decl: verifyDecl.Decl,
Memoize: true,
Nondeterministic: verifyDecl.Nondeterministic,
},
verifyIntotoEnvelope),
},
{
Decl: attestDecl,
Func: rego.Function1(
&rego.Function{
Name: attestDecl.Name,
Decl: attestDecl.Decl,
Memoize: true,
Nondeterministic: attestDecl.Nondeterministic,
},
fetchIntotoAttestations(resolver)),
},
}
}
func fetchIntotoAttestations(resolver oci.AttestationResolver) func(rego.BuiltinContext, *ast.Term) (*ast.Term, error) {
return func(rCtx rego.BuiltinContext, predicateTypeTerm *ast.Term) (*ast.Term, error) {
predicateTypeStr, ok := predicateTypeTerm.Value.(ast.String)
if !ok {
return nil, fmt.Errorf("predicateTypeTerm is not a string")
}
predicateType := string(predicateTypeStr)
envelopes, err := resolver.Attestations(rCtx.Context, predicateType)
if err != nil {
return nil, err
}
// Convert each envelope to an ast.Value.
values := make([]*ast.Term, len(envelopes))
for i, envelope := range envelopes {
value, err := ast.InterfaceToValue(envelope)
if err != nil {
return nil, err
}
values[i] = ast.NewTerm(value)
}
// Wrap the values in an ast.Array and convert it to an ast.Term.
array := ast.NewTerm(ast.NewArray(values...))
return array, nil
}
}
func verifyIntotoEnvelope(rCtx rego.BuiltinContext, envTerm, keysTerm *ast.Term) (*ast.Term, error) {
env := new(att.Envelope)
var keys att.Keys
err := ast.As(envTerm.Value, env)
if err != nil {
return nil, fmt.Errorf("failed to cast envelope: %w", err)
}
err = ast.As(keysTerm.Value, &keys)
if err != nil {
return nil, fmt.Errorf("failed to cast keys: %w", err)
}
keysmap := make(map[string]att.KeyMetadata, len(keys))
for _, key := range keys {
keysmap[key.ID] = key
}
payload, err := att.VerifyDSSE(rCtx.Context, env, keysmap)
if err != nil {
return nil, err
}
statement := new(intoto.Statement)
switch env.PayloadType {
case intoto.PayloadType:
err = json.Unmarshal(payload, statement)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal statement: %w", err)
}
// TODO: implement other types of envelope
default:
return nil, fmt.Errorf("unsupported payload type: %s", env.PayloadType)
}
value, err := ast.InterfaceToValue(statement)
if err != nil {
return nil, err
}
return ast.NewTerm(value), nil
}
func loadYAML(path string, bs []byte) (interface{}, error) {
var x interface{}
bs, err := yaml.YAMLToJSON(bs)
if err != nil {
return nil, fmt.Errorf("%v: error converting YAML to JSON: %v", path, err)
}
err = opa.UnmarshalJSON(bs, &x)
if err != nil {
return nil, fmt.Errorf("%s: %w", path, err)
}
return x, nil
}

View File

@@ -1 +0,0 @@
config:

View File

@@ -1,5 +0,0 @@
package docker
import rego.v1
allow := true

Some files were not shown because too many files have changed in this diff Show More