From 8b2e47ecf6f6465d7ea73b4cdd25fa0b420ae2f7 Mon Sep 17 00:00:00 2001 From: Dave Waltermire Date: Sun, 9 Jun 2024 11:41:58 -0400 Subject: [PATCH] Refactored CLI commands to simplify the command pathways for conversion, profile resolution, and validations to make these commands more CI/CD friendly. --- .../secauto/oscal/tools/cli/core/CLI.java | 6 + .../AbstractOscalConvertSubcommand.java | 26 +- .../AbstractOscalValidationSubcommand.java | 11 +- .../core/commands/AbstractResolveCommand.java | 322 ++++++++++++++++++ .../cli/core/commands/ConvertCommand.java | 98 ++++++ .../cli/core/commands/ResolveCommand.java | 41 +++ .../cli/core/commands/ValidateCommand.java | 71 ++++ .../assessmentplan/ConvertSubcommand.java | 2 +- .../assessmentplan/ValidateSubcommand.java | 4 +- .../assessmentresults/ConvertSubcommand.java | 2 +- .../assessmentresults/ValidateSubcommand.java | 4 +- .../commands/catalog/ConvertSubcommand.java | 2 +- .../commands/catalog/ValidateSubcommand.java | 4 +- .../ConvertSubcommand.java | 2 +- .../ValidateSubcommand.java | 4 +- ...ctDeprecatedOscalValidationSubcommand.java | 65 ++++ .../core/commands/poam/ConvertSubcommand.java | 2 +- .../commands/poam/ValidateSubcommand.java | 4 +- .../commands/profile/ConvertSubcommand.java | 2 +- .../commands/profile/ResolveSubcommand.java | 291 +--------------- .../commands/profile/ValidateSubcommand.java | 4 +- .../core/commands/ssp/ConvertSubcommand.java | 2 +- .../core/commands/ssp/ValidateSubcommand.java | 4 +- src/main/resources/log4j2.xml | 20 +- .../secauto/oscal/tools/cli/core/CLITest.java | 68 +++- .../core/commands/Issue96ClassLoaderTest.java | 2 - 26 files changed, 742 insertions(+), 321 deletions(-) rename src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/{oscal => }/AbstractOscalConvertSubcommand.java (75%) rename src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/{oscal => }/AbstractOscalValidationSubcommand.java (92%) create mode 100644 src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/AbstractResolveCommand.java create mode 100644 src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/ConvertCommand.java create mode 100644 src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/ResolveCommand.java create mode 100644 src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/ValidateCommand.java create mode 100644 src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/oscal/AbstractDeprecatedOscalValidationSubcommand.java diff --git a/src/main/java/gov/nist/secauto/oscal/tools/cli/core/CLI.java b/src/main/java/gov/nist/secauto/oscal/tools/cli/core/CLI.java index fecc835..31208f5 100644 --- a/src/main/java/gov/nist/secauto/oscal/tools/cli/core/CLI.java +++ b/src/main/java/gov/nist/secauto/oscal/tools/cli/core/CLI.java @@ -34,6 +34,9 @@ import gov.nist.secauto.metaschema.core.util.ObjectUtils; import gov.nist.secauto.oscal.lib.LibOscalVersion; import gov.nist.secauto.oscal.lib.OscalVersion; +import gov.nist.secauto.oscal.tools.cli.core.commands.ConvertCommand; +import gov.nist.secauto.oscal.tools.cli.core.commands.ResolveCommand; +import gov.nist.secauto.oscal.tools.cli.core.commands.ValidateCommand; import gov.nist.secauto.oscal.tools.cli.core.commands.assessmentplan.AssessmentPlanCommand; import gov.nist.secauto.oscal.tools.cli.core.commands.assessmentresults.AssessmentResultsCommand; import gov.nist.secauto.oscal.tools.cli.core.commands.catalog.CatalogCommand; @@ -75,6 +78,9 @@ public static ExitStatus runCli(String... args) { processor.addCommandHandler(new AssessmentResultsCommand()); processor.addCommandHandler(new PlanOfActionsAndMilestonesCommand()); processor.addCommandHandler(new MetaschemaCommand()); + processor.addCommandHandler(new ValidateCommand()); + processor.addCommandHandler(new ConvertCommand()); + processor.addCommandHandler(new ResolveCommand()); return processor.process(args); } diff --git a/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/oscal/AbstractOscalConvertSubcommand.java b/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/AbstractOscalConvertSubcommand.java similarity index 75% rename from src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/oscal/AbstractOscalConvertSubcommand.java rename to src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/AbstractOscalConvertSubcommand.java index 43b9f38..926fbe8 100644 --- a/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/oscal/AbstractOscalConvertSubcommand.java +++ b/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/AbstractOscalConvertSubcommand.java @@ -24,20 +24,31 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.oscal.tools.cli.core.commands.oscal; +package gov.nist.secauto.oscal.tools.cli.core.commands; import gov.nist.secauto.metaschema.cli.commands.AbstractConvertSubcommand; import gov.nist.secauto.metaschema.cli.processor.CLIProcessor.CallingContext; +import gov.nist.secauto.metaschema.cli.processor.ExitStatus; import gov.nist.secauto.metaschema.cli.processor.command.ICommandExecutor; import gov.nist.secauto.metaschema.databind.IBindingContext; +import gov.nist.secauto.metaschema.databind.io.Format; +import gov.nist.secauto.metaschema.databind.io.IBoundLoader; import gov.nist.secauto.oscal.lib.OscalBindingContext; import org.apache.commons.cli.CommandLine; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.Writer; +import java.net.URI; import edu.umd.cs.findbugs.annotations.NonNull; public abstract class AbstractOscalConvertSubcommand extends AbstractConvertSubcommand { + private static final Logger LOGGER = LogManager.getLogger(AbstractOscalConvertSubcommand.class); @NonNull public abstract Class getOscalClass(); @@ -62,8 +73,17 @@ protected IBindingContext getBindingContext() { } @Override - protected Class getLoadedClass() { - return getOscalClass(); + public ExitStatus execute() { + LOGGER.atWarn().log("This command path is deprecated. Please use 'convert'."); + + return super.execute(); + } + + @Override + protected void handleConversion(URI source, Format toFormat, Writer writer, IBoundLoader loader) + throws FileNotFoundException, IOException { + Class clazz = getOscalClass(); + loader.convert(source, writer, toFormat, clazz); } } } diff --git a/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/oscal/AbstractOscalValidationSubcommand.java b/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/AbstractOscalValidationSubcommand.java similarity index 92% rename from src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/oscal/AbstractOscalValidationSubcommand.java rename to src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/AbstractOscalValidationSubcommand.java index ebc5c21..8b0b6c6 100644 --- a/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/oscal/AbstractOscalValidationSubcommand.java +++ b/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/AbstractOscalValidationSubcommand.java @@ -24,7 +24,7 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.oscal.tools.cli.core.commands.oscal; +package gov.nist.secauto.oscal.tools.cli.core.commands; import gov.nist.secauto.metaschema.cli.commands.AbstractValidateContentCommand; import gov.nist.secauto.metaschema.cli.processor.CLIProcessor.CallingContext; @@ -39,6 +39,7 @@ import org.json.JSONObject; import java.io.IOException; +import java.net.URL; import java.util.List; import java.util.Set; @@ -60,10 +61,10 @@ public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine c return new OscalCommandExecutor(callingContext, commandLine); } - private final class OscalCommandExecutor + protected class OscalCommandExecutor extends AbstractValidationCommandExecutor { - private OscalCommandExecutor( + protected OscalCommandExecutor( @NonNull CallingContext callingContext, @NonNull CommandLine commandLine) { super(callingContext, commandLine); @@ -85,13 +86,13 @@ protected IBindingContext getBindingContext(@NonNull Set constra @Override @NonNull - public List getXmlSchemas() throws IOException { + public List getXmlSchemas(URL targetResource) throws IOException { return getOscalXmlSchemas(); } @Override @NonNull - public JSONObject getJsonSchema() throws IOException { + public JSONObject getJsonSchema(JSONObject json) throws IOException { return getOscalJsonSchema(); } } diff --git a/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/AbstractResolveCommand.java b/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/AbstractResolveCommand.java new file mode 100644 index 0000000..fdd8da0 --- /dev/null +++ b/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/AbstractResolveCommand.java @@ -0,0 +1,322 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.oscal.tools.cli.core.commands; + +import gov.nist.secauto.metaschema.cli.processor.CLIProcessor.CallingContext; +import gov.nist.secauto.metaschema.cli.processor.ExitCode; +import gov.nist.secauto.metaschema.cli.processor.ExitStatus; +import gov.nist.secauto.metaschema.cli.processor.InvalidArgumentException; +import gov.nist.secauto.metaschema.cli.processor.OptionUtils; +import gov.nist.secauto.metaschema.cli.processor.command.AbstractTerminalCommand; +import gov.nist.secauto.metaschema.cli.processor.command.DefaultExtraArgument; +import gov.nist.secauto.metaschema.cli.processor.command.ExtraArgument; +import gov.nist.secauto.metaschema.cli.processor.command.ICommandExecutor; +import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.StaticContext; +import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; +import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; +import gov.nist.secauto.metaschema.core.util.CustomCollectors; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; +import gov.nist.secauto.metaschema.databind.io.DeserializationFeature; +import gov.nist.secauto.metaschema.databind.io.Format; +import gov.nist.secauto.metaschema.databind.io.IBoundLoader; +import gov.nist.secauto.metaschema.databind.io.ISerializer; +import gov.nist.secauto.oscal.lib.OscalBindingContext; +import gov.nist.secauto.oscal.lib.model.Catalog; +import gov.nist.secauto.oscal.lib.model.Profile; +import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionException; +import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolver; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Locale; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public abstract class AbstractResolveCommand + extends AbstractTerminalCommand { + @NonNull + private static final List EXTRA_ARGUMENTS = ObjectUtils.notNull(List.of( + new DefaultExtraArgument("file to resolve", true), + new DefaultExtraArgument("destination file", false))); + @NonNull + private static final Option AS_OPTION = ObjectUtils.notNull( + Option.builder() + .longOpt("as") + .hasArg() + .argName("FORMAT") + .desc("source format: xml, json, or yaml") + .build()); + @NonNull + private static final Option TO_OPTION = ObjectUtils.notNull( + Option.builder() + .longOpt("to") + .required() + .hasArg().argName("FORMAT") + .desc("convert to format: xml, json, or yaml") + .build()); + @NonNull + private static final Option OVERWRITE_OPTION = ObjectUtils.notNull( + Option.builder() + .longOpt("overwrite") + .desc("overwrite the destination if it exists") + .build()); + @NonNull + private static final List