From 8fe70875a33262ae1f29795763d7a7e68a292000 Mon Sep 17 00:00:00 2001 From: Appu Goundan Date: Fri, 4 Oct 2024 13:06:47 -0400 Subject: [PATCH] Add tuf conformance ignores all currently failing tests with xfails Signed-off-by: Appu Goundan --- .github/workflows/tuf-conformance.yml | 41 +++++++ settings.gradle.kts | 4 +- tuf-cli/README.md | 11 ++ tuf-cli/build.gradle.kts | 39 +++++++ .../java/dev/sigstore/tuf/cli/Download.java | 59 ++++++++++ .../main/java/dev/sigstore/tuf/cli/Init.java | 51 +++++++++ .../java/dev/sigstore/tuf/cli/Refresh.java | 51 +++++++++ .../main/java/dev/sigstore/tuf/cli/Tuf.java | 107 ++++++++++++++++++ tuf-cli/tuf-cli.xfails | 45 ++++++++ 9 files changed, 407 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/tuf-conformance.yml create mode 100644 tuf-cli/README.md create mode 100644 tuf-cli/build.gradle.kts create mode 100644 tuf-cli/src/main/java/dev/sigstore/tuf/cli/Download.java create mode 100644 tuf-cli/src/main/java/dev/sigstore/tuf/cli/Init.java create mode 100644 tuf-cli/src/main/java/dev/sigstore/tuf/cli/Refresh.java create mode 100644 tuf-cli/src/main/java/dev/sigstore/tuf/cli/Tuf.java create mode 100644 tuf-cli/tuf-cli.xfails diff --git a/.github/workflows/tuf-conformance.yml b/.github/workflows/tuf-conformance.yml new file mode 100644 index 00000000..54e5ce86 --- /dev/null +++ b/.github/workflows/tuf-conformance.yml @@ -0,0 +1,41 @@ +name: TUF Conformance Tests + +on: + push: + branches: + - '**' + pull_request: + workflow_dispatch: + # TODO: add cron + +jobs: + conformance: + strategy: + max-parallel: 1 + matrix: + java-version: [11, 17] + fail-fast: false + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + + - name: Set up JDK ${{ matrix.java-version }} + uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4.4.0 + with: + java-version: ${{ matrix.java-version }} + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0 + + - name: Build tuf cli + run: ./gradlew :tuf-cli:build + + - name: Unpack tuf distribution + run: tar -xvf ${{ github.workspace }}/tuf-cli/build/distributions/tuf-cli-*.tar --strip-components 1 + + - uses: theupdateframework/tuf-conformance@v2 + with: + entrypoint: ${{ github.workspace }}/bin/tuf-cli + artifact-name: test repositories for tuf-cli java ${{ matrix.java-version }} diff --git a/settings.gradle.kts b/settings.gradle.kts index 628f2228..48d85287 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,7 +7,9 @@ include("sigstore-java") include("sigstore-gradle:sigstore-gradle-sign-base-plugin") include("sigstore-gradle:sigstore-gradle-sign-plugin") include("sigstore-testkit") -include("sigstore-cli") include("sigstore-maven-plugin") +include("sigstore-cli") +include("tuf-cli") + include("fuzzing") diff --git a/tuf-cli/README.md b/tuf-cli/README.md new file mode 100644 index 00000000..971907e4 --- /dev/null +++ b/tuf-cli/README.md @@ -0,0 +1,11 @@ +# Sigstore-Java Tuf CLI + +Used for conformance testing and internal processes. This is not meant for public consumption, we will not support +any usecase that uses this. + +## Usage + +### Help +``` +./gradlew tuf-cli:run +``` diff --git a/tuf-cli/build.gradle.kts b/tuf-cli/build.gradle.kts new file mode 100644 index 00000000..859a5b32 --- /dev/null +++ b/tuf-cli/build.gradle.kts @@ -0,0 +1,39 @@ +plugins { + id("build-logic.java") + id("application") +} + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":sigstore-java")) + implementation("info.picocli:picocli:4.7.6") + implementation("com.google.guava:guava:33.3.1-jre") + + implementation(platform("com.google.oauth-client:google-oauth-client-bom:1.36.0")) + implementation("com.google.oauth-client:google-oauth-client") + + annotationProcessor("info.picocli:picocli-codegen:4.7.6") +} + +tasks.compileJava { + options.compilerArgs.add("-Aproject=${project.group}/${project.name}") +} + +application { + mainClass.set("dev.sigstore.tuf.cli.Tuf") +} + +distributions.main { + contents { + from("tuf-cli.xfails") { + into("bin") + } + } +} + +tasks.run.configure { + workingDir = rootProject.projectDir +} diff --git a/tuf-cli/src/main/java/dev/sigstore/tuf/cli/Download.java b/tuf-cli/src/main/java/dev/sigstore/tuf/cli/Download.java new file mode 100644 index 00000000..9c243f93 --- /dev/null +++ b/tuf-cli/src/main/java/dev/sigstore/tuf/cli/Download.java @@ -0,0 +1,59 @@ +/* + * Copyright 2024 The Sigstore 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 dev.sigstore.tuf.cli; + +import dev.sigstore.tuf.FileSystemTufStore; +import dev.sigstore.tuf.HttpFetcher; +import dev.sigstore.tuf.MetaFetcher; +import dev.sigstore.tuf.PassthroughCacheMetaStore; +import dev.sigstore.tuf.RootProvider; +import dev.sigstore.tuf.TrustedMetaStore; +import dev.sigstore.tuf.Updater; +import java.util.concurrent.Callable; +import picocli.CommandLine.Command; +import picocli.CommandLine.ParentCommand; + +@Command(name = "download", description = "download targets from a remote location") +public class Download implements Callable { + + @ParentCommand private Tuf tufCommand; + + @Override + public Integer call() throws Exception { + var metadataDir = tufCommand.getMetadataDir(); + var metadataUrl = tufCommand.getMetadataUrl(); + var targetDir = tufCommand.getTargetDir(); + var targetBaseUrl = tufCommand.getTargetBaseUrl(); + var targetName = tufCommand.getTargetName(); + + var fsStore = FileSystemTufStore.newFileSystemStore(metadataDir, targetDir); + var tuf = + Updater.builder() + .setTrustedMetaStore( + TrustedMetaStore.newTrustedMetaStore( + PassthroughCacheMetaStore.newPassthroughMetaCache(fsStore))) + .setTrustedRootPath(RootProvider.fromFile(metadataDir.resolve("root.json"))) + .setMetaFetcher(MetaFetcher.newFetcher(HttpFetcher.newFetcher(metadataUrl))) + .setTargetFetcher(HttpFetcher.newFetcher(targetBaseUrl)) + .setTargetStore(fsStore) + .build(); + // the java client isn't one shot like other clients, so downloadTarget doesn't call update + // for the sake of conformance updateMeta here + tuf.updateMeta(); + tuf.downloadTarget(targetName); + return 0; + } +} diff --git a/tuf-cli/src/main/java/dev/sigstore/tuf/cli/Init.java b/tuf-cli/src/main/java/dev/sigstore/tuf/cli/Init.java new file mode 100644 index 00000000..2ff6c737 --- /dev/null +++ b/tuf-cli/src/main/java/dev/sigstore/tuf/cli/Init.java @@ -0,0 +1,51 @@ +/* + * Copyright 2024 The Sigstore 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 dev.sigstore.tuf.cli; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.Callable; +import picocli.CommandLine.Command; +import picocli.CommandLine.Parameters; +import picocli.CommandLine.ParentCommand; + +@Command(name = "init", description = "initialize a local tuf repo") +public class Init implements Callable { + + @Parameters(arity = "1", paramLabel = "") + Path trustedRoot; + + @ParentCommand private Tuf tufCommand; + + @Override + public Integer call() throws Exception { + var metadataDir = tufCommand.getMetadataDir(); + + if (!Files.isRegularFile(trustedRoot)) { + throw new IllegalArgumentException(trustedRoot + " is not a regular file"); + } + if (Files.exists(metadataDir)) { + if (!Files.isDirectory(metadataDir)) { + throw new IllegalArgumentException(metadataDir + " is not a directory"); + } + } else { + Files.createDirectories(metadataDir); + } + + Files.copy(trustedRoot, metadataDir.resolve("root.json")); + return 0; + } +} diff --git a/tuf-cli/src/main/java/dev/sigstore/tuf/cli/Refresh.java b/tuf-cli/src/main/java/dev/sigstore/tuf/cli/Refresh.java new file mode 100644 index 00000000..bcc206c5 --- /dev/null +++ b/tuf-cli/src/main/java/dev/sigstore/tuf/cli/Refresh.java @@ -0,0 +1,51 @@ +/* + * Copyright 2024 The Sigstore 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 dev.sigstore.tuf.cli; + +import dev.sigstore.tuf.FileSystemTufStore; +import dev.sigstore.tuf.HttpFetcher; +import dev.sigstore.tuf.MetaFetcher; +import dev.sigstore.tuf.PassthroughCacheMetaStore; +import dev.sigstore.tuf.RootProvider; +import dev.sigstore.tuf.TrustedMetaStore; +import dev.sigstore.tuf.Updater; +import java.util.concurrent.Callable; +import picocli.CommandLine.Command; +import picocli.CommandLine.ParentCommand; + +@Command(name = "refresh", description = "update local tuf metadata from the repository") +public class Refresh implements Callable { + + @ParentCommand private Tuf tufCommand; + + @Override + public Integer call() throws Exception { + var metadataDir = tufCommand.getMetadataDir(); + var metadataUrl = tufCommand.getMetadataUrl(); + + var fsStore = FileSystemTufStore.newFileSystemStore(metadataDir); + var tuf = + Updater.builder() + .setTrustedMetaStore( + TrustedMetaStore.newTrustedMetaStore( + PassthroughCacheMetaStore.newPassthroughMetaCache(fsStore))) + .setTrustedRootPath(RootProvider.fromFile(metadataDir.resolve("root.json"))) + .setMetaFetcher(MetaFetcher.newFetcher(HttpFetcher.newFetcher(metadataUrl))) + .build(); + tuf.updateMeta(); + return 0; + } +} diff --git a/tuf-cli/src/main/java/dev/sigstore/tuf/cli/Tuf.java b/tuf-cli/src/main/java/dev/sigstore/tuf/cli/Tuf.java new file mode 100644 index 00000000..e8e28aa1 --- /dev/null +++ b/tuf-cli/src/main/java/dev/sigstore/tuf/cli/Tuf.java @@ -0,0 +1,107 @@ +/* + * Copyright 2024 The Sigstore 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 dev.sigstore.tuf.cli; + +import java.net.URL; +import java.nio.file.Path; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Model.CommandSpec; +import picocli.CommandLine.Option; +import picocli.CommandLine.ParameterException; +import picocli.CommandLine.Spec; + +@Command( + name = "tuf", + mixinStandardHelpOptions = true, + subcommands = {Init.class, Refresh.class, Download.class}) +public class Tuf { + @Spec CommandSpec spec; + + public CommandSpec getSpec() { + return spec; + } + + @Option( + names = {"--metadata-dir"}, + required = false, + paramLabel = "") + private Path metadataDir; + + @Option( + names = {"--metadata-url"}, + required = false, + paramLabel = "") + private URL metadataUrl; + + @Option( + names = {"--target-name"}, + required = false, + paramLabel = "") + private String targetName; + + @Option( + names = {"--target-base-url"}, + required = false, + paramLabel = "") + private URL targetBaseUrl; + + @Option( + names = {"--target-dir"}, + required = false, + paramLabel = "") + private Path targetDir; + + Path getMetadataDir() { + if (metadataDir == null) { + throw new ParameterException(spec.commandLine(), "--metadata-dir not set"); + } + return metadataDir; + } + + URL getMetadataUrl() { + if (metadataUrl == null) { + throw new ParameterException(spec.commandLine(), "--metadata-url not set"); + } + return metadataUrl; + } + + String getTargetName() { + if (targetName == null) { + throw new ParameterException(spec.commandLine(), "--target-name not set"); + } + return targetName; + } + + URL getTargetBaseUrl() { + if (targetBaseUrl == null) { + throw new ParameterException(spec.commandLine(), "--target-base-url not set"); + } + return targetBaseUrl; + } + + Path getTargetDir() { + if (targetDir == null) { + throw new ParameterException(spec.commandLine(), "--target-dir not set"); + } + return targetDir; + } + + public static void main(String[] args) { + int exitCode = new CommandLine(new Tuf()).execute(args); + System.exit(exitCode); + } +} diff --git a/tuf-cli/tuf-cli.xfails b/tuf-cli/tuf-cli.xfails new file mode 100644 index 00000000..c3298724 --- /dev/null +++ b/tuf-cli/tuf-cli.xfails @@ -0,0 +1,45 @@ +test_metadata_bytes_match +test_client_downloads_expected_file_in_sub_dir +test_duplicate_sig_keyids +test_keytype_and_scheme[rsa/rsassa-pss-sha256] +test_keytype_and_scheme[ed25519/ed25519] +test_unusual_role_name[?] +test_unusual_role_name[#] +test_unusual_role_name[/delegatedrole] +test_unusual_role_name[../delegatedrole] +test_snapshot_rollback[basic] +test_snapshot_rollback[with +test_static_repository[tuf-on-ci-0.11] +test_graph_traversal[basic-delegation] +test_graph_traversal[single-level-delegations] +test_graph_traversal[two-level-delegations] +test_graph_traversal[two-level-test-DFS-order-of-traversal] +test_graph_traversal[three-level-delegation-test-DFS-order-of-traversal] +test_graph_traversal[two-level-terminating-ignores-all-but-roles-descendants] +test_graph_traversal[three-level-terminating-ignores-all-but-roles-descendants] +test_graph_traversal[two-level-ignores-all-branches-not-matching-paths] +test_graph_traversal[three-level-ignores-all-branches-not-matching-paths] +test_graph_traversal[cyclic-graph] +test_graph_traversal[two-roles-delegating-to-a-third] +test_graph_traversal[two-roles-delegating-to-a-third-different-paths] +test_targetfile_search[targetpath matches wildcard] +test_targetfile_search[targetpath with separators x] +test_targetfile_search[targetpath with separators y] +test_targetfile_search[targetpath is not delegated by all roles in the chain] +test_root_rotation[1-of-1-key-rotation] +test_root_rotation[1-of-1-key-rotation-unused-signatures] +test_root_rotation[3-of-5-sign-with-different-keycombos] +test_root_rotation[3-of-5-one-key-rotated] +test_root_rotation[3-of-5-one-key-rotated-with-intermediate-step] +test_root_rotation[3-of-5-all-keys-rotated-with-intermediate-step] +test_root_rotation[1-of-3-threshold-increase-to-2-of-3] +test_root_rotation[2-of-3-threshold-decrease-to-1-of-3] +test_root_rotation[1-of-2-threshold-increase-to-2-of-2] +test_non_root_rotations[1-of-1-key-rotation] +test_non_root_rotations[1-of-1-key-rotation-unused-signatures] +test_non_root_rotations[3-of-5-sign-first-combo] +test_non_root_rotations[3-of-5-sign-second-combo] +test_non_root_rotations[3-of-5-sign-third-combo] +test_non_root_rotations[3-of-5-sign-fourth-combo] +test_non_root_rotations[3-of-5-sign-fifth-combo] +test_snapshot_rollback[with hashes]