From 24c0cd8a96828415b9ce11ca87f2c429d329f883 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 5 Mar 2021 14:15:54 +0100 Subject: [PATCH] SOP: Properly return error codes to the caller, error code 37 for invalid input args --- .../org/pgpainless/sop/PGPainlessCLI.java | 10 ++++- .../org/pgpainless/sop/commands/Armor.java | 3 +- .../org/pgpainless/sop/commands/Dearmor.java | 3 +- .../org/pgpainless/sop/commands/Decrypt.java | 3 +- .../org/pgpainless/sop/commands/Encrypt.java | 3 +- .../pgpainless/sop/commands/ExtractCert.java | 3 +- .../pgpainless/sop/commands/GenerateKey.java | 16 ++++---- .../org/pgpainless/sop/commands/Sign.java | 3 +- .../org/pgpainless/sop/commands/Verify.java | 3 +- .../org/pgpainless/sop/commands/Version.java | 3 +- .../java/org/pgpainless/sop/ExitCodeTest.java | 39 +++++++++++++++++++ 11 files changed, 72 insertions(+), 17 deletions(-) create mode 100644 pgpainless-sop/src/test/java/org/pgpainless/sop/ExitCodeTest.java diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/PGPainlessCLI.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/PGPainlessCLI.java index 1cc7cf4e..b1d85dae 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/PGPainlessCLI.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/PGPainlessCLI.java @@ -26,7 +26,7 @@ import org.pgpainless.sop.commands.Verify; import org.pgpainless.sop.commands.Version; import picocli.CommandLine; -@CommandLine.Command( +@CommandLine.Command(exitCodeOnInvalidInput = 69, subcommands = { Armor.class, Dearmor.class, @@ -41,8 +41,14 @@ import picocli.CommandLine; ) public class PGPainlessCLI implements Runnable { + public PGPainlessCLI() { + + } + public static void main(String[] args) { - CommandLine.run(new PGPainlessCLI(), args); + int code = new CommandLine(new PGPainlessCLI()) + .execute(args); + System.exit(code); } @Override diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Armor.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Armor.java index c93998ed..955c8e21 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Armor.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Armor.java @@ -28,7 +28,8 @@ import java.util.Arrays; import static org.pgpainless.sop.Print.err_ln; @CommandLine.Command(name = "armor", - description = "Add ASCII Armor to standard input") + description = "Add ASCII Armor to standard input", + exitCodeOnInvalidInput = 37) public class Armor implements Runnable { private static final byte[] BEGIN_ARMOR = "-----BEGIN PGP".getBytes(StandardCharsets.UTF_8); diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Dearmor.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Dearmor.java index 737049bb..5de54b8c 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Dearmor.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Dearmor.java @@ -24,7 +24,8 @@ import java.io.IOException; import static org.pgpainless.sop.Print.err_ln; @CommandLine.Command(name = "dearmor", - description = "Remove ASCII Armor from standard input") + description = "Remove ASCII Armor from standard input", + exitCodeOnInvalidInput = 37) public class Dearmor implements Runnable { @Override diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Decrypt.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Decrypt.java index 4d1ca712..36f77bf0 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Decrypt.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Decrypt.java @@ -43,7 +43,8 @@ import org.pgpainless.key.OpenPgpV4Fingerprint; import picocli.CommandLine; @CommandLine.Command(name = "decrypt", - description = "Decrypt a message from standard input") + description = "Decrypt a message from standard input", + exitCodeOnInvalidInput = 37) public class Decrypt implements Runnable { private static final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Encrypt.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Encrypt.java index 3f4fb812..afc0dd74 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Encrypt.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Encrypt.java @@ -44,7 +44,8 @@ import org.pgpainless.util.Passphrase; import picocli.CommandLine; @CommandLine.Command(name = "encrypt", - description = "Encrypt a message from standard input") + description = "Encrypt a message from standard input", + exitCodeOnInvalidInput = 37) public class Encrypt implements Runnable { public enum Type { diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/ExtractCert.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/ExtractCert.java index 30ec1043..a2f46757 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/ExtractCert.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/ExtractCert.java @@ -29,7 +29,8 @@ import org.pgpainless.sop.Print; import picocli.CommandLine; @CommandLine.Command(name = "extract-cert", - description = "Extract a public key certificate from a secret key from standard input") + description = "Extract a public key certificate from a secret key from standard input", + exitCodeOnInvalidInput = 37) public class ExtractCert implements Runnable { @CommandLine.Option(names = "--no-armor", diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/GenerateKey.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/GenerateKey.java index 99618235..3cb40532 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/GenerateKey.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/GenerateKey.java @@ -38,7 +38,9 @@ import picocli.CommandLine; import static org.pgpainless.sop.Print.err_ln; import static org.pgpainless.sop.Print.print_ln; -@CommandLine.Command(name = "generate-key", description = "Generate a secret key") +@CommandLine.Command(name = "generate-key", + description = "Generate a secret key", + exitCodeOnInvalidInput = 37) public class GenerateKey implements Runnable { @CommandLine.Option(names = "--no-armor", @@ -51,6 +53,12 @@ public class GenerateKey implements Runnable { @Override public void run() { + if (userId.isEmpty()) { + print_ln("At least one user-id expected."); + System.exit(1); + return; + } + try { KeyRingBuilderInterface.WithAdditionalUserIdOrPassphrase builder = PGPainless.generateKeyRing() .withSubKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)) @@ -64,12 +72,6 @@ public class GenerateKey implements Runnable { .withDefaultAlgorithms()) .withPrimaryUserId(userId.get(0)); - if (userId.isEmpty()) { - print_ln("At least one user-id expected."); - System.exit(1); - return; - } - for (int i = 1; i < userId.size(); i++) { builder.withAdditionalUserId(userId.get(i)); } diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Sign.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Sign.java index 7984e141..3a607705 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Sign.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Sign.java @@ -35,7 +35,8 @@ import static org.pgpainless.sop.Print.err_ln; import static org.pgpainless.sop.Print.print_ln; @CommandLine.Command(name = "sign", - description = "Create a detached signature on the data from standard input") + description = "Create a detached signature on the data from standard input", + exitCodeOnInvalidInput = 37) public class Sign implements Runnable { public enum Type { diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java index cb66c7a6..1e0ab95d 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Verify.java @@ -43,7 +43,8 @@ import static org.pgpainless.sop.Print.err_ln; import static org.pgpainless.sop.Print.print_ln; @CommandLine.Command(name = "verify", - description = "Verify a detached signature over the data from standard input") + description = "Verify a detached signature over the data from standard input", + exitCodeOnInvalidInput = 37) public class Verify implements Runnable { private static final TimeZone tz = TimeZone.getTimeZone("UTC"); diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Version.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Version.java index 1aafa4a5..e3974739 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Version.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/commands/Version.java @@ -22,7 +22,8 @@ import java.util.Properties; import picocli.CommandLine; -@CommandLine.Command(name = "version", description = "Display version information about the tool") +@CommandLine.Command(name = "version", description = "Display version information about the tool", + exitCodeOnInvalidInput = 37) public class Version implements Runnable { @Override diff --git a/pgpainless-sop/src/test/java/org/pgpainless/sop/ExitCodeTest.java b/pgpainless-sop/src/test/java/org/pgpainless/sop/ExitCodeTest.java new file mode 100644 index 00000000..e8933b19 --- /dev/null +++ b/pgpainless-sop/src/test/java/org/pgpainless/sop/ExitCodeTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2021 Paul Schaub. + * + * 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 org.pgpainless.sop; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import picocli.CommandLine; + +public class ExitCodeTest { + + @Test + public void testUnknownCommand_69() { + assertEquals(69, new CommandLine(new PGPainlessCLI()).execute("generate-kex")); + } + + @Test + public void testCommandWithUnknownOption_37() { + assertEquals(37, new CommandLine(new PGPainlessCLI()).execute("generate-key", "-k", "\"k is unknown\"")); + } + + @Test + public void successfulVersion_0 () { + assertEquals(0, new CommandLine(new PGPainlessCLI()).execute("version")); + } +}