From 4d70917297a21ef65f9c0903b2a3371ea71b5559 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Struewer Date: Wed, 4 Sep 2024 11:06:36 +0200 Subject: [PATCH 1/2] chore: updated library version --- .gitignore | 5 ++- library | 2 +- .../commands/TransformToolResultCommand.kt | 31 ++++++++++++------- .../cli/transformer/Tool2RawKpiTransformer.kt | 13 ++++---- .../TransformToolResultCommandTest.kt | 26 +++++++++------- .../transformer/Tool2RawKpiTransformerTest.kt | 4 +-- 6 files changed, 48 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index c3c5454..7b0e968 100644 --- a/.gitignore +++ b/.gitignore @@ -282,4 +282,7 @@ gradle-app.setting # Java heap dump *.hprof -# End of https://www.toptal.com/developers/gitignore/api/kotlin,gradle,intellij,intellij+iml,intellij+all \ No newline at end of file +### MAC Stuff ### +.DS_Store + +# End of https://www.toptal.com/developers/gitignore/api/kotlin,gradle,intellij,intellij+iml,intellij+all diff --git a/library b/library index 79aa627..200ccab 160000 --- a/library +++ b/library @@ -1 +1 @@ -Subproject commit 79aa6279906b6df5a63c57cec3e138408c600b66 +Subproject commit 200ccab3c18b46ea4cde5fd6e3d6c43d6d82f7ca diff --git a/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommand.kt b/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommand.kt index a6f450f..79f4c4e 100644 --- a/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommand.kt +++ b/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommand.kt @@ -3,7 +3,6 @@ package de.fraunhofer.iem.spha.cli.commands import com.github.ajalt.clikt.parameters.options.multiple import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.options.required -import de.fraunhofer.iem.kpiCalculator.adapter.tools.SupportedTool import de.fraunhofer.iem.spha.cli.SphaToolCommandBase import de.fraunhofer.iem.spha.cli.transformer.RawKpiTransformer import de.fraunhofer.iem.spha.cli.transformer.TransformerOptions @@ -16,25 +15,33 @@ import java.nio.file.FileSystem import java.nio.file.Path import kotlin.io.path.createDirectories -internal class TransformToolResultCommand : SphaToolCommandBase(name = "transform", +internal class TransformToolResultCommand : SphaToolCommandBase( + name = "transform", help = "transforms a specified KPI-provider (such as a SAST tool) result into a uniform data format, " + - "so that it can be used for the 'calculate' command."), KoinComponent { + "so that it can be used for the 'calculate' command." +), KoinComponent { private val transformer by inject() private val fileSystem by inject() - private val toolName by option("-t", "--tool", + private val toolName by option( + "-t", "--tool", help = "The identifier of the KPI-provider tool that produced the input. " + - "Use the command --list-tools to get a list of available identifiers.") - .required() + "Use the command --list-tools to get a list of available identifiers." + ) + .required() - private val inputFiles by option("-i", "--inputFile", + private val inputFiles by option( + "-i", "--inputFile", help = "List of input files. Usually these are result files produced by the tool as specified by --tool." + - "To specify multiple input files (if supported by --tool), the option can be used multiple times.") + "To specify multiple input files (if supported by --tool), the option can be used multiple times." + ) .multiple() - private val output by option("-o", "--output", - help = "The output directory where the result of the operation is stored. Default is the current working directory.") + private val output by option( + "-o", "--output", + help = "The output directory where the result of the operation is stored. Default is the current working directory." + ) @OptIn(ExperimentalSerializationApi::class) @@ -43,7 +50,7 @@ internal class TransformToolResultCommand : SphaToolCommandBase(name = "transfor val result = transformer.getRawKpis(TransformerOptions(toolName, inputFiles), strict) val resultPath = getResultFilePath() fileSystem.provider().newOutputStream(resultPath).use { - Logger.trace{ "Storing result to '$resultPath'" } + Logger.trace { "Storing result to '$resultPath'" } Json.encodeToStream(result, it) } } @@ -58,7 +65,7 @@ internal class TransformToolResultCommand : SphaToolCommandBase(name = "transfor return location.resolve(fileName).toAbsolutePath() } - companion object{ + companion object { internal const val RESULT_FILE_SUFFIX = "-result.json" } } diff --git a/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformer.kt b/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformer.kt index fa80e52..01182fd 100644 --- a/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformer.kt +++ b/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformer.kt @@ -1,7 +1,6 @@ package de.fraunhofer.iem.spha.cli.transformer import de.fraunhofer.iem.kpiCalculator.adapter.AdapterResult -import de.fraunhofer.iem.kpiCalculator.adapter.tools.ToolNotFoundException import de.fraunhofer.iem.kpiCalculator.adapter.tools.trivy.TrivyAdapter import de.fraunhofer.iem.kpiCalculator.model.adapter.trivy.TrivyDto import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpi @@ -12,10 +11,12 @@ import org.koin.core.component.inject import java.io.InputStream import java.nio.file.FileSystem -internal interface RawKpiTransformer { - fun getRawKpis(options: TransformerOptions, strictMode: Boolean) : Collection +internal fun interface RawKpiTransformer { + fun getRawKpis(options: TransformerOptions, strictMode: Boolean): Collection } +class ToolNotFoundException(message: String) : Exception(message) + internal class Tool2RawKpiTransformer : RawKpiTransformer, KoinComponent { private val _fileSystem by inject() @@ -32,7 +33,7 @@ internal class Tool2RawKpiTransformer : RawKpiTransformer, KoinComponent { getSingleInputStreamFromInputFile(options.inputFiles, strictMode).use { _logger.info { "Selected supported tool: Trivy" } val adapterInput: TrivyDto = TrivyAdapter.dtoFromJson(it) - return@use TrivyAdapter.transformDataToKpi(adapterInput) + return@use TrivyAdapter.transformDataToKpi(listOf(adapterInput)) } } @@ -54,8 +55,8 @@ internal class Tool2RawKpiTransformer : RawKpiTransformer, KoinComponent { } internal fun getSingleInputStreamFromInputFile(inputFiles: List?, strictMode: Boolean): InputStream { - if (inputFiles.isNullOrEmpty()) { - throw IllegalStateException("No input files specified.") + check(!inputFiles.isNullOrEmpty()) { + "No input files specified." } if (inputFiles.count() > 1) { diff --git a/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommandTest.kt b/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommandTest.kt index eacd94c..28b559a 100644 --- a/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommandTest.kt +++ b/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommandTest.kt @@ -3,12 +3,11 @@ package de.fraunhofer.iem.spha.cli.commands import com.github.ajalt.clikt.testing.test import com.google.common.jimfs.Configuration import com.google.common.jimfs.Jimfs -import de.fraunhofer.iem.kpiCalculator.adapter.tools.SupportedTool -import de.fraunhofer.iem.kpiCalculator.adapter.tools.ToolNotFoundException import de.fraunhofer.iem.kpiCalculator.model.kpi.KpiId import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpi import de.fraunhofer.iem.spha.cli.appModules import de.fraunhofer.iem.spha.cli.transformer.RawKpiTransformer +import de.fraunhofer.iem.spha.cli.transformer.ToolNotFoundException import de.fraunhofer.iem.spha.cli.transformer.TransformerOptions import io.mockk.every import io.mockk.mockkClass @@ -59,7 +58,7 @@ class TransformToolResultCommandTest : KoinTest { @Test fun testTransform_TransformerInternal_Throws() { - val toolName = SupportedTool.Occmd.name + val toolName = "occmd" val fileSystem = declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } @@ -78,10 +77,10 @@ class TransformToolResultCommandTest : KoinTest { @ParameterizedTest @ValueSource(booleans = [false, true]) fun testTransform_StrictModeApplied(expectedStrict: Boolean) { - val toolName = SupportedTool.Occmd.name + val toolName = "occmd" declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } - val transformer = declareMock{ + val transformer = declareMock { every { getRawKpis(any(), any()) } returns listOf() } val strictCommandInput = if (expectedStrict) "--strict" else "" @@ -93,7 +92,7 @@ class TransformToolResultCommandTest : KoinTest { @Test fun testTransform_ResultFileOverwrite() { - val toolName = SupportedTool.Occmd.name + val toolName = "occmd" val fileSystem = declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } @@ -115,11 +114,12 @@ class TransformToolResultCommandTest : KoinTest { @Test fun testTransform_ResultFileSerialize() { - val toolName = SupportedTool.Occmd.name + val toolName = "occmd" val resultList = listOf( RawValueKpi(KpiId.SECRETS, 100), - RawValueKpi(KpiId.SECURITY, 1)) + RawValueKpi(KpiId.SECURITY, 1) + ) val fileSystem = declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } @@ -155,7 +155,7 @@ class TransformToolResultCommandTest : KoinTest { val toolName = "toolA" declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } - val transformer = declareMock{ + val transformer = declareMock { every { getRawKpis(any(), any()) } returns listOf() } TransformToolResultCommand().test("-t $toolName $inputArg") @@ -164,14 +164,18 @@ class TransformToolResultCommandTest : KoinTest { verify(exactly = 1) { transformer.getRawKpis(eq(options), any()) } } - companion object{ + companion object { @JvmStatic fun outputTestSource(): List { val toolName = "toolA" return listOf( arguments("toolA", ".", "/work/$toolName${TransformToolResultCommand.RESULT_FILE_SUFFIX}"), arguments("toolA", "dir", "/work/dir/$toolName${TransformToolResultCommand.RESULT_FILE_SUFFIX}"), - arguments("toolA", "/other/dir", "/other/dir/$toolName${TransformToolResultCommand.RESULT_FILE_SUFFIX}"), + arguments( + "toolA", + "/other/dir", + "/other/dir/$toolName${TransformToolResultCommand.RESULT_FILE_SUFFIX}" + ), // This is a misuse, but it should work nether the less. arguments("toolA", "/file.txt", "/file.txt/$toolName${TransformToolResultCommand.RESULT_FILE_SUFFIX}") ) diff --git a/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformerTest.kt b/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformerTest.kt index 3859ca9..9d734b4 100644 --- a/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformerTest.kt +++ b/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformerTest.kt @@ -81,7 +81,7 @@ class Tool2RawKpiTransformerTest : KoinTest { @Test fun getRawKpis_Trivy() { val fileSystem = declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } - fileSystem.provider().newOutputStream(fileSystem.getPath("a")).use { } + fileSystem.provider().newOutputStream(fileSystem.getPath("a")).use { } val trivyVulns = listOf( VulnerabilityDto("A", "1", 1.0), @@ -96,6 +96,6 @@ class Tool2RawKpiTransformerTest : KoinTest { assertEquals(2, kpis.count()) verify(exactly = 1) { TrivyAdapter.dtoFromJson(any()) } - verify(exactly = 1) { TrivyAdapter.transformDataToKpi(eq(TrivyDto(trivyVulns))) } + verify(exactly = 1) { TrivyAdapter.transformDataToKpi(eq(listOf(TrivyDto(trivyVulns)))) } } } From e755b8b514559146163082a8b83d7e90fe82496e Mon Sep 17 00:00:00 2001 From: Jan-Niclas Struewer Date: Wed, 4 Sep 2024 17:52:20 +0200 Subject: [PATCH 2/2] refactor: applied formatting --- .editorconfig | 441 +----------------- build.gradle.kts | 26 +- spha-cli/build.gradle.kts | 20 +- spha-cli/gradle/libs.versions.toml | 2 + .../kotlin/de/fraunhofer/iem/spha/cli/Main.kt | 78 ++-- .../commands/TransformToolResultCommand.kt | 61 ++- .../cli/transformer/Tool2RawKpiTransformer.kt | 62 +-- .../cli/transformer/TransformerOptions.kt | 5 +- .../TransformToolResultCommandTest.kt | 88 ++-- .../transformer/Tool2RawKpiTransformerTest.kt | 55 +-- 10 files changed, 236 insertions(+), 602 deletions(-) diff --git a/.editorconfig b/.editorconfig index bf37d91..85d6e05 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,410 +1,40 @@ -root = true +# This .editorconfig section approximates ktfmt's formatting rules. You can include it in an +# existing .editorconfig file or use it standalone by copying it to /.editorconfig +# and making sure your editor is set to read settings from .editorconfig files. +# +# It includes editor-specific config options for IntelliJ IDEA. +# +# If any option is wrong, PR are welcome -[*] -charset = utf-8 -end_of_line = lf -indent_size = 4 +[{*.kt,*.kts}] indent_style = space insert_final_newline = true -max_line_length = 120 -tab_width = 4 -ij_continuation_indent_size = 4 -ij_formatter_off_tag = @formatter:off -ij_formatter_on_tag = @formatter:on -ij_formatter_tags_enabled = true -ij_smart_tabs = false -ij_visual_guides = -ij_wrap_on_typing = false - -[*.java] -ij_java_align_consecutive_assignments = false -ij_java_align_consecutive_variable_declarations = false -ij_java_align_group_field_declarations = false -ij_java_align_multiline_annotation_parameters = false -ij_java_align_multiline_array_initializer_expression = false -ij_java_align_multiline_assignment = false -ij_java_align_multiline_binary_operation = false -ij_java_align_multiline_chained_methods = false -ij_java_align_multiline_deconstruction_list_components = true -ij_java_align_multiline_extends_list = false -ij_java_align_multiline_for = true -ij_java_align_multiline_method_parentheses = false -ij_java_align_multiline_parameters = true -ij_java_align_multiline_parameters_in_calls = false -ij_java_align_multiline_parenthesized_expression = false -ij_java_align_multiline_records = true -ij_java_align_multiline_resources = true -ij_java_align_multiline_ternary_operation = false -ij_java_align_multiline_text_blocks = false -ij_java_align_multiline_throws_list = false -ij_java_align_subsequent_simple_methods = false -ij_java_align_throws_keyword = false -ij_java_align_types_in_multi_catch = true -ij_java_annotation_parameter_wrap = off -ij_java_array_initializer_new_line_after_left_brace = false -ij_java_array_initializer_right_brace_on_new_line = false -ij_java_array_initializer_wrap = off -ij_java_assert_statement_colon_on_next_line = false -ij_java_assert_statement_wrap = off -ij_java_assignment_wrap = off -ij_java_binary_operation_sign_on_next_line = false -ij_java_binary_operation_wrap = off -ij_java_blank_lines_after_anonymous_class_header = 0 -ij_java_blank_lines_after_class_header = 0 -ij_java_blank_lines_after_imports = 1 -ij_java_blank_lines_after_package = 1 -ij_java_blank_lines_around_class = 1 -ij_java_blank_lines_around_field = 0 -ij_java_blank_lines_around_field_in_interface = 0 -ij_java_blank_lines_around_initializer = 1 -ij_java_blank_lines_around_method = 1 -ij_java_blank_lines_around_method_in_interface = 1 -ij_java_blank_lines_before_class_end = 0 -ij_java_blank_lines_before_imports = 1 -ij_java_blank_lines_before_method_body = 0 -ij_java_blank_lines_before_package = 0 -ij_java_block_brace_style = end_of_line -ij_java_block_comment_add_space = false -ij_java_block_comment_at_first_column = true -ij_java_builder_methods = -ij_java_call_parameters_new_line_after_left_paren = false -ij_java_call_parameters_right_paren_on_new_line = false -ij_java_call_parameters_wrap = off -ij_java_case_statement_on_separate_line = true -ij_java_catch_on_new_line = false -ij_java_class_annotation_wrap = split_into_lines -ij_java_class_brace_style = end_of_line -ij_java_class_count_to_use_import_on_demand = 5 -ij_java_class_names_in_javadoc = 1 -ij_java_deconstruction_list_wrap = normal -ij_java_do_not_indent_top_level_class_members = false -ij_java_do_not_wrap_after_single_annotation = false -ij_java_do_not_wrap_after_single_annotation_in_parameter = false -ij_java_do_while_brace_force = never -ij_java_doc_add_blank_line_after_description = true -ij_java_doc_add_blank_line_after_param_comments = false -ij_java_doc_add_blank_line_after_return = false -ij_java_doc_add_p_tag_on_empty_lines = true -ij_java_doc_align_exception_comments = true -ij_java_doc_align_param_comments = true -ij_java_doc_do_not_wrap_if_one_line = false -ij_java_doc_enable_formatting = true -ij_java_doc_enable_leading_asterisks = true -ij_java_doc_indent_on_continuation = false -ij_java_doc_keep_empty_lines = true -ij_java_doc_keep_empty_parameter_tag = true -ij_java_doc_keep_empty_return_tag = true -ij_java_doc_keep_empty_throws_tag = true -ij_java_doc_keep_invalid_tags = true -ij_java_doc_param_description_on_new_line = false -ij_java_doc_preserve_line_breaks = false -ij_java_doc_use_throws_not_exception_tag = true -ij_java_else_on_new_line = false -ij_java_entity_dd_prefix = -ij_java_entity_dd_suffix = EJB -ij_java_entity_eb_prefix = -ij_java_entity_eb_suffix = Bean -ij_java_entity_hi_prefix = -ij_java_entity_hi_suffix = Home -ij_java_entity_lhi_prefix = Local -ij_java_entity_lhi_suffix = Home -ij_java_entity_li_prefix = Local -ij_java_entity_li_suffix = -ij_java_entity_pk_class = java.lang.String -ij_java_entity_ri_prefix = -ij_java_entity_ri_suffix = -ij_java_entity_vo_prefix = -ij_java_entity_vo_suffix = VO -ij_java_enum_constants_wrap = off -ij_java_extends_keyword_wrap = off -ij_java_extends_list_wrap = off -ij_java_field_annotation_wrap = split_into_lines -ij_java_field_name_prefix = -ij_java_field_name_suffix = -ij_java_filter_class_prefix = -ij_java_filter_class_suffix = -ij_java_filter_dd_prefix = -ij_java_filter_dd_suffix = -ij_java_finally_on_new_line = false -ij_java_for_brace_force = never -ij_java_for_statement_new_line_after_left_paren = false -ij_java_for_statement_right_paren_on_new_line = false -ij_java_for_statement_wrap = off -ij_java_generate_final_locals = false -ij_java_generate_final_parameters = false -ij_java_if_brace_force = never -ij_java_imports_layout = *, |, javax.**, java.**, |, $* -ij_java_indent_case_from_switch = true -ij_java_insert_inner_class_imports = false -ij_java_insert_override_annotation = true -ij_java_keep_blank_lines_before_right_brace = 2 -ij_java_keep_blank_lines_between_package_declaration_and_header = 2 -ij_java_keep_blank_lines_in_code = 2 -ij_java_keep_blank_lines_in_declarations = 2 -ij_java_keep_builder_methods_indents = false -ij_java_keep_control_statement_in_one_line = true -ij_java_keep_first_column_comment = true -ij_java_keep_indents_on_empty_lines = false -ij_java_keep_line_breaks = true -ij_java_keep_multiple_expressions_in_one_line = false -ij_java_keep_simple_blocks_in_one_line = false -ij_java_keep_simple_classes_in_one_line = false -ij_java_keep_simple_lambdas_in_one_line = false -ij_java_keep_simple_methods_in_one_line = false -ij_java_label_indent_absolute = false -ij_java_label_indent_size = 0 -ij_java_lambda_brace_style = end_of_line -ij_java_layout_static_imports_separately = true -ij_java_line_comment_add_space = false -ij_java_line_comment_add_space_on_reformat = false -ij_java_line_comment_at_first_column = true -ij_java_listener_class_prefix = -ij_java_listener_class_suffix = -ij_java_local_variable_name_prefix = -ij_java_local_variable_name_suffix = -ij_java_message_dd_prefix = -ij_java_message_dd_suffix = EJB -ij_java_message_eb_prefix = -ij_java_message_eb_suffix = Bean -ij_java_method_annotation_wrap = split_into_lines -ij_java_method_brace_style = end_of_line -ij_java_method_call_chain_wrap = off -ij_java_method_parameters_new_line_after_left_paren = false -ij_java_method_parameters_right_paren_on_new_line = false -ij_java_method_parameters_wrap = off -ij_java_modifier_list_wrap = false -ij_java_multi_catch_types_wrap = normal -ij_java_names_count_to_use_import_on_demand = 3 -ij_java_new_line_after_lparen_in_annotation = false -ij_java_new_line_after_lparen_in_deconstruction_pattern = true -ij_java_new_line_after_lparen_in_record_header = false -ij_java_packages_to_use_import_on_demand = java.awt.*, javax.swing.* -ij_java_parameter_annotation_wrap = off -ij_java_parameter_name_prefix = -ij_java_parameter_name_suffix = -ij_java_parentheses_expression_new_line_after_left_paren = false -ij_java_parentheses_expression_right_paren_on_new_line = false -ij_java_place_assignment_sign_on_next_line = false -ij_java_prefer_longer_names = true -ij_java_prefer_parameters_wrap = false -ij_java_record_components_wrap = normal -ij_java_repeat_synchronized = true -ij_java_replace_instanceof_and_cast = false -ij_java_replace_null_check = true -ij_java_replace_sum_lambda_with_method_ref = true -ij_java_resource_list_new_line_after_left_paren = false -ij_java_resource_list_right_paren_on_new_line = false -ij_java_resource_list_wrap = off -ij_java_rparen_on_new_line_in_annotation = false -ij_java_rparen_on_new_line_in_deconstruction_pattern = true -ij_java_rparen_on_new_line_in_record_header = false -ij_java_servlet_class_prefix = -ij_java_servlet_class_suffix = -ij_java_servlet_dd_prefix = -ij_java_servlet_dd_suffix = -ij_java_session_dd_prefix = -ij_java_session_dd_suffix = EJB -ij_java_session_eb_prefix = -ij_java_session_eb_suffix = Bean -ij_java_session_hi_prefix = -ij_java_session_hi_suffix = Home -ij_java_session_lhi_prefix = Local -ij_java_session_lhi_suffix = Home -ij_java_session_li_prefix = Local -ij_java_session_li_suffix = -ij_java_session_ri_prefix = -ij_java_session_ri_suffix = -ij_java_session_si_prefix = -ij_java_session_si_suffix = Service -ij_java_space_after_closing_angle_bracket_in_type_argument = false -ij_java_space_after_colon = true -ij_java_space_after_comma = true -ij_java_space_after_comma_in_type_arguments = true -ij_java_space_after_for_semicolon = true -ij_java_space_after_quest = true -ij_java_space_after_type_cast = true -ij_java_space_before_annotation_array_initializer_left_brace = false -ij_java_space_before_annotation_parameter_list = false -ij_java_space_before_array_initializer_left_brace = false -ij_java_space_before_catch_keyword = true -ij_java_space_before_catch_left_brace = true -ij_java_space_before_catch_parentheses = true -ij_java_space_before_class_left_brace = true -ij_java_space_before_colon = true -ij_java_space_before_colon_in_foreach = true -ij_java_space_before_comma = false -ij_java_space_before_deconstruction_list = false -ij_java_space_before_do_left_brace = true -ij_java_space_before_else_keyword = true -ij_java_space_before_else_left_brace = true -ij_java_space_before_finally_keyword = true -ij_java_space_before_finally_left_brace = true -ij_java_space_before_for_left_brace = true -ij_java_space_before_for_parentheses = true -ij_java_space_before_for_semicolon = false -ij_java_space_before_if_left_brace = true -ij_java_space_before_if_parentheses = true -ij_java_space_before_method_call_parentheses = false -ij_java_space_before_method_left_brace = true -ij_java_space_before_method_parentheses = false -ij_java_space_before_opening_angle_bracket_in_type_parameter = false -ij_java_space_before_quest = true -ij_java_space_before_switch_left_brace = true -ij_java_space_before_switch_parentheses = true -ij_java_space_before_synchronized_left_brace = true -ij_java_space_before_synchronized_parentheses = true -ij_java_space_before_try_left_brace = true -ij_java_space_before_try_parentheses = true -ij_java_space_before_type_parameter_list = false -ij_java_space_before_while_keyword = true -ij_java_space_before_while_left_brace = true -ij_java_space_before_while_parentheses = true -ij_java_space_inside_one_line_enum_braces = false -ij_java_space_within_empty_array_initializer_braces = false -ij_java_space_within_empty_method_call_parentheses = false -ij_java_space_within_empty_method_parentheses = false -ij_java_spaces_around_additive_operators = true -ij_java_spaces_around_annotation_eq = true -ij_java_spaces_around_assignment_operators = true -ij_java_spaces_around_bitwise_operators = true -ij_java_spaces_around_equality_operators = true -ij_java_spaces_around_lambda_arrow = true -ij_java_spaces_around_logical_operators = true -ij_java_spaces_around_method_ref_dbl_colon = false -ij_java_spaces_around_multiplicative_operators = true -ij_java_spaces_around_relational_operators = true -ij_java_spaces_around_shift_operators = true -ij_java_spaces_around_type_bounds_in_type_parameters = true -ij_java_spaces_around_unary_operator = false -ij_java_spaces_within_angle_brackets = false -ij_java_spaces_within_annotation_parentheses = false -ij_java_spaces_within_array_initializer_braces = false -ij_java_spaces_within_braces = false -ij_java_spaces_within_brackets = false -ij_java_spaces_within_cast_parentheses = false -ij_java_spaces_within_catch_parentheses = false -ij_java_spaces_within_deconstruction_list = false -ij_java_spaces_within_for_parentheses = false -ij_java_spaces_within_if_parentheses = false -ij_java_spaces_within_method_call_parentheses = false -ij_java_spaces_within_method_parentheses = false -ij_java_spaces_within_parentheses = false -ij_java_spaces_within_record_header = false -ij_java_spaces_within_switch_parentheses = false -ij_java_spaces_within_synchronized_parentheses = false -ij_java_spaces_within_try_parentheses = false -ij_java_spaces_within_while_parentheses = false -ij_java_special_else_if_treatment = true -ij_java_static_field_name_prefix = -ij_java_static_field_name_suffix = -ij_java_subclass_name_prefix = -ij_java_subclass_name_suffix = Impl -ij_java_ternary_operation_signs_on_next_line = false -ij_java_ternary_operation_wrap = off -ij_java_test_name_prefix = -ij_java_test_name_suffix = Test -ij_java_throws_keyword_wrap = off -ij_java_throws_list_wrap = off -ij_java_use_external_annotations = false -ij_java_use_fq_class_names = false -ij_java_use_relative_indents = false -ij_java_use_single_class_imports = true -ij_java_variable_annotation_wrap = off -ij_java_visibility = public -ij_java_while_brace_force = never -ij_java_while_on_new_line = false -ij_java_wrap_comments = false -ij_java_wrap_first_method_in_call_chain = false -ij_java_wrap_long_lines = false - -[.editorconfig] -ij_editorconfig_align_group_field_declarations = false -ij_editorconfig_space_after_colon = false -ij_editorconfig_space_after_comma = true -ij_editorconfig_space_before_colon = false -ij_editorconfig_space_before_comma = false -ij_editorconfig_spaces_around_assignment_operators = true - -[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}] -ij_xml_align_attributes = true -ij_xml_align_text = false -ij_xml_attribute_wrap = normal -ij_xml_block_comment_add_space = false -ij_xml_block_comment_at_first_column = true -ij_xml_keep_blank_lines = 2 -ij_xml_keep_indents_on_empty_lines = false -ij_xml_keep_line_breaks = true -ij_xml_keep_line_breaks_in_text = true -ij_xml_keep_whitespaces = false -ij_xml_keep_whitespaces_around_cdata = preserve -ij_xml_keep_whitespaces_inside_cdata = false -ij_xml_line_comment_at_first_column = true -ij_xml_space_after_tag_name = false -ij_xml_space_around_equals_in_attribute = false -ij_xml_space_inside_empty_tag = false -ij_xml_text_wrap = normal -ij_xml_use_custom_settings = false - -[{*.bash,*.sh,*.zsh}] -indent_size = 2 -tab_width = 2 -ij_shell_binary_ops_start_line = false -ij_shell_keep_column_alignment_padding = false -ij_shell_minify_program = false -ij_shell_redirect_followed_by_space = false -ij_shell_switch_cases_indented = false -ij_shell_use_unix_line_separator = true - -[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,bowerrc,jest.config}] -indent_size = 2 -ij_json_array_wrapping = split_into_lines -ij_json_keep_blank_lines_in_code = 0 -ij_json_keep_indents_on_empty_lines = false -ij_json_keep_line_breaks = true -ij_json_keep_trailing_comma = false -ij_json_object_wrapping = split_into_lines -ij_json_property_alignment = do_not_align -ij_json_space_after_colon = true -ij_json_space_after_comma = true -ij_json_space_before_colon = false -ij_json_space_before_comma = false -ij_json_spaces_within_braces = false -ij_json_spaces_within_brackets = false -ij_json_wrap_long_lines = false - -[{*.http,*.rest}] -indent_size = 0 +max_line_length = 100 +indent_size = 4 ij_continuation_indent_size = 4 -ij_http-request_call_parameters_wrap = normal -ij_http-request_method_parameters_wrap = split_into_lines -ij_http-request_space_before_comma = true -ij_http-request_spaces_around_assignment_operators = true - -[{*.kt,*.kts}] -ktlint_no-wildcard-imports = disabled +ij_java_names_count_to_use_import_on_demand = 9999 ij_kotlin_align_in_columns_case_branch = false ij_kotlin_align_multiline_binary_operation = false ij_kotlin_align_multiline_extends_list = false ij_kotlin_align_multiline_method_parentheses = false ij_kotlin_align_multiline_parameters = true ij_kotlin_align_multiline_parameters_in_calls = false -ij_kotlin_allow_trailing_comma = false -ij_kotlin_allow_trailing_comma_on_call_site = false +ij_kotlin_allow_trailing_comma = true +ij_kotlin_allow_trailing_comma_on_call_site = true ij_kotlin_assignment_wrap = normal ij_kotlin_blank_lines_after_class_header = 0 ij_kotlin_blank_lines_around_block_when_branches = 0 ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1 -ij_kotlin_block_comment_add_space = false ij_kotlin_block_comment_at_first_column = true ij_kotlin_call_parameters_new_line_after_left_paren = true -ij_kotlin_call_parameters_right_paren_on_new_line = true +ij_kotlin_call_parameters_right_paren_on_new_line = false ij_kotlin_call_parameters_wrap = on_every_item ij_kotlin_catch_on_new_line = false ij_kotlin_class_annotation_wrap = split_into_lines ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL -ij_kotlin_continuation_indent_for_chained_calls = false -ij_kotlin_continuation_indent_for_expression_bodies = false -ij_kotlin_continuation_indent_in_argument_lists = false +ij_kotlin_continuation_indent_for_chained_calls = true +ij_kotlin_continuation_indent_for_expression_bodies = true +ij_kotlin_continuation_indent_in_argument_lists = true ij_kotlin_continuation_indent_in_elvis = false ij_kotlin_continuation_indent_in_if_conditions = false ij_kotlin_continuation_indent_in_parameter_lists = false @@ -412,11 +42,11 @@ ij_kotlin_continuation_indent_in_supertype_lists = false ij_kotlin_else_on_new_line = false ij_kotlin_enum_constants_wrap = off ij_kotlin_extends_list_wrap = normal -ij_kotlin_field_annotation_wrap = split_into_lines +ij_kotlin_field_annotation_wrap = off ij_kotlin_finally_on_new_line = false -ij_kotlin_if_rparen_on_new_line = true +ij_kotlin_if_rparen_on_new_line = false ij_kotlin_import_nested_classes = false -ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^ +ij_kotlin_imports_layout = * ij_kotlin_insert_whitespaces_in_simple_one_line_method = true ij_kotlin_keep_blank_lines_before_right_brace = 2 ij_kotlin_keep_blank_lines_in_code = 2 @@ -425,18 +55,15 @@ ij_kotlin_keep_first_column_comment = true ij_kotlin_keep_indents_on_empty_lines = false ij_kotlin_keep_line_breaks = true ij_kotlin_lbrace_on_next_line = false -ij_kotlin_line_break_after_multiline_when_entry = true ij_kotlin_line_comment_add_space = false -ij_kotlin_line_comment_add_space_on_reformat = false ij_kotlin_line_comment_at_first_column = true ij_kotlin_method_annotation_wrap = split_into_lines ij_kotlin_method_call_chain_wrap = normal ij_kotlin_method_parameters_new_line_after_left_paren = true ij_kotlin_method_parameters_right_paren_on_new_line = true ij_kotlin_method_parameters_wrap = on_every_item -ij_kotlin_name_count_to_use_star_import = 5 -ij_kotlin_name_count_to_use_star_import_for_members = 3 -ij_kotlin_packages_to_use_import_on_demand = java.util.*, kotlinx.android.synthetic.**, io.ktor.** +ij_kotlin_name_count_to_use_star_import = 9999 +ij_kotlin_name_count_to_use_star_import_for_members = 9999 ij_kotlin_parameter_annotation_wrap = off ij_kotlin_space_after_comma = true ij_kotlin_space_after_extend_colon = true @@ -465,27 +92,3 @@ ij_kotlin_while_on_new_line = false ij_kotlin_wrap_elvis_expressions = 1 ij_kotlin_wrap_expression_body_functions = 1 ij_kotlin_wrap_first_method_in_call_chain = false - -[{*.markdown,*.md}] -ij_markdown_force_one_space_after_blockquote_symbol = true -ij_markdown_force_one_space_after_header_symbol = true -ij_markdown_force_one_space_after_list_bullet = true -ij_markdown_force_one_space_between_words = true -ij_markdown_format_tables = true -ij_markdown_insert_quote_arrows_on_wrap = true -ij_markdown_keep_indents_on_empty_lines = false -ij_markdown_keep_line_breaks_inside_text_blocks = true -ij_markdown_max_lines_around_block_elements = 1 -ij_markdown_max_lines_around_header = 1 -ij_markdown_max_lines_between_paragraphs = 1 -ij_markdown_min_lines_around_block_elements = 1 -ij_markdown_min_lines_around_header = 1 -ij_markdown_min_lines_between_paragraphs = 1 -ij_markdown_wrap_text_if_long = true -ij_markdown_wrap_text_inside_blockquotes = true - -[{*.properties,spring.handlers,spring.schemas}] -ij_properties_align_group_field_declarations = false -ij_properties_keep_blank_lines = false -ij_properties_key_value_delimiter = equals -ij_properties_spaces_around_key_value_delimiter = false diff --git a/build.gradle.kts b/build.gradle.kts index dc53954..a8d3620 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,21 +1,17 @@ -plugins { - kotlin("jvm") version "2.0.10" -} +plugins { kotlin("jvm") version "2.0.10" } group = "de.fraunhofer.iem" + version = "1.0-SNAPSHOT" -repositories { - mavenCentral() -} +repositories { mavenCentral() } + +dependencies { testImplementation(kotlin("test")) } + +tasks.register("ktfmtCheck") { dependsOn(gradle.includedBuild("spha-cli").task(":ktfmtCheck")) } + +tasks.register("ktfmtFormat") { dependsOn(gradle.includedBuild("spha-cli").task(":ktfmtFormat")) } -dependencies { - testImplementation(kotlin("test")) -} +tasks.test { useJUnitPlatform() } -tasks.test { - useJUnitPlatform() -} -kotlin { - jvmToolchain(21) -} \ No newline at end of file +kotlin { jvmToolchain(21) } diff --git a/spha-cli/build.gradle.kts b/spha-cli/build.gradle.kts index 597a518..735f39a 100644 --- a/spha-cli/build.gradle.kts +++ b/spha-cli/build.gradle.kts @@ -1,17 +1,17 @@ plugins { alias(libs.plugins.kotlin) alias(libs.plugins.serialization) + alias(libs.plugins.ktfmt) application } group = "de.fraunhofer.iem.spha" + version = "0.0.2-SNAPSHOT" -repositories { - mavenCentral() -} +repositories { mavenCentral() } -dependencies{ +dependencies { implementation(libs.bundles.kpiCalculator) implementation(libs.kotlin.cli) @@ -30,13 +30,14 @@ dependencies{ testImplementation(libs.test.junit5.params) } -tasks.test { - useJUnitPlatform() +ktfmt { + // KotlinLang style - 4 space indentation - From kotlinlang.org/docs/coding-conventions.html + kotlinLangStyle() } -application{ - mainClass = "de.fraunhofer.iem.spha.cli.MainKt" -} +tasks.test { useJUnitPlatform() } + +application { mainClass = "de.fraunhofer.iem.spha.cli.MainKt" } kotlin { compilerOptions { @@ -44,4 +45,3 @@ kotlin { apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0) } } - diff --git a/spha-cli/gradle/libs.versions.toml b/spha-cli/gradle/libs.versions.toml index a676afa..85766f7 100644 --- a/spha-cli/gradle/libs.versions.toml +++ b/spha-cli/gradle/libs.versions.toml @@ -9,6 +9,7 @@ mockk = "1.13.12" jimfs = "1.3.0" apache-commoms = "3.16.0" junit = "5.11.0" +ktfmt = "0.20.1" [libraries] # KPI calculator @@ -36,6 +37,7 @@ test-junit5-params = { module = "org.junit.jupiter:junit-jupiter-params", versio [plugins] kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +ktfmt = { id = "com.ncorti.ktfmt.gradle", version.ref = "ktfmt" } [bundles] kpiCalculator = ["kpi-calculator-adapter", "kpi-calculator-core", "kpi-calculator-model"] diff --git a/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/Main.kt b/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/Main.kt index c81c352..5ac2d68 100644 --- a/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/Main.kt +++ b/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/Main.kt @@ -9,31 +9,27 @@ import de.fraunhofer.iem.spha.cli.commands.TransformToolResultCommand import de.fraunhofer.iem.spha.cli.transformer.RawKpiTransformer import de.fraunhofer.iem.spha.cli.transformer.Tool2RawKpiTransformer import io.github.oshai.kotlinlogging.KotlinLogging +import java.nio.file.FileSystem +import java.nio.file.FileSystems +import kotlin.system.exitProcess import org.koin.core.component.KoinComponent import org.koin.core.context.GlobalContext.startKoin import org.koin.dsl.module import org.slf4j.simple.SimpleLogger -import java.nio.file.FileSystem -import java.nio.file.FileSystems -import kotlin.system.exitProcess internal val appModules = module { - single{ Tool2RawKpiTransformer() } - single{ FileSystems.getDefault() } + single { Tool2RawKpiTransformer() } + single { FileSystems.getDefault() } } fun main(args: Array) { - startKoin{ - modules(appModules) - } + startKoin { modules(appModules) } try { - MainSphaToolCommand() - .subcommands(TransformToolResultCommand()) - .main(args) - } catch (e : Exception){ - val logger = KotlinLogging.logger{} - logger.error(e, {e.message}) + MainSphaToolCommand().subcommands(TransformToolResultCommand()).main(args) + } catch (e: Exception) { + val logger = KotlinLogging.logger {} + logger.error(e, { e.message }) exitProcess(1) } } @@ -41,41 +37,53 @@ fun main(args: Array) { /** * The Main command of this application. Supports a global switch to enable verbose logging mode. */ -private class MainSphaToolCommand : NoOpCliktCommand(){ +private class MainSphaToolCommand : NoOpCliktCommand() { - val verbose by option("--verbose", "-v", - help="When set, the application provides detailed logging. Default is unset.") - .flag() + val verbose by + option( + "--verbose", + "-v", + help = "When set, the application provides detailed logging. Default is unset.", + ) + .flag() override fun run() { configureLogging() } - private fun configureLogging(){ - if (verbose) - System.setProperty(SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE") + private fun configureLogging() { + if (verbose) System.setProperty(SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE") } } /** - * Base class for all commands of this application, except for the main command. - * @implNote Due to the design of clikt, the main command should be separate - * and this base class should not introduce the --verbose switch. Otherwise, the following cli input would be legal: - * './spha -v transform -t abc -v'. The first -v switch actually triggers the logging configuration, - * where the second -v switch is independent to the first switch. This will cause confusion for users, which switch to use. + * Base class for all commands of this application, except for the main command. @implNote Due to + * the design of clikt, the main command should be separate and this base class should not introduce + * the --verbose switch. Otherwise, the following cli input would be legal: './spha -v transform -t + * abc -v'. The first -v switch actually triggers the logging configuration, where the second -v + * switch is independent to the first switch. This will cause confusion for users, which switch to + * use. */ -internal abstract class SphaToolCommandBase(name: String? = null, help: String = "") - : CliktCommand(name = name, help = help), KoinComponent { - // NB: Needs to be lazy, as otherwise we initialize this variable before setting the logger configuration. - private val _lazyLogger = lazy { KotlinLogging.logger{} } - protected val Logger get() = _lazyLogger.value +internal abstract class SphaToolCommandBase(name: String? = null, help: String = "") : + CliktCommand(name = name, help = help), KoinComponent { + // NB: Needs to be lazy, as otherwise we initialize this variable before setting the logger + // configuration. + private val _lazyLogger = lazy { KotlinLogging.logger {} } + protected val Logger + get() = _lazyLogger.value - val strict by option("--strict", - help="When set, the application is less tolerant to unknown input formats. Default is unset.") - .flag() + val strict by + option( + "--strict", + help = + "When set, the application is less tolerant to unknown input formats. Default is unset.", + ) + .flag() override fun run() { - Logger.trace { "Original command arguments: '${currentContext.originalArgv.joinToString()}}'" } + Logger.trace { + "Original command arguments: '${currentContext.originalArgv.joinToString()}}'" + } Logger.debug { "Running command: $commandName" } } } diff --git a/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommand.kt b/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommand.kt index 79f4c4e..2f95a5e 100644 --- a/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommand.kt +++ b/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommand.kt @@ -6,43 +6,54 @@ import com.github.ajalt.clikt.parameters.options.required import de.fraunhofer.iem.spha.cli.SphaToolCommandBase import de.fraunhofer.iem.spha.cli.transformer.RawKpiTransformer import de.fraunhofer.iem.spha.cli.transformer.TransformerOptions +import java.nio.file.FileSystem +import java.nio.file.Path +import kotlin.io.path.createDirectories import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.json.encodeToStream import org.koin.core.component.KoinComponent import org.koin.core.component.inject -import java.nio.file.FileSystem -import java.nio.file.Path -import kotlin.io.path.createDirectories -internal class TransformToolResultCommand : SphaToolCommandBase( - name = "transform", - help = "transforms a specified KPI-provider (such as a SAST tool) result into a uniform data format, " + - "so that it can be used for the 'calculate' command." -), KoinComponent { +internal class TransformToolResultCommand : + SphaToolCommandBase( + name = "transform", + help = + "transforms a specified KPI-provider (such as a SAST tool) result into a uniform data format, " + + "so that it can be used for the 'calculate' command.", + ), + KoinComponent { private val transformer by inject() private val fileSystem by inject() - private val toolName by option( - "-t", "--tool", - help = "The identifier of the KPI-provider tool that produced the input. " + - "Use the command --list-tools to get a list of available identifiers." - ) - .required() - - private val inputFiles by option( - "-i", "--inputFile", - help = "List of input files. Usually these are result files produced by the tool as specified by --tool." + - "To specify multiple input files (if supported by --tool), the option can be used multiple times." - ) - .multiple() + private val toolName by + option( + "-t", + "--tool", + help = + "The identifier of the KPI-provider tool that produced the input. " + + "Use the command --list-tools to get a list of available identifiers.", + ) + .required() - private val output by option( - "-o", "--output", - help = "The output directory where the result of the operation is stored. Default is the current working directory." - ) + private val inputFiles by + option( + "-i", + "--inputFile", + help = + "List of input files. Usually these are result files produced by the tool as specified by --tool." + + "To specify multiple input files (if supported by --tool), the option can be used multiple times.", + ) + .multiple() + private val output by + option( + "-o", + "--output", + help = + "The output directory where the result of the operation is stored. Default is the current working directory.", + ) @OptIn(ExperimentalSerializationApi::class) override fun run() { diff --git a/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformer.kt b/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformer.kt index 01182fd..76df6ec 100644 --- a/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformer.kt +++ b/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformer.kt @@ -6,10 +6,10 @@ import de.fraunhofer.iem.kpiCalculator.model.adapter.trivy.TrivyDto import de.fraunhofer.iem.kpiCalculator.model.kpi.RawValueKpi import de.fraunhofer.iem.spha.cli.StrictModeConstraintFailed import io.github.oshai.kotlinlogging.KotlinLogging -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject import java.io.InputStream import java.nio.file.FileSystem +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject internal fun interface RawKpiTransformer { fun getRawKpis(options: TransformerOptions, strictMode: Boolean): Collection @@ -22,31 +22,36 @@ internal class Tool2RawKpiTransformer : RawKpiTransformer, KoinComponent { private val _fileSystem by inject() private val _logger = KotlinLogging.logger {} - override fun getRawKpis(options: TransformerOptions, strictMode: Boolean): Collection { + override fun getRawKpis( + options: TransformerOptions, + strictMode: Boolean, + ): Collection { - val result: Collection = when (options.tool) { -// "occmd" -> { -// val adapterInput: OccmdDto = OccmdAdapter.createInputFrom(input) -// OccmdAdapter.transformDataToKpi(adapterInput) -// } - "trivy" -> { - getSingleInputStreamFromInputFile(options.inputFiles, strictMode).use { - _logger.info { "Selected supported tool: Trivy" } - val adapterInput: TrivyDto = TrivyAdapter.dtoFromJson(it) - return@use TrivyAdapter.transformDataToKpi(listOf(adapterInput)) + val result: Collection = + when (options.tool) { + // "occmd" -> { + // val adapterInput: OccmdDto = OccmdAdapter.createInputFrom(input) + // OccmdAdapter.transformDataToKpi(adapterInput) + // } + "trivy" -> { + getSingleInputStreamFromInputFile(options.inputFiles, strictMode).use { + _logger.info { "Selected supported tool: Trivy" } + val adapterInput: TrivyDto = TrivyAdapter.dtoFromJson(it) + return@use TrivyAdapter.transformDataToKpi(listOf(adapterInput)) + } } - } - else -> throw ToolNotFoundException("Tool ${options.tool} is not yet supported.") - } + else -> throw ToolNotFoundException("Tool ${options.tool} is not yet supported.") + } - val rawKpis = result.mapNotNull { - if (it !is AdapterResult.Success) - return@mapNotNull null - return@mapNotNull it.rawValueKpi - } + val rawKpis = + result.mapNotNull { + if (it !is AdapterResult.Success) return@mapNotNull null + return@mapNotNull it.rawValueKpi + } - // If we have unequal counts, we know that adapter returned faulted elements. Thus, we throw in strict mode. + // If we have unequal counts, we know that adapter returned faulted elements. Thus, we throw + // in strict mode. if (strictMode && rawKpis.count() != result.count()) { throw StrictModeConstraintFailed("The adapter produced faulted results.") } @@ -54,16 +59,19 @@ internal class Tool2RawKpiTransformer : RawKpiTransformer, KoinComponent { return rawKpis } - internal fun getSingleInputStreamFromInputFile(inputFiles: List?, strictMode: Boolean): InputStream { - check(!inputFiles.isNullOrEmpty()) { - "No input files specified." - } + internal fun getSingleInputStreamFromInputFile( + inputFiles: List?, + strictMode: Boolean, + ): InputStream { + check(!inputFiles.isNullOrEmpty()) { "No input files specified." } if (inputFiles.count() > 1) { if (strictMode) { throw StrictModeConstraintFailed("Expected only one input file.") } - _logger.warn { "Expected only one input file. But go #${inputFiles.count()}. Will use first entry." } + _logger.warn { + "Expected only one input file. But go #${inputFiles.count()}. Will use first entry." + } } return _fileSystem.provider().newInputStream(_fileSystem.getPath(inputFiles.first())) diff --git a/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/transformer/TransformerOptions.kt b/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/transformer/TransformerOptions.kt index 12d85ef..726b212 100644 --- a/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/transformer/TransformerOptions.kt +++ b/spha-cli/src/main/kotlin/de/fraunhofer/iem/spha/cli/transformer/TransformerOptions.kt @@ -1,6 +1,3 @@ package de.fraunhofer.iem.spha.cli.transformer -data class TransformerOptions( - val tool : String, - val inputFiles : List? = null -) +data class TransformerOptions(val tool: String, val inputFiles: List? = null) diff --git a/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommandTest.kt b/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommandTest.kt index 28b559a..d57a9e0 100644 --- a/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommandTest.kt +++ b/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/commands/TransformToolResultCommandTest.kt @@ -12,6 +12,12 @@ import de.fraunhofer.iem.spha.cli.transformer.TransformerOptions import io.mockk.every import io.mockk.mockkClass import io.mockk.verify +import java.nio.file.FileSystem +import kotlin.io.path.readText +import kotlin.io.path.writeText +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue import kotlinx.serialization.json.Json import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.RegisterExtension @@ -26,28 +32,20 @@ import org.koin.test.junit5.KoinTestExtension import org.koin.test.junit5.mock.MockProviderExtension import org.koin.test.mock.declare import org.koin.test.mock.declareMock -import java.nio.file.FileSystem -import kotlin.io.path.readText -import kotlin.io.path.writeText -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue - class TransformToolResultCommandTest : KoinTest { @JvmField @RegisterExtension - val koinTestRule = KoinTestExtension.create { - printLogger(Level.DEBUG) - modules(appModules) - } + val koinTestRule = + KoinTestExtension.create { + printLogger(Level.DEBUG) + modules(appModules) + } @JvmField @RegisterExtension - val mockProvider = MockProviderExtension.create { clazz -> - mockkClass(clazz) - } + val mockProvider = MockProviderExtension.create { clazz -> mockkClass(clazz) } @Test fun testTransform_ToolDoesNotExists_Throws() { @@ -60,7 +58,8 @@ class TransformToolResultCommandTest : KoinTest { val toolName = "occmd" - val fileSystem = declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } + val fileSystem = + declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } declareMock { every { getRawKpis(any(), any()) } throws IllegalStateException() @@ -80,9 +79,8 @@ class TransformToolResultCommandTest : KoinTest { val toolName = "occmd" declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } - val transformer = declareMock { - every { getRawKpis(any(), any()) } returns listOf() - } + val transformer = + declareMock { every { getRawKpis(any(), any()) } returns listOf() } val strictCommandInput = if (expectedStrict) "--strict" else "" TransformToolResultCommand().test("$strictCommandInput -t $toolName") @@ -94,11 +92,11 @@ class TransformToolResultCommandTest : KoinTest { val toolName = "occmd" - val fileSystem = declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } + val fileSystem = + declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } - val transformer = declareMock { - every { getRawKpis(any(), any()) } returns listOf() - } + val transformer = + declareMock { every { getRawKpis(any(), any()) } returns listOf() } val expectedResultPath = fileSystem.getPath("$toolName-result.json").toAbsolutePath() expectedResultPath.writeText("someJunk...") @@ -116,23 +114,20 @@ class TransformToolResultCommandTest : KoinTest { val toolName = "occmd" - val resultList = listOf( - RawValueKpi(KpiId.SECRETS, 100), - RawValueKpi(KpiId.SECURITY, 1) - ) + val resultList = listOf(RawValueKpi(KpiId.SECRETS, 100), RawValueKpi(KpiId.SECURITY, 1)) - val fileSystem = declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } + val fileSystem = + declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } - declareMock { - every { getRawKpis(any(), any()) } returns resultList - } + declareMock { every { getRawKpis(any(), any()) } returns resultList } TransformToolResultCommand().test("-t $toolName") val expectedResultPath = fileSystem.getPath("$toolName-result.json").toAbsolutePath() // Read in the written file and check if it matches the result - val actualRawKpis = Json.decodeFromString>(expectedResultPath.readText()) + val actualRawKpis = + Json.decodeFromString>(expectedResultPath.readText()) assertEquals(actualRawKpis, resultList) } @@ -148,16 +143,14 @@ class TransformToolResultCommandTest : KoinTest { assertTrue { fileSystem.provider().exists(fileSystem.getPath(expectedFilePath)) } } - @ParameterizedTest @MethodSource("inputTestSource") fun testTransform_MultipleInputsSplit(inputArg: String, expectedInputs: List) { val toolName = "toolA" declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } - val transformer = declareMock { - every { getRawKpis(any(), any()) } returns listOf() - } + val transformer = + declareMock { every { getRawKpis(any(), any()) } returns listOf() } TransformToolResultCommand().test("-t $toolName $inputArg") val options = TransformerOptions(toolName, inputFiles = expectedInputs) @@ -169,15 +162,27 @@ class TransformToolResultCommandTest : KoinTest { fun outputTestSource(): List { val toolName = "toolA" return listOf( - arguments("toolA", ".", "/work/$toolName${TransformToolResultCommand.RESULT_FILE_SUFFIX}"), - arguments("toolA", "dir", "/work/dir/$toolName${TransformToolResultCommand.RESULT_FILE_SUFFIX}"), + arguments( + "toolA", + ".", + "/work/$toolName${TransformToolResultCommand.RESULT_FILE_SUFFIX}", + ), + arguments( + "toolA", + "dir", + "/work/dir/$toolName${TransformToolResultCommand.RESULT_FILE_SUFFIX}", + ), arguments( "toolA", "/other/dir", - "/other/dir/$toolName${TransformToolResultCommand.RESULT_FILE_SUFFIX}" + "/other/dir/$toolName${TransformToolResultCommand.RESULT_FILE_SUFFIX}", ), // This is a misuse, but it should work nether the less. - arguments("toolA", "/file.txt", "/file.txt/$toolName${TransformToolResultCommand.RESULT_FILE_SUFFIX}") + arguments( + "toolA", + "/file.txt", + "/file.txt/$toolName${TransformToolResultCommand.RESULT_FILE_SUFFIX}", + ), ) } @@ -186,7 +191,10 @@ class TransformToolResultCommandTest : KoinTest { return listOf( arguments("-i input.json", listOf("input.json")), arguments("--inputFile \"a b.json\"", listOf("a b.json")), - arguments("-i a.json --inputFile b.json -i \"d e.json\"", listOf("a.json", "b.json", "d e.json")), + arguments( + "-i a.json --inputFile b.json -i \"d e.json\"", + listOf("a.json", "b.json", "d e.json"), + ), ) } } diff --git a/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformerTest.kt b/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformerTest.kt index 9d734b4..59365e1 100644 --- a/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformerTest.kt +++ b/spha-cli/src/test/kotlin/de/fraunhofer/iem/spha/cli/transformer/Tool2RawKpiTransformerTest.kt @@ -11,6 +11,9 @@ import io.mockk.every import io.mockk.mockkClass import io.mockk.mockkObject import io.mockk.verify +import java.nio.file.FileSystem +import kotlin.test.Test +import kotlin.test.assertEquals import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.RegisterExtension import org.junit.jupiter.params.ParameterizedTest @@ -20,55 +23,55 @@ import org.koin.test.KoinTest import org.koin.test.junit5.KoinTestExtension import org.koin.test.junit5.mock.MockProviderExtension import org.koin.test.mock.declare -import java.nio.file.FileSystem -import kotlin.test.Test -import kotlin.test.assertEquals class Tool2RawKpiTransformerTest : KoinTest { @JvmField @RegisterExtension - val koinTestRule = KoinTestExtension.create { - printLogger(Level.DEBUG) - modules(appModules) - } + val koinTestRule = + KoinTestExtension.create { + printLogger(Level.DEBUG) + modules(appModules) + } @JvmField @RegisterExtension - val mockProvider = MockProviderExtension.create { clazz -> - mockkClass(clazz) - } + val mockProvider = MockProviderExtension.create { clazz -> mockkClass(clazz) } @ParameterizedTest @ValueSource(booleans = [false, true]) fun getSingleInputStreamFromInputFile_NullInput_Throws(strict: Boolean) { val command = Tool2RawKpiTransformer() - assertThrows { command.getSingleInputStreamFromInputFile(null, strict) } + assertThrows { + command.getSingleInputStreamFromInputFile(null, strict) + } } @ParameterizedTest @ValueSource(booleans = [false, true]) fun getSingleInputStreamFromInputFile_Empty_Throws(strict: Boolean) { val command = Tool2RawKpiTransformer() - assertThrows { command.getSingleInputStreamFromInputFile(listOf(), strict) } + assertThrows { + command.getSingleInputStreamFromInputFile(listOf(), strict) + } } @Test fun getSingleInputStreamFromInputFile_MultipleInputs_Strict_Throws() { - val fileSystem = declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } - fileSystem.provider().newOutputStream(fileSystem.getPath("a")).use { - it.write(123) - } - fileSystem.provider().newOutputStream(fileSystem.getPath("b")).use { - it.write(789) - } + val fileSystem = + declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } + fileSystem.provider().newOutputStream(fileSystem.getPath("a")).use { it.write(123) } + fileSystem.provider().newOutputStream(fileSystem.getPath("b")).use { it.write(789) } val command = Tool2RawKpiTransformer() - assertThrows { command.getSingleInputStreamFromInputFile(listOf("a", "b"), true) } + assertThrows { + command.getSingleInputStreamFromInputFile(listOf("a", "b"), true) + } } @Test fun getSingleInputStreamFromInputFile_MultipleInputs_NonStrict_TakeFirst() { - val fileSystem = declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } + val fileSystem = + declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } fileSystem.provider().newOutputStream(fileSystem.getPath("a")).use { it.write(123) } fileSystem.provider().newOutputStream(fileSystem.getPath("b")).use { it.write(789) } @@ -80,13 +83,11 @@ class Tool2RawKpiTransformerTest : KoinTest { @Test fun getRawKpis_Trivy() { - val fileSystem = declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } - fileSystem.provider().newOutputStream(fileSystem.getPath("a")).use { } + val fileSystem = + declare { Jimfs.newFileSystem(Configuration.forCurrentPlatform()) } + fileSystem.provider().newOutputStream(fileSystem.getPath("a")).use {} - val trivyVulns = listOf( - VulnerabilityDto("A", "1", 1.0), - VulnerabilityDto("B", "2", 2.3), - ) + val trivyVulns = listOf(VulnerabilityDto("A", "1", 1.0), VulnerabilityDto("B", "2", 2.3)) mockkObject(TrivyAdapter) every { TrivyAdapter.dtoFromJson(any()) } returns TrivyDto(trivyVulns)