diff --git a/.github/workflows/github-action-scan.yml b/.github/workflows/github-action-scan.yml index 5824a3323b..b9e8f62b1f 100644 --- a/.github/workflows/github-action-scan.yml +++ b/.github/workflows/github-action-scan.yml @@ -29,14 +29,22 @@ jobs: steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - + + - name: Cache Node.js modules + uses: actions/cache@v2 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + - name: Use Node.js uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af with: node-version: 22 - - name: Clean install - run: npm ci + - name: Install + run: npm install - name: Build run: npm run build diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 3a2a60682c..74ffae8952 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -69,7 +69,7 @@ jobs: run: ./gradlew :sechub-cli:buildGo :sechub-cli:testGo - name: Build Server, DAUI and generate OpenAPI file - run: ./gradlew ensureLocalhostCertificate build generateOpenapi buildDeveloperAdminUI -x :sechub-cli:build + run: ./gradlew ensureLocalhostCertificate build generateOpenapi -x :sechub-cli:build - name: Generate and build Java projects related to SecHub Java API run: ./gradlew :sechub-api-java:build :sechub-systemtest:build :sechub-pds-tools:buildPDSToolsCLI -Dsechub.build.stage=api-necessary diff --git a/.github/workflows/release-client-server-pds.yml b/.github/workflows/release-client-server-pds.yml index d509fe4877..067f576494 100644 --- a/.github/workflows/release-client-server-pds.yml +++ b/.github/workflows/release-client-server-pds.yml @@ -173,7 +173,7 @@ jobs: # Build SecHub Server + PDS # ---------------------- - name: Build Server and PDS artifacts - run: ./gradlew ensureLocalhostCertificate build generateOpenapi buildDeveloperAdminUI -x :sechub-cli:build + run: ./gradlew ensureLocalhostCertificate build generateOpenapi -x :sechub-cli:build # ---------------------- # Build API Java publish @@ -330,7 +330,7 @@ jobs: mkdir server-release-artifacts # Collect release artifacts cp sechub-server/build/libs/sechub-server-${{ inputs.server-version }}.jar \ - sechub-developertools/build/libs/sechub-developer-admin-ui-${{ inputs.server-version }}.jar \ + sechub-developertools/build/libs/sechub-developertools-${{ inputs.server-version }}.jar \ server-release-artifacts/ cp sechub-doc/build/docs/asciidoc/sechub-architecture.pdf \ server-release-artifacts/sechub-architecture-${{ inputs.server-version }}.pdf diff --git a/.github/workflows/release-pds-tools.yml b/.github/workflows/release-pds-tools.yml index f453c5c8fb..22e9ee0b9e 100644 --- a/.github/workflows/release-pds-tools.yml +++ b/.github/workflows/release-pds-tools.yml @@ -96,7 +96,7 @@ jobs: # Build SecHub PDS-Tools # ---------------------- - name: Build Server, DAUI and generate OpenAPI file - run: ./gradlew ensureLocalhostCertificate build generateOpenapi buildDeveloperAdminUI -x :sechub-integrationtest:test -x :sechub-cli:build + run: ./gradlew ensureLocalhostCertificate build generateOpenapi -x :sechub-integrationtest:test -x :sechub-cli:build - name: Generate and build Java projects related to SecHub Java API run: ./gradlew :sechub-api-java:build :sechub-systemtest:build :sechub-pds-tools:buildPDSToolsCLI -Dsechub.build.stage=api-necessary diff --git a/continous-integration-multibranch-pipeline.jenkins b/continous-integration-multibranch-pipeline.jenkins index 907c86ff0d..bfda3d70ec 100644 --- a/continous-integration-multibranch-pipeline.jenkins +++ b/continous-integration-multibranch-pipeline.jenkins @@ -57,7 +57,7 @@ pipeline { // We do NOT build sechub-integrationtest // Reason: because we do NOT want to have the integration tests executed, otherwise gradle will not execute them // on integration phase again (because nothing has changed, so gradle will cache the results which are ignored ... - callGradleWrapper("ensureLocalhostCertificate build generateOpenapi buildDeveloperAdminUI -x :sechub-cli:build -Psechub.test.wiremock.https_port=${env.SECHUB_TEST_WIREMOCK_HTTPS_PORT} -Psechub.test.wiremock.http_port=${env.SECHUB_TEST_WIREMOCK_HTTP_PORT} --console=plain") + callGradleWrapper("ensureLocalhostCertificate build generateOpenapi -x :sechub-cli:build -Psechub.test.wiremock.https_port=${env.SECHUB_TEST_WIREMOCK_HTTPS_PORT} -Psechub.test.wiremock.http_port=${env.SECHUB_TEST_WIREMOCK_HTTP_PORT} --console=plain") callGradleWrapper(":sechub-api-java:build :sechub-systemtest:build :sechub-pds-tools:buildPDSToolsCLI -Dsechub.build.stage=api-necessary --console=plain") } } diff --git a/docs/latest/sechub-developer-quickstart-guide.html b/docs/latest/sechub-developer-quickstart-guide.html index 151a4ccca0..874da0b4be 100644 --- a/docs/latest/sechub-developer-quickstart-guide.html +++ b/docs/latest/sechub-developer-quickstart-guide.html @@ -1939,7 +1939,7 @@

5.2. Run Integration Tests From
-
./gradlew ensureLocalhostCertificate build generateOpenapi buildDeveloperAdminUI -x :sechub-cli:build
+
./gradlew ensureLocalhostCertificate build generateOpenapi -x :sechub-cli:build
diff --git a/github-actions/scan/README.adoc b/github-actions/scan/README.adoc index 28f945113b..8d6c3e5de3 100644 --- a/github-actions/scan/README.adoc +++ b/github-actions/scan/README.adoc @@ -94,25 +94,29 @@ The following variables take priority over the configuration file: If no custom `sechub.json` is provided, it will be generated from the remaining specified variables and used. However, if a custom `sechub.json` is provided, no separate configuration will be created, meaning the remaining set variables will essentially be ignored. ==== -=== Outputs +=== Use SecHub results in GitHub workflows -The following table lists the output variables available after this SecHub GitHub Action has completed: +==== GitHub Output +Because of problems with GitHub outputs (see https://github.com/mercedes-benz/sechub/issues/3481 ) SecHub no longer supports outputs but provides environment variables instead. + +==== Environment variables +The following table lists the environment variables containing result data after this SecHub GitHub Action has completed: [cols="20%,40%,40%"] |=== -| Output Name | Description | Expected Values +| Environment variable | Description | Expected Values -| scan-trafficlight | The color of the traffic light reported by SecHub if the scan ran successfully, otherwise `FAILURE`. | One of `GREEN`, `YELLOW`, `RED`, or `FAILURE`. -| scan-findings-count | The total number of findings reported by SecHub. Returns 0 if the scan didn't complete. | 0 -| scan-findings-high | The number of high-level findings reported by SecHub. | 0 -| scan-findings-medium | The number of medium-level findings reported by SecHub. | 0 -| scan-findings-low | The number of low-level findings reported by SecHub. | 0 -| scan-readable-summary| A human-readable summary of the scan outcome, including the traffic light color, findings count, and their distribution. | For example, `SecHub scan could not be executed` if an error occurred. Otherwise, i.e. `SecHub reported traffic light color YELLOW with 15 findings, categorized as follows: MEDIUM (8), LOW (7)` +| SECHUB_OUTPUT_SCAN_TRAFFICLIGHT | The color of the traffic light reported by SecHub if the scan ran successfully, otherwise `FAILURE`. | One of `GREEN`, `YELLOW`, `RED`, or `FAILURE`. +| SECHUB_OUTPUT_SCAN_FINDINGS_COUNT | The total number of findings reported by SecHub. Returns 0 if the scan didn't complete. | 0 +| SECHUB_OUTPUT_SCAN_FINDINGS_HIGH | The number of high-level findings reported by SecHub. | 0 +| SECHUB_OUTPUT_SCAN_FINDINGS_MEDIUM | The number of medium-level findings reported by SecHub. | 0 +| SECHUB_OUTPUT_SCAN_FINDINGS_LOW | The number of low-level findings reported by SecHub. | 0 +| SECHUB_OUTPUT_SCAN_READABLE_SUMMARY| A human-readable summary of the scan outcome, including the traffic light color, findings count, and their distribution. | For example, `SecHub scan could not be executed` if an error occurred. Otherwise, i.e. `SecHub reported traffic light color YELLOW with 15 findings, categorized as follows: MEDIUM (8), LOW (7)` |=== -You can access them after the action has run with `${{ steps..outputs. }}` +You can access them after the action has run with `${{ env. }}` === Build @@ -129,6 +133,16 @@ npm run build This runs the ncc compiler and transpiles the files from the src folder into the `dist/` folder. +=== Deployment +A GitHub action needs a transpiled `index.js` to be used as an action from workflows. + +As long as we do not provide a new index.js the old action is still in usage, even when the source code has +changed. If we do not build the file and commit and push it to git repository, the action will not +be available! + +The complete deployment process is automated by `.github/workflows/release-github-action.yml` which will create a +PR which will do all necessary steps. + === Test ==== Unit tests @@ -142,10 +156,19 @@ npm run test ==== Integration-Test As a precondition to run the integration tests locally you have to +execute `01-start.sh $secHubServerVersion $sechubServerPortNr $pdsVersion $pdsPortN` +inside the integration test folder. -- execute `__test__/01-start.sh $secHubServerVersion $sechubServerPortNr $pdsVersion $pdsPortNr` +An example: + +[source,bash] +---- +# Next lines will start a SecHub server of version 2.4.0 and a PDS with version 2.1.0 +cd ./github-actions/scan/__test__/integrationtest +./01-start.sh 2.4.0 8443 2.1.0 8444 +---- -TIP: You can also start a SecHub server and a PDS (both in integration test mode) instead of using the `01-start` script. +TIP: You can also start a SecHub server and a PDS from IDE (both in integration test mode) instead of using the `01-start` script. After the script has been executed, you can execute integration tests multiple times via following command: @@ -176,7 +199,9 @@ In this setup the tests can be executed from sidebar and from links created insi [TIP] ==== -Unfortunately, the Jest UI integration works only for npm script "test". But to handle integration tests different (the tests shall only be executed when all is build and servers are started) they are not executed by "test" script. +Unfortunately, the Jest UI integration works only for npm script "test". +But to handle integration tests different (the tests shall only be executed +when all is built and servers are started) they are not executed by "test" script. If you want to **debug an integration test**, there is a temporary workaround necessary while you debug the test: diff --git a/github-actions/scan/__test__/client-version-helper.test.ts b/github-actions/scan/__test__/client-version-helper.test.ts index d8227497b2..72f9052e4f 100644 --- a/github-actions/scan/__test__/client-version-helper.test.ts +++ b/github-actions/scan/__test__/client-version-helper.test.ts @@ -3,6 +3,22 @@ import { getClientVersion } from '../src/client-version-helper'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; +import * as core from '@actions/core'; + +jest.mock('@actions/core'); + +const mockDebug = core.debug as jest.MockedFunction; + +const debugEnabled = false; + +beforeEach(() => { + mockDebug.mockImplementation((message: string | Error) => { + if (debugEnabled) { + console.log(`Debug: ${message}`); + } + }); + mockDebug.mockClear(); +}); describe('getClientVersion', function () { diff --git a/github-actions/scan/__test__/configuration-builder.test.ts b/github-actions/scan/__test__/configuration-builder.test.ts index 2787db9307..1b8e238e5d 100644 --- a/github-actions/scan/__test__/configuration-builder.test.ts +++ b/github-actions/scan/__test__/configuration-builder.test.ts @@ -6,7 +6,12 @@ import { SecHubConfigurationModelBuilderData } from '../src/configuration-builde jest.mock('@actions/core'); -function dumpModel(model: SecHubConfigurationModel){ +const debugEnabled = false; + +function logDebug(model: SecHubConfigurationModel){ + if (! debugEnabled){ + return; + } const json = JSON.stringify(model, null, 2); // pretty printed output console.log('json='+json); @@ -34,7 +39,7 @@ describe('configuration-builder', function() { const model= configBuilder.createSecHubConfigurationModel(builderData); /* test */ - dumpModel(model); + logDebug(model); expect(model.apiVersion).toEqual('1.0'); @@ -66,7 +71,7 @@ describe('configuration-builder', function() { const model= configBuilder.createSecHubConfigurationModel(builderData); /* test */ - dumpModel(model); + logDebug(model); expect(model.apiVersion).toEqual('1.0'); @@ -101,7 +106,7 @@ describe('configuration-builder', function() { const model= configBuilder.createSecHubConfigurationModel(builderData); /* test */ - dumpModel(model); + logDebug(model); expect(model.apiVersion).toEqual('1.0'); @@ -138,7 +143,7 @@ describe('configuration-builder', function() { const model= configBuilder.createSecHubConfigurationModel(builderData); /* test */ - dumpModel(model); + logDebug(model); expect(model.apiVersion).toEqual('1.0'); @@ -172,7 +177,7 @@ describe('configuration-builder', function() { const model= configBuilder.createSecHubConfigurationModel(builderData); /* test */ - dumpModel(model); + logDebug(model); expect(model.apiVersion).toEqual('1.0'); @@ -206,7 +211,7 @@ describe('configuration-builder', function() { const model= configBuilder.createSecHubConfigurationModel(builderData); /* test */ - dumpModel(model); + logDebug(model); expect(model.apiVersion).toEqual('1.0'); @@ -241,7 +246,7 @@ describe('configuration-builder', function() { const model= configBuilder.createSecHubConfigurationModel(builderData); /* test */ - dumpModel(model); + logDebug(model); expect(model.apiVersion).toEqual('1.0'); diff --git a/github-actions/scan/__test__/init-scan.test.ts b/github-actions/scan/__test__/init-scan.test.ts index 94c3fa99f9..16737206ea 100644 --- a/github-actions/scan/__test__/init-scan.test.ts +++ b/github-actions/scan/__test__/init-scan.test.ts @@ -5,6 +5,22 @@ import {initReportFormats, initSecHubJson} from '../src/init-scan'; jest.mock('./../src/configuration-builder'); import {SecHubConfigurationModelBuilderData, createSecHubConfigJsonFile} from '../src/configuration-builder'; +import * as core from '@actions/core'; + +jest.mock('@actions/core'); + +const mockInfo = core.info as jest.MockedFunction; + +const debugEnabled = false; + +beforeEach(() => { + mockInfo.mockImplementation((message: string | Error) => { + if (debugEnabled) { + console.log(`Info: ${message}`); + } + }); + mockInfo.mockClear(); +}); describe('initSecHubJson', function () { it('throws error if configPath is set, but file does not exist', function () { diff --git a/github-actions/scan/__test__/output-helper.test.ts b/github-actions/scan/__test__/output-helper.test.ts new file mode 100644 index 0000000000..22a84716a0 --- /dev/null +++ b/github-actions/scan/__test__/output-helper.test.ts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +import * as outputHelper from '../src/output-helper'; +import * as core from '@actions/core'; + +jest.mock('@actions/core'); + +describe('storeOutput', () => { + const mockedCore = core as jest.Mocked; + + it('test-key shall set SECHUB_OUTPUT_TEST_KEY', () => { + /* execute */ + outputHelper.storeOutput('test-key', 'test value1'); + + /* test */ + expect(mockedCore.exportVariable).toBeCalledWith('SECHUB_OUTPUT_TEST_KEY', 'test value1'); + }); + +}); \ No newline at end of file diff --git a/github-actions/scan/__test__/post-scan.test.ts b/github-actions/scan/__test__/post-scan.test.ts index 8f1d49ce3e..ada369953b 100644 --- a/github-actions/scan/__test__/post-scan.test.ts +++ b/github-actions/scan/__test__/post-scan.test.ts @@ -1,12 +1,15 @@ // SPDX-License-Identifier: MIT import * as core from '@actions/core'; +import * as outputHelper from '../src/output-helper'; import { collectReportData, reportOutputs } from '../src/post-scan'; import { getReport } from '../src/sechub-cli'; import { LAUNCHER_CONTEXT_DEFAULTS } from '../src/launcher'; jest.mock('@actions/core'); +jest.mock('../src/output-helper'); const mockedCore = core as jest.Mocked; +const mockedOutputHelper = outputHelper as jest.Mocked; jest.mock('../src/sechub-cli'); const mockedGetReport = getReport as jest.MockedFunction; @@ -20,7 +23,7 @@ describe('collectReportData', function () { /* prepare */ const testContext = Object.create(LAUNCHER_CONTEXT_DEFAULTS); - testContext.reportFormats= []; + testContext.reportFormats = []; /* execute */ collectReportData(testContext); @@ -33,7 +36,7 @@ describe('collectReportData', function () { it('format "json" - logs called 1 time , getReport never called', function () { /* prepare */ const testContext = Object.create(LAUNCHER_CONTEXT_DEFAULTS); - testContext.reportFormats= ['json']; + testContext.reportFormats = ['json']; /* execute */ collectReportData(testContext); @@ -47,8 +50,8 @@ describe('collectReportData', function () { /* prepare */ const testContext = Object.create(LAUNCHER_CONTEXT_DEFAULTS); - testContext.reportFormats= ['json','html']; - testContext.jobUUID=1234; // necessary for download + testContext.reportFormats = ['json', 'html']; + testContext.jobUUID = 1234; // necessary for download collectReportData(testContext); @@ -61,8 +64,8 @@ describe('collectReportData', function () { /* prepare */ const testContext = Object.create(LAUNCHER_CONTEXT_DEFAULTS); - testContext.reportFormats= ['json','html']; - testContext.jobUUID=1234; // necessary for download + testContext.reportFormats = ['json', 'html']; + testContext.jobUUID = 1234; // necessary for download /* execute */ collectReportData(testContext); @@ -78,11 +81,11 @@ describe('collectReportData', function () { /* prepare */ const testContext = Object.create(LAUNCHER_CONTEXT_DEFAULTS); - testContext.reportFormats= ['json','html','xyz','bla']; - testContext.jobUUID=1234; // necessary for download - + testContext.reportFormats = ['json', 'html', 'xyz', 'bla']; + testContext.jobUUID = 1234; // necessary for download + fsMock.readFileSync = jest.fn(() => '{"test": "test"}'); // Mock an empty JSON report - const sampleJson = {'test': 'test'}; + const sampleJson = { 'test': 'test' }; /* execute */ collectReportData(testContext); @@ -90,7 +93,7 @@ describe('collectReportData', function () { /* test */ expect(mockedCore.info).toHaveBeenCalledTimes(4); // "json, html, xyz, bla" - 4 times logged (valid format check is not done here) expect(mockedGetReport).toHaveBeenCalledTimes(3); // we fetch not json via getReport again (already done before), so only "html, xyz, bla" used - + expect(testContext.secHubReportJsonObject).toEqual(sampleJson); // json object is available }); @@ -136,13 +139,13 @@ describe('reportOutputs', function () { /* test */ expect(mockedCore.debug).toHaveBeenCalledTimes(6); - expect(mockedCore.setOutput).toHaveBeenCalledTimes(6); - expect(mockedCore.setOutput).toBeCalledWith('scan-trafficlight', 'RED'); - expect(mockedCore.setOutput).toBeCalledWith('scan-findings-count', '2'); - expect(mockedCore.setOutput).toBeCalledWith('scan-findings-high', '1'); - expect(mockedCore.setOutput).toBeCalledWith('scan-findings-medium', '0'); - expect(mockedCore.setOutput).toBeCalledWith('scan-findings-low', '1'); - expect(mockedCore.setOutput).toBeCalledWith('scan-readable-summary', 'SecHub reported traffic light color RED with 2 findings, categorized as follows: HIGH (1), LOW (1)'); + expect(mockedOutputHelper.storeOutput).toHaveBeenCalledTimes(6); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-trafficlight', 'RED'); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-findings-count', '2'); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-findings-high', '1'); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-findings-medium', '0'); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-findings-low', '1'); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-readable-summary', 'SecHub reported traffic light color RED with 2 findings, categorized as follows: HIGH (1), LOW (1)'); }); it('calls set github output with correct values when JSON report did not exist', function () { @@ -152,13 +155,13 @@ describe('reportOutputs', function () { /* test */ expect(mockedCore.debug).toHaveBeenCalledTimes(7); expect(mockedCore.debug).toBeCalledWith('No findings reported to be categorized.'); - expect(mockedCore.setOutput).toHaveBeenCalledTimes(6); - expect(mockedCore.setOutput).toBeCalledWith('scan-trafficlight', 'FAILURE'); - expect(mockedCore.setOutput).toBeCalledWith('scan-findings-count', '0'); - expect(mockedCore.setOutput).toBeCalledWith('scan-findings-high', '0'); - expect(mockedCore.setOutput).toBeCalledWith('scan-findings-medium', '0'); - expect(mockedCore.setOutput).toBeCalledWith('scan-findings-low', '0'); - expect(mockedCore.setOutput).toBeCalledWith('scan-readable-summary', 'SecHub scan could not be executed.'); + expect(mockedOutputHelper.storeOutput).toHaveBeenCalledTimes(6); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-trafficlight', 'FAILURE'); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-findings-count', '0'); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-findings-high', '0'); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-findings-medium', '0'); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-findings-low', '0'); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-readable-summary', 'SecHub scan could not be executed.'); }); it('calls set github output with correct values when traffic light is green without findings.', function () { @@ -180,13 +183,13 @@ describe('reportOutputs', function () { /* test */ expect(mockedCore.debug).toHaveBeenCalledTimes(6); - expect(mockedCore.setOutput).toHaveBeenCalledTimes(6); - expect(mockedCore.setOutput).toBeCalledWith('scan-trafficlight', 'GREEN'); - expect(mockedCore.setOutput).toBeCalledWith('scan-findings-count', '0'); - expect(mockedCore.setOutput).toBeCalledWith('scan-findings-high', '0'); - expect(mockedCore.setOutput).toBeCalledWith('scan-findings-medium', '0'); - expect(mockedCore.setOutput).toBeCalledWith('scan-findings-low', '0'); - expect(mockedCore.setOutput).toBeCalledWith('scan-readable-summary', 'SecHub reported traffic light color GREEN without findings'); + expect(mockedOutputHelper.storeOutput).toHaveBeenCalledTimes(6); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-trafficlight', 'GREEN'); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-findings-count', '0'); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-findings-high', '0'); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-findings-medium', '0'); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-findings-low', '0'); + expect(mockedOutputHelper.storeOutput).toBeCalledWith('scan-readable-summary', 'SecHub reported traffic light color GREEN without findings'); }); }); diff --git a/github-actions/scan/__test__/shell-arg-sanitizer.test.ts b/github-actions/scan/__test__/shell-arg-sanitizer.test.ts index f03ed1a19d..bd4c5021f8 100644 --- a/github-actions/scan/__test__/shell-arg-sanitizer.test.ts +++ b/github-actions/scan/__test__/shell-arg-sanitizer.test.ts @@ -1,8 +1,27 @@ +/* eslint-disable indent */ // SPDX-License-Identifier: MIT import * as shellArgSanitizer from '../src/shell-arg-sanitizer'; +import * as core from '@actions/core'; + +jest.mock('@actions/core'); + +const mockError = core.error as jest.MockedFunction; + +const debugEnabled = false; + +beforeEach(() => { + mockError.mockImplementation((message: string | Error) => { + if (debugEnabled) { + console.log(`Error: ${message}`); + } + }); + mockError.mockClear(); +}); + describe('sanitize', () => { + test.each([ ['rm -rf /; echo hacked'], // Command chaining ['echo $(whoami)'], // Command substitution @@ -69,7 +88,7 @@ describe('sanitize', () => { (arg) => { /* test */ expect(() => shellArgSanitizer.sanitize(arg)).not.toThrow(); - }); + }); it('removes whitespaces', function () { /* prepare */ diff --git a/github-actions/scan/dist/index.js b/github-actions/scan/dist/index.js index da1b79d485..89e5803b74 100644 --- a/github-actions/scan/dist/index.js +++ b/github-actions/scan/dist/index.js @@ -28403,6 +28403,7 @@ function getReport(jobUUID, reportFormat, context) { } ;// CONCATENATED MODULE: ./src/json-helper.ts +// SPDX-License-Identifier: MIT /** * Reads the given field from JSON. @@ -28428,6 +28429,50 @@ function getFieldFromJson(field, jsonData) { return currentKey; } +;// CONCATENATED MODULE: ./src/output-helper.ts + +/** + * Sets the value of an output (environment ) variable for the GitHub Action. + * This method is a workaround because of problems with of core.setOutput(..) method. + * There were problems with core.setOutput(...), see + * - https://github.com/mercedes-benz/sechub/issues/3481#issuecomment-2539015176 and + * - https://github.com/actions/toolkit/issues/1218 + * - https://github.com/actions/toolkit/issues/1906 + * + * As a workaround we provide instead of output + * special SecHub ouput environment variables with naming convention "SECHUB_OUTPUT_${fieldAdopted}" + * + * `fieldAdopted` is same as `field`, but uppercased and `-` will be replaced by `_` + * + * For example: `scan-readable-summary` will become `SECHUB_OUTPUT_SCAN_READABLE_SUMMARY` + * + * If debugging is enabled in action the setting will be logged. + */ +function storeOutput(field, value) { + // export the output to an "output" variable (this works) + const envVarName = `SECHUB_OUTPUT_${field.toUpperCase().replace(/-/g, '_')}`; + (0,core.exportVariable)(envVarName, value); + if (process.env.ACTIONS_RUNNER_DEBUG === 'true') { + // Print the environment variable for debugging + console.log(`Exported environment variable ${envVarName} with value: ${value}`); + } + // 1. This following out commented code was thought as a workaround + // for https://github.com/actions/toolkit/issues/1218 + // Because the GITHUB_OUTPUT file from a worfklow step (which worked) did not contain + // crypto.randomUUID() parts we tried to write the key/value file "normally" without + // the crypto parts, but It did not appear inside context output, means it didn't work + // (even when it the exact file structure as done by an echo ?!?!) + // But we keep it here for documentation: + // const outputFilePath = process.env['GITHUB_OUTPUT'] || ''; + // if (!outputFilePath) { + // throw new Error('GITHUB_OUTPUT environment variable is not set'); + // } + // const outputLine = `${field}=${value}\n`; + // fs.appendFileSync(outputFilePath, outputLine, { encoding: 'utf8' }); + // 2. Offical way by core API (does not work) + // setOutput(field,value); +} + ;// CONCATENATED MODULE: ./src/post-scan.ts // SPDX-License-Identifier: MIT @@ -28439,6 +28484,7 @@ function getFieldFromJson(field, jsonData) { + const NEW_LINE_SEPARATOR = '\n'; /** * Collect all necessary report data, downloads additional report formats (e.g. 'html') if necessary @@ -28662,7 +28708,7 @@ function buildSummary(trafficLight, totalFindings, findings) { function setOutput(field, value, dataFormat) { value = value !== null && value !== void 0 ? value : (dataFormat === 'number' ? 0 : 'FAILURE'); core.debug(`Output ${field} set to ${value}`); - core.setOutput(field, value.toString()); // Ensure value is converted to a string as GitHub Actions expects output variables to be strings. + storeOutput(field, value.toString()); // Ensure value is converted to a string as GitHub Actions expects output variables to be strings. } ;// CONCATENATED MODULE: ./src/projectname-resolver.ts @@ -28702,6 +28748,7 @@ function projectname_resolver_asJsonObject(text) { // EXTERNAL MODULE: external "os" var external_os_ = __nccwpck_require__(2037); ;// CONCATENATED MODULE: ./src/platform-helper.ts +// SPDX-License-Identifier: MIT function getPlatform() { return external_os_.platform(); @@ -46017,6 +46064,7 @@ const { parseHTML: esm_parseHTML } = static_namespaceObject; const { root: esm_root } = static_namespaceObject; //# sourceMappingURL=index.js.map ;// CONCATENATED MODULE: ./src/client-version-helper.ts +// SPDX-License-Identifier: MIT @@ -46190,7 +46238,7 @@ async function postScan(context) { main().catch(handleError); async function main() { - // Seperated launcher and main method. + // Separated launcher and main method. // Reason: launch mechanism would be loaded on imports // before we can handle mocking in integration tests! await launch(); diff --git a/github-actions/scan/package.json b/github-actions/scan/package.json index a2b1b52947..37db237428 100644 --- a/github-actions/scan/package.json +++ b/github-actions/scan/package.json @@ -5,6 +5,7 @@ "main": "dist/main.js", "scripts": { "build": "ncc build src/main.ts", + "deploy" : "./deploy.sh", "cleanBuild": "ncc cache clean;ncc build src/main.ts", "lint": "eslint src", "prettier": "npx prettier --write src", diff --git a/github-actions/scan/src/main.ts b/github-actions/scan/src/main.ts index f4cb84aa3f..4cf6dbc799 100644 --- a/github-actions/scan/src/main.ts +++ b/github-actions/scan/src/main.ts @@ -6,7 +6,7 @@ import { handleError } from './action-helper'; main().catch(handleError); async function main(): Promise { - // Seperated launcher and main method. + // Separated launcher and main method. // Reason: launch mechanism would be loaded on imports // before we can handle mocking in integration tests! await launch(); diff --git a/github-actions/scan/src/output-helper.ts b/github-actions/scan/src/output-helper.ts new file mode 100644 index 0000000000..c26a1002a8 --- /dev/null +++ b/github-actions/scan/src/output-helper.ts @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +import { setOutput } from '@actions/core/lib/core'; +import { exportVariable } from '@actions/core/lib/core'; + +/** + * Sets the value of an output (environment ) variable for the GitHub Action. + * This method is a workaround because of problems with of core.setOutput(..) method. + * There were problems with core.setOutput(...), see + * - https://github.com/mercedes-benz/sechub/issues/3481#issuecomment-2539015176 and + * - https://github.com/actions/toolkit/issues/1218 + * - https://github.com/actions/toolkit/issues/1906 + * + * As a workaround we provide instead of output + * special SecHub ouput environment variables with naming convention "SECHUB_OUTPUT_${fieldAdopted}" + * + * `fieldAdopted` is same as `field`, but uppercased and `-` will be replaced by `_` + * + * For example: `scan-readable-summary` will become `SECHUB_OUTPUT_SCAN_READABLE_SUMMARY` + * + * If debugging is enabled in action the setting will be logged. + */ +export function storeOutput(field: string, value: string) { + // export the output to an "output" variable (this works) + const envVarName = `SECHUB_OUTPUT_${field.toUpperCase().replace(/-/g, '_')}`; + exportVariable(envVarName, value); + if (process.env.ACTIONS_RUNNER_DEBUG === 'true') { + // Print the environment variable for debugging + console.log(`Exported environment variable ${envVarName} with value: ${value}`); + } + + // 1. This following out commented code was thought as a workaround + // for https://github.com/actions/toolkit/issues/1218 + // Because the GITHUB_OUTPUT file from a worfklow step (which worked) did not contain + // crypto.randomUUID() parts we tried to write the key/value file "normally" without + // the crypto parts, but It did not appear inside context output, means it didn't work + // (even when it the exact file structure as done by an echo ?!?!) + // But we keep it here for documentation: + + // const outputFilePath = process.env['GITHUB_OUTPUT'] || ''; + // if (!outputFilePath) { + // throw new Error('GITHUB_OUTPUT environment variable is not set'); + // } + + // const outputLine = `${field}=${value}\n`; + // fs.appendFileSync(outputFilePath, outputLine, { encoding: 'utf8' }); + + + // 2. Offical way by core API (does not work) + // setOutput(field,value); + +} diff --git a/github-actions/scan/src/post-scan.ts b/github-actions/scan/src/post-scan.ts index e7ffa5fca6..3442bf097c 100644 --- a/github-actions/scan/src/post-scan.ts +++ b/github-actions/scan/src/post-scan.ts @@ -8,8 +8,9 @@ import { LaunchContext } from './launcher'; import { logExitCode } from './exitcode'; import { getReport } from './sechub-cli'; import { getFieldFromJson } from './json-helper'; -import { execFileSync } from "child_process"; -import { sanitize } from "./shell-arg-sanitizer"; +import { execFileSync } from 'child_process'; +import { sanitize } from './shell-arg-sanitizer'; +import { storeOutput } from './output-helper'; const NEW_LINE_SEPARATOR = '\n'; @@ -280,5 +281,5 @@ function setOutput(field: string, value: any, dataFormat: string) { value = value ?? (dataFormat === 'number' ? 0 : 'FAILURE'); core.debug(`Output ${field} set to ${value}`); - core.setOutput(field, value.toString()); // Ensure value is converted to a string as GitHub Actions expects output variables to be strings. + storeOutput(field, value.toString()); // Ensure value is converted to a string as GitHub Actions expects output variables to be strings. } diff --git a/gradle/projects.gradle b/gradle/projects.gradle index acadbd954c..2955e328fb 100644 --- a/gradle/projects.gradle +++ b/gradle/projects.gradle @@ -58,7 +58,10 @@ projectType = [ project(':sechub-wrapper-secretvalidation'), /* archUnit */ - project(':sechub-archunit-test') + project(':sechub-archunit-test'), + + /* developerTools */ + project(':sechub-developertools') // to have DAUI available by build and a working fat jar provided by spring boot ], /* adapter projects - have simple spring dependencies, but know only sechub-adapter as base */ diff --git a/release-pipeline.jenkins b/release-pipeline.jenkins index 6f60cc88a8..0f214961ce 100644 --- a/release-pipeline.jenkins +++ b/release-pipeline.jenkins @@ -70,7 +70,7 @@ pipeline { * Reason: because we do NOT want to have the integration tests executed, otherwise gradle will not execute them * on integration phase again (because nothing has changed, so gradle will cache the results which are ignored ... */ - callGradleWrapper("ensureLocalhostCertificate build generateOpenapi buildDeveloperAdminUI -x :sechub-integrationtest:test -x :sechub-cli:build -Psechub.test.wiremock.https_port=${env.SECHUB_TEST_WIREMOCK_HTTPS_PORT} -Psechub.test.wiremock.http_port=${env.SECHUB_TEST_WIREMOCK_HTTP_PORT}") + callGradleWrapper("ensureLocalhostCertificate build generateOpenapi -x :sechub-integrationtest:test -x :sechub-cli:build -Psechub.test.wiremock.https_port=${env.SECHUB_TEST_WIREMOCK_HTTPS_PORT} -Psechub.test.wiremock.http_port=${env.SECHUB_TEST_WIREMOCK_HTTP_PORT}") callGradleWrapper(":sechub-api-java:build :sechub-systemtest:build :sechub-pds-tools:buildPDSToolsCLI -Dsechub.build.stage=api-necessary") } } diff --git a/sechub-developertools/README.adoc b/sechub-developertools/README.adoc new file mode 100644 index 0000000000..e238bf67c2 --- /dev/null +++ b/sechub-developertools/README.adoc @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +:toc: + +== About +`sechub-developertools` is a gradle subprojects +only containing tools for SecHub development. It is not intended to be used in production. + +WARNING: No other gradle submodule shall have this project as a dependency! + + +== Content + +=== DAUI +The Developer Administration UI (DAUI) is a simple quick and dirty administration client which will reuse parts +from `sechub-integration--test` (for example REST api access, URL building). + +Every single feature we implement, is available at this simple UI from the beginning. +It is extreme simple to extend. + +[TIP] +==== +Look into +`/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/job/GetJobStatusAction.java` +for an example how you can simply implement a feature. +==== + +===== Build +Inside sechub root folder execute: + +[source, bash] +---- +./gradlew :sechub-developertools:bootJar +---- + +This will build +`/sechub-developertools/build/libs/sechub-developertools-0.0.0.jar` + + +===== Start +[source, bash] +---- +export SECHUB_ADMIN_APITOKEN=int-test_superadmin-pwd +export SECHUB_ADMIN_ENVIRONMENT=localhost +export SECHUB_ADMIN_USERID=int-test_superadmin +export SECHUB_ADMIN_SERVER=localhost +export SECHUB_ADMIN_SERVER_PORT=8443 + +java -jar ./sechub-developertools/build/libs/sechub-developertools-0.0.0.jar +---- + diff --git a/sechub-developertools/README.md b/sechub-developertools/README.md deleted file mode 100644 index 8b14dd7e24..0000000000 --- a/sechub-developertools/README.md +++ /dev/null @@ -1,13 +0,0 @@ - -What is "sechub-developertools" for? -==================================== - -A collection of some tooling around sechub which is only available for developers and not part of -a deployment or a client given to users. - -__NEVER give those tools to admins or users!__ - -## Content: -### 1. Quick and dirty admin client -One of the first things created here is a simple quick and dirty administration client which will reuse parts -from integration test project - e.g. rest api access, url building etc. which is already done there. *So we do not reinvent the wheel...* \ No newline at end of file diff --git a/sechub-developertools/build.gradle b/sechub-developertools/build.gradle index b7e3887037..4b132ec41d 100644 --- a/sechub-developertools/build.gradle +++ b/sechub-developertools/build.gradle @@ -7,7 +7,9 @@ */ plugins { id 'java-library' + id 'org.springframework.boot' apply true } + dependencies { implementation project(':sechub-testframework') @@ -23,48 +25,9 @@ dependencies { } -task startIntegrationTestAdminUI(type: JavaExec){ - - group 'sechub' - description 'Starts developer admin ui ready to use with for a server started by gradle task ' - - classpath = sourceSets.main.runtimeClasspath - - mainClass = 'com.mercedesbenz.sechub.developertools.admin.ui.DeveloperAdministrationUI' - - jvmArgs = ['-Dsechub.developertools.admin.integrationtestserver=true', - '-Dsechub.developertools.admin.server=localhost', - '-Dsechub.developertools.admin.serverport=8443', - '-Dsechub.developertools.admin.userid=int-test_superadmin', - '-Dsechub.developertools.admin.apitoken=int-test_superadmin-pwd'] - -} - -task buildDeveloperAdminUI(type: Jar, dependsOn: build) { - group 'sechub' - description 'Builds the SecHub Developer Admin tool as standalone executable jar. Use launch-developer-admin-ui script to execute' - archiveBaseName = 'sechub-developer-admin-ui' - /* TODO: This is a 'dirty' fix for the standard archive entries limit of 64K. We should refactor this module to make it leaner */ - zip64 = true - - manifest { - attributes 'Main-Class': 'com.mercedesbenz.sechub.developertools.admin.ui.DeveloperAdministrationUI' - } - - from { - configurations.runtimeClasspath.collect { - it.isDirectory() ? it : zipTree(it) - } - } - duplicatesStrategy = DuplicatesStrategy.INCLUDE - with jar -} - - task importEclipseProjectsNeedingOpenApiFile(type: Exec){ workingDir "$rootDir" commandLine './gradlew', ':sechub-systemtest:cleanEclipse',':sechub-systemtest:eclipse', ':sechub-web-server:cleanEclipse',':sechub-web-server:eclipse', ':sechub-api-java:cleanEclipse',':sechub-api-java:eclipse', ':sechub-pds-tools:cleanEclipse',':sechub-pds-tools:eclipse',':sechub-examples:example-sechub-api-java:cleanEclipse',':sechub-examples:example-sechub-api-java:eclipse','-Dsechub.build.stage=all' - } /* diff --git a/sechub-developertools/scripts/launch-developer-admin-ui b/sechub-developertools/scripts/launch-developer-admin-ui index 8e7e6f49da..3e06b4a36d 100755 --- a/sechub-developertools/scripts/launch-developer-admin-ui +++ b/sechub-developertools/scripts/launch-developer-admin-ui @@ -1,17 +1,17 @@ #!/bin/bash -# This the base template suitable for having a standalone running integrationtest admin ui. -# Howto use when manually created: -# - copy this script to a dedicated folder (X) -# - define system properties and/or environment entries as necessary -# - call ./gradlew buildDeveloperAdminUI -# - copy build/libs/sechub-developer-admin-ui-0.0.0.jar to the folder (X) -# - ensure script is executable and you have a java >=8 available on your system -# - call the script and you are done -# -# - -DAUI_VERSION="0.0.0" +# This the base template suitable for having a standalone running SecHub admin ui. +# The sechub-developertools-x.y.z.jar can be downloaded +# from the latest SecHub server release on github.com. +# Alternatively the jar file can be created manually: +# - call ./gradlew :sechub-developertools:bootJar +# - the created file is: build/libs/sechub-developertools-0.0.0.jar +# +# Use: +# - copy this script to a dedicated folder and make it executable: chmod a+x +# - edit the copied script and define your system properties below +# - copy sechub-developertools-x.y.z.jar to the folder +# - call the script and the SecHub Developer Admin UI will open export SECHUB_ADMIN_USERID=int-test_superadmin # your admin user id export SECHUB_ADMIN_APITOKEN=int-test_superadmin-pwd @@ -26,26 +26,17 @@ export SECHUB_ENABLE_INTEGRATION_TESTSERVER_MENU=true export SECHUB_DISABLE_CONFIRMATIONS=true export SECHUB_CHECK_STATUS_ON_STARTUP=false -export SECHUB_ADMIN_ENVIRONMENT=localhost #Use : localhost, no color. Dedicated menu colors for: PROD, INT or TESTxyz +export SECHUB_ADMIN_ENVIRONMENT="localhost" # Use : "localhost", no color. Dedicated menu colors for: "PROD", "INT" or "TESTxyz" -export SECHUB_MASS_OPERATION_PARENTDIRECTORY=/home/$UID/.sechub/inttest/mass-operations #mass operation directory (containign csv files ) -export SECHUB_TARGETFOLDER_FOR_SECHUB_CLIENT_SCAN="/home/$USER/.sechub/test-targetfolder1" +export SECHUB_MASS_OPERATION_PARENTDIRECTORY="$HOME/.sechub/inttest/mass-operations" # mass operation directory (containign csv files ) +export SECHUB_TARGETFOLDER_FOR_SECHUB_CLIENT_SCAN="$HOME/.sechub/test-targetfolder1" -export SECHUB_PATH_TO_SECHUB_CLIENT_BINARY="/home/$USER/.local/bin/sechub" +export SECHUB_PATH_TO_SECHUB_CLIENT_BINARY="/usr/local/bin/sechub" export SECHUB_TRUSTALL_DENIED=false; # change this for other environments! -# when next entry is set to true TRUSTALL is denied (currently only at go client) echo "-------------------------------------------------------------" -echo "Starting DAUI $DAUI_VERSION (Developer Admin UI)" +echo "Starting SecHub Developer Admin UI" echo "-------------------------------------------------------------" echo "- SECHUB_TARGETFOLDER_FOR_SECHUB_CLIENT_SCAN:$SECHUB_TARGETFOLDER_FOR_SECHUB_CLIENT_SCAN" echo "- SECHUB_PATH_TO_SECHUB_CLIENT_BINARY: $SECHUB_PATH_TO_SECHUB_CLIENT_BINARY" -java -jar sechub-developer-admin-ui-$DAUI_VERSION.jar - -echo "Press any key to continue" -while [ true ] ; do -read -t 3 -n 1 -if [ $? = 0 ] ; then -exit ; -fi -done +java -jar sechub-developertools-*.jar diff --git a/sechub-developertools/scripts/sdc.sh b/sechub-developertools/scripts/sdc.sh index 003ca50a2d..f502bc9622 100755 --- a/sechub-developertools/scripts/sdc.sh +++ b/sechub-developertools/scripts/sdc.sh @@ -345,7 +345,7 @@ if [[ "$FULL_BUILD" = "YES" ]]; then ./gradlew spotlessCheck :sechub-cli:buildGo :sechub-cli:testGo step "Build Server, DAUI and generate OpenAPI file" - ./gradlew ensureLocalhostCertificate build generateOpenapi buildDeveloperAdminUI -x :sechub-cli:build + ./gradlew ensureLocalhostCertificate build generateOpenapi -x :sechub-cli:build step "Generate and build Java projects related to SecHub Java API" ./gradlew :sechub-api-java:build :sechub-systemtest:build :sechub-pds-tools:buildPDSToolsCLI :sechub-web-server:build -Dsechub.build.stage=api-necessary diff --git a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/DAUIApplication.java b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/DAUIApplication.java new file mode 100644 index 0000000000..e402416979 --- /dev/null +++ b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/DAUIApplication.java @@ -0,0 +1,16 @@ +package com.mercedesbenz.sechub.developertools; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication(exclude = RepositoryRestMvcAutoConfiguration.class) // we do not want to have automatic resources in HAL & co +public class DAUIApplication { + + public static void main(String[] args) { + SpringApplicationBuilder builder = new SpringApplicationBuilder(DAUIApplication.class); + builder.headless(false); + + builder.run(args); + } +} diff --git a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/DAUICLIRunner.java b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/DAUICLIRunner.java new file mode 100644 index 0000000000..faba0a26ac --- /dev/null +++ b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/DAUICLIRunner.java @@ -0,0 +1,16 @@ +package com.mercedesbenz.sechub.developertools; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.mercedesbenz.sechub.developertools.admin.ui.DeveloperAdministrationUI; + +@Component +public class DAUICLIRunner implements CommandLineRunner { + + @Override + public void run(String... args) throws Exception { + DeveloperAdministrationUI.main(args); + } + +} diff --git a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/CommandUI.java b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/CommandUI.java index cdbefba643..e38c0d3530 100644 --- a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/CommandUI.java +++ b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/CommandUI.java @@ -460,7 +460,7 @@ private void createIntegrationTestServerMenu() { if (!ConfigurationSetup.isIntegrationTestServerMenuEnabled()) { menu.setEnabled(false); - menu.setToolTipText("Not enabled, use \"-D" + ConfigurationSetup.SECHUB_ENABLE_INTEGRATION_TESTSERVER_MENU.getSystemPropertyid() + menu.setToolTipText("Not enabled, use \"-D" + ConfigurationSetup.SECHUB_ENABLE_INTEGRATION_TESTSERVER_MENU.getSystemPropertyId() + "=true\" to enable it and run an integration test server!"); return; } diff --git a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/ConfigurationSetup.java b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/ConfigurationSetup.java index b785a70f35..60644b8033 100644 --- a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/ConfigurationSetup.java +++ b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/ConfigurationSetup.java @@ -15,88 +15,94 @@ */ public enum ConfigurationSetup { - SECHUB_ADMIN_USERID(false), + SECHUB_ADMIN_USERID(false, true), - SECHUB_ADMIN_APITOKEN(false), + SECHUB_ADMIN_APITOKEN(false, true), - SECHUB_ADMIN_SERVER("sechub.developertools.admin.server", false), + SECHUB_ADMIN_SERVER("sechub.developertools.admin.server", false, false), - SECHUB_ADMIN_SERVER_PORT("sechub.developertools.admin.serverport", true), + SECHUB_ADMIN_SERVER_PORT("sechub.developertools.admin.serverport", true, false), - SECHUB_ADMIN_SERVER_PROTOCOL("sechub.developertools.admin.serverprotocol", true), + SECHUB_ADMIN_SERVER_PROTOCOL("sechub.developertools.admin.serverprotocol", true, false), - SECHUB_ENABLE_INTEGRATION_TESTSERVER_MENU("sechub.developertools.admin.integrationtestserver", true), + SECHUB_ENABLE_INTEGRATION_TESTSERVER_MENU("sechub.developertools.admin.integrationtestserver", true, false), - SECHUB_DISABLE_CONFIRMATIONS("sechub.developertools.admin.disable.confim", true, "When set to true, no confirmation dialogs will appear"), + SECHUB_DISABLE_CONFIRMATIONS("sechub.developertools.admin.disable.confim", true, "When set to true, no confirmation dialogs will appear", false), - SECHUB_CHECK_STATUS_ON_STARTUP("sechub.developertools.admin.statuscheck.onstartup", true), + SECHUB_CHECK_STATUS_ON_STARTUP("sechub.developertools.admin.statuscheck.onstartup", true, false), /** * Here you can set environment information. See description for details */ SECHUB_ADMIN_ENVIRONMENT("sechub.developertools.admin.environment", false, - "Use 'PROD', 'INT' or anything containing 'TEST' for dedicated colors (red,yellow,cyan). All other variants are without special colors"), + "Use 'PROD', 'INT' or anything containing 'TEST' for dedicated colors (red,yellow,cyan). All other variants are without special colors", false), - SECHUB_MASS_OPERATION_PARENTDIRECTORY("sechub.developertools.admin.massoperation.parentdirectory", true), + SECHUB_MASS_OPERATION_PARENTDIRECTORY("sechub.developertools.admin.massoperation.parentdirectory", true, false), - PDS_SOLUTION_GENERATOR_SECHUB_CONFIGURATION_DIRECTORY("pds.solution.generator.config.sechub.parentdirectory", true), + PDS_SOLUTION_GENERATOR_SECHUB_CONFIGURATION_DIRECTORY("pds.solution.generator.config.sechub.parentdirectory", true, false), /** * Usage: for example -Dsechub.developertools.output.font.settings="courier 18" */ - SECHUB_OUTPUT_FONT_SETTINGS("sechub.developertools.output.font.settings", true), + SECHUB_OUTPUT_FONT_SETTINGS("sechub.developertools.output.font.settings", true, false), - SECHUB_LOOK_AND_FEEL("sechub.developertools.lookandfeel.nimbus", true), + SECHUB_LOOK_AND_FEEL("sechub.developertools.lookandfeel.nimbus", true, false), SECHUB_TARGETFOLDER_FOR_SECHUB_CLIENT_SCAN("sechub.developertools.admin.launch.scan.targetfolder", true, - "Default path to parent folder of configuration file and sources to scan"), + "Default path to parent folder of configuration file and sources to scan", false), /** * When defined we use this path instead IDE relative one */ - SECHUB_PATH_TO_SECHUB_CLIENT_BINARY("sechub.developertools.admin.launch.clientbinary.path", true), + SECHUB_PATH_TO_SECHUB_CLIENT_BINARY("sechub.developertools.admin.launch.clientbinary.path", true, false), - SECHUB_TRUSTALL_DENIED("sechub.developertools.trustall.denied", true); + SECHUB_TRUSTALL_DENIED("sechub.developertools.trustall.denied", true, false); ; - private String systemPropertyid; + private String systemPropertyId; private String environmentEntryId; private boolean optional; private String description; + private boolean sensitiveInformation; - private ConfigurationSetup(boolean optional) { - this(null, optional); + private ConfigurationSetup(boolean optional, boolean sensitiveInformation) { + this(null, optional, null, sensitiveInformation); } - private ConfigurationSetup(String systemPropertyid, boolean optional) { - this(systemPropertyid, optional, null); + private ConfigurationSetup(String systemPropertyid, boolean optional, boolean sensitiveInformation) { + this(systemPropertyid, optional, null, sensitiveInformation); } - private ConfigurationSetup(String systemPropertyid, boolean optional, String description) { + private ConfigurationSetup(String systemPropertyid, boolean optional, String description, boolean sensitiveInformation) { this.optional = optional; - this.systemPropertyid = systemPropertyid; + this.systemPropertyId = systemPropertyid; this.environmentEntryId = name(); this.description = description; + this.sensitiveInformation = sensitiveInformation; + } + + public boolean isSensitiveInformation() { + return sensitiveInformation; } public String getEnvironmentEntryId() { return environmentEntryId; } - public String getSystemPropertyid() { - return systemPropertyid; + public String getSystemPropertyId() { + return systemPropertyId; } public static boolean isIntegrationTestServerMenuEnabled() { - return Boolean.getBoolean(ConfigurationSetup.SECHUB_ENABLE_INTEGRATION_TESTSERVER_MENU.getSystemPropertyid()); + return Boolean.getBoolean(ConfigurationSetup.SECHUB_ENABLE_INTEGRATION_TESTSERVER_MENU.getSystemPropertyId()); } public static boolean isConfirmationDisabled() { - return Boolean.getBoolean(ConfigurationSetup.SECHUB_DISABLE_CONFIRMATIONS.getSystemPropertyid()); + return Boolean.getBoolean(ConfigurationSetup.SECHUB_DISABLE_CONFIRMATIONS.getSystemPropertyId()); } public static boolean isCheckOnStartupEnabled() { - return Boolean.getBoolean(ConfigurationSetup.SECHUB_CHECK_STATUS_ON_STARTUP.getSystemPropertyid()); + return Boolean.getBoolean(ConfigurationSetup.SECHUB_CHECK_STATUS_ON_STARTUP.getSystemPropertyId()); } public static String getOutputFontSettings(String defaultSetting) { @@ -114,11 +120,11 @@ public static String getParentFolderPathForSecHubClientScanOrNull() { * GTK */ public static boolean isNimbusLookAndFeelEnabled() { - return Boolean.getBoolean(ConfigurationSetup.SECHUB_LOOK_AND_FEEL.getSystemPropertyid()); + return Boolean.getBoolean(ConfigurationSetup.SECHUB_LOOK_AND_FEEL.getSystemPropertyId()); } public static boolean isTrustAllDenied() { - return Boolean.getBoolean(ConfigurationSetup.SECHUB_TRUSTALL_DENIED.getSystemPropertyid()); + return Boolean.getBoolean(ConfigurationSetup.SECHUB_TRUSTALL_DENIED.getSystemPropertyId()); } /** @@ -161,8 +167,8 @@ public String getStringValue(String defaultValue, boolean failWhenNull) { } /* then try system property - if not already set */ if (value == null) { - if (systemPropertyid != null) { - value = System.getProperty(systemPropertyid, defaultValue); + if (systemPropertyId != null) { + value = System.getProperty(systemPropertyId, defaultValue); } } /* then use default value - if not already set */ @@ -185,15 +191,56 @@ private void assertNotEmpty(String part, String missing) { private static String description() { StringBuilder sb = new StringBuilder(); - sb.append("Use following system properties:\n"); + sb.append("\nMandatory settings:\n------------------------------\nAs environment variables):\n"); + appendEnvironmentVariables(sb, false); + sb.append("Or by system properties:\n"); + appendSystemProperties(sb, false); + + sb.append("\nOptional settings:\n------------------------------\nAs environment variables):\n"); + appendEnvironmentVariables(sb, true); + appendSystemProperties(sb, true); + + return sb.toString(); + } + + private static void appendEnvironmentVariables(StringBuilder sb, boolean expectedToBeOptional) { + for (ConfigurationSetup setup : values()) { + if (expectedToBeOptional != setup.optional) { + continue; + } + if (setup.environmentEntryId == null) { + continue; + } + sb.append(" "); + sb.append(setup.environmentEntryId); + sb.append("=some-value"); + if (setup.optional) { + sb.append(" (optional)"); + } + if (setup.isSensitiveInformation()) { + sb.append(" (sensitive information)"); + } + if (setup.description != null) { + sb.append(" ["); + sb.append(setup.description); + sb.append("]"); + } + sb.append("\n"); + } + } + + private static void appendSystemProperties(StringBuilder sb, boolean expectedToBeOptional) { for (ConfigurationSetup setup : values()) { - if (setup.systemPropertyid == null) { + if (setup.optional != expectedToBeOptional) { + continue; + } + if (setup.systemPropertyId == null || setup.isSensitiveInformation()) { continue; } sb.append("-D"); - sb.append(setup.systemPropertyid); + sb.append(setup.systemPropertyId); sb.append("="); - String val = System.getProperty(setup.systemPropertyid); + String val = System.getProperty(setup.systemPropertyId); if (val != null && !val.isEmpty()) { val = "**** (already set)"; } @@ -208,16 +255,6 @@ private static String description() { } sb.append("\n"); } - sb.append("\nFor security reasons next parts must be set as environment variables (so not visible in process view):\n"); - for (ConfigurationSetup setup : values()) { - if (setup.environmentEntryId == null) { - continue; - } - sb.append(" "); - sb.append(setup.environmentEntryId); - sb.append("'\n"); - } - return sb.toString(); } } diff --git a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/CredentialUI.java b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/CredentialUI.java index c9ba22220e..5a34a0e6bc 100644 --- a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/CredentialUI.java +++ b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/CredentialUI.java @@ -88,11 +88,11 @@ public CredentialUI(UIContext context) { serverPortSpinner.setEnabled(false); protocolField.setEnabled(false); - serverField.setToolTipText(ConfigurationSetup.SECHUB_ADMIN_SERVER.getSystemPropertyid()); - useridField.setToolTipText(ConfigurationSetup.SECHUB_ADMIN_USERID.getSystemPropertyid()); - passwordField.setToolTipText(ConfigurationSetup.SECHUB_ADMIN_APITOKEN.getSystemPropertyid()); - serverPortSpinner.setToolTipText(ConfigurationSetup.SECHUB_ADMIN_SERVER_PORT.getSystemPropertyid()); - protocolField.setToolTipText(ConfigurationSetup.SECHUB_ADMIN_SERVER_PROTOCOL.getSystemPropertyid()); + serverField.setToolTipText(ConfigurationSetup.SECHUB_ADMIN_SERVER.getSystemPropertyId()); + useridField.setToolTipText(ConfigurationSetup.SECHUB_ADMIN_USERID.getSystemPropertyId()); + passwordField.setToolTipText(ConfigurationSetup.SECHUB_ADMIN_APITOKEN.getSystemPropertyId()); + serverPortSpinner.setToolTipText(ConfigurationSetup.SECHUB_ADMIN_SERVER_PORT.getSystemPropertyId()); + protocolField.setToolTipText(ConfigurationSetup.SECHUB_ADMIN_SERVER_PROTOCOL.getSystemPropertyId()); useDifferentColorsForWellknownEnvironments(); diff --git a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/client/TriggerSecHubClientSynchronousScanAction.java b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/client/TriggerSecHubClientSynchronousScanAction.java index b00b4b0cc5..03f0f9d548 100644 --- a/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/client/TriggerSecHubClientSynchronousScanAction.java +++ b/sechub-developertools/src/main/java/com/mercedesbenz/sechub/developertools/admin/ui/action/client/TriggerSecHubClientSynchronousScanAction.java @@ -36,7 +36,7 @@ public void execute(ActionEvent event) throws Exception { "Please enter target folder for sechub scan:\n\n"+ (optionalProject.isPresent() ? "": "WARN: You didn't define a project. So this folder must contain a sechub.json having projectId defined inside to work!)")+"\nServer, user and apitoken are used by DAUI setup!\n"+ "INFO: You can set a a default by system property:\n"+ - ConfigurationSetup.SECHUB_TARGETFOLDER_FOR_SECHUB_CLIENT_SCAN.getSystemPropertyid(), InputCacheIdentifier.CLIENT_SCAN_TARGETFOLDER + ConfigurationSetup.SECHUB_TARGETFOLDER_FOR_SECHUB_CLIENT_SCAN.getSystemPropertyId(), InputCacheIdentifier.CLIENT_SCAN_TARGETFOLDER ); /* @formatter:on */ if (!optionalPath.isPresent()) { diff --git a/sechub-developertools/src/main/resources/application.yaml b/sechub-developertools/src/main/resources/application.yaml new file mode 100644 index 0000000000..4b4cc19c9c --- /dev/null +++ b/sechub-developertools/src/main/resources/application.yaml @@ -0,0 +1,3 @@ +spring: + main: + web-application-type: none # we do not want to start tomcat by DAUI \ No newline at end of file diff --git a/sechub-doc/src/docs/asciidoc/sechub-developer-quickstart-guide.adoc b/sechub-doc/src/docs/asciidoc/sechub-developer-quickstart-guide.adoc index c87c1a1889..8acdc852af 100644 --- a/sechub-doc/src/docs/asciidoc/sechub-developer-quickstart-guide.adoc +++ b/sechub-doc/src/docs/asciidoc/sechub-developer-quickstart-guide.adoc @@ -641,7 +641,7 @@ We explain the integration test setup based on the IntelliJ IDEA IDE. First run the following commands to create the necessary certificates for the integration test servers: ---- -./gradlew ensureLocalhostCertificate build generateOpenapi buildDeveloperAdminUI -x :sechub-cli:build +./gradlew ensureLocalhostCertificate build generateOpenapi -x :sechub-cli:build ---- ---- ./gradlew clean diff --git a/sechub-openapi-java/src/main/resources/openapi.yaml b/sechub-openapi-java/src/main/resources/openapi.yaml index 89428aa834..28369cef85 100644 --- a/sechub-openapi-java/src/main/resources/openapi.yaml +++ b/sechub-openapi-java/src/main/resources/openapi.yaml @@ -2019,13 +2019,17 @@ components: title: SecHubJobInfoForUserListPage type: object properties: - jobs: + content: type: array items: $ref: '#/components/schemas/SecHubJobInfoForUser' projectId: type: string - + page: + type: int + totalPages: + type: int + TemplateType: title: TemplateType type: string diff --git a/sechub-web-ui/README.md b/sechub-web-ui/README.md index 9d4872cecd..d5ee0f159b 100644 --- a/sechub-web-ui/README.md +++ b/sechub-web-ui/README.md @@ -40,6 +40,8 @@ To start the development server with hot-reload, run the following command. The npm run dev ``` +> If you receive an empty page, do a reload (sometimes it needs a little bit of time until everything is setup correctly) + > Add NODE_OPTIONS='--no-warnings' to suppress the JSON import warnings that happen as part of the Vuetify import mapping. If you are on Node [v21.3.0](https://nodejs.org/en/blog/release/v21.3.0) or higher, you can change this to NODE_OPTIONS='--disable-warning=5401'. If you don't mind the warning, you can remove this from your package.json dev script. #### Running in Development mode with sechub for testing diff --git a/sechub-web-ui/package-lock.json b/sechub-web-ui/package-lock.json index e9661aa883..5f7e2c0eb8 100644 --- a/sechub-web-ui/package-lock.json +++ b/sechub-web-ui/package-lock.json @@ -11,6 +11,7 @@ "@mdi/font": "7.4.47", "@openapitools/openapi-generator-cli": "^2.15.3", "core-js": "^3.37.1", + "pinia": "^2.3.0", "roboto-fontface": "*", "vue": "^3.4.31", "vue-i18n": "^10.0.5", @@ -5871,6 +5872,28 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pinia": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.0.tgz", + "integrity": "sha512-ohZj3jla0LL0OH5PlLTDMzqKiVw2XARmC1XYLdLWIPBMdhDW/123ZWr4zVAhtJm+aoSkFa13pYXskAvAscIkhQ==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/pkg-types": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.1.tgz", @@ -7272,6 +7295,32 @@ } } }, + "node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/vue-eslint-parser": { "version": "9.4.3", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", diff --git a/sechub-web-ui/package.json b/sechub-web-ui/package.json index d8ef8e8434..ffe48f1ef9 100644 --- a/sechub-web-ui/package.json +++ b/sechub-web-ui/package.json @@ -12,6 +12,7 @@ "@mdi/font": "7.4.47", "@openapitools/openapi-generator-cli": "^2.15.3", "core-js": "^3.37.1", + "pinia": "^2.3.0", "roboto-fontface": "*", "vue": "^3.4.31", "vue-i18n": "^10.0.5", diff --git a/sechub-web-ui/src/components.d.ts b/sechub-web-ui/src/components.d.ts index a2f3fbe49f..0e51db0019 100644 --- a/sechub-web-ui/src/components.d.ts +++ b/sechub-web-ui/src/components.d.ts @@ -9,9 +9,10 @@ export {} declare module 'vue' { export interface GlobalComponents { AppHeader: typeof import('./components/AppHeader.vue')['default'] - Projects: typeof import('./components/Projects.vue')['default'] + Pagination: typeof import('./components/Pagination.vue')['default'] + Project: typeof import('./components/Project.vue')['default'] + ProjectsList: typeof import('./components/ProjectsList.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] - SecHubDefault: typeof import('./components/SecHubDefault.vue')['default'] } } diff --git a/sechub-web-ui/src/components/Pagination.vue b/sechub-web-ui/src/components/Pagination.vue new file mode 100644 index 0000000000..62283dd3aa --- /dev/null +++ b/sechub-web-ui/src/components/Pagination.vue @@ -0,0 +1,59 @@ + + + diff --git a/sechub-web-ui/src/components/Project.vue b/sechub-web-ui/src/components/Project.vue new file mode 100644 index 0000000000..51e3f93f96 --- /dev/null +++ b/sechub-web-ui/src/components/Project.vue @@ -0,0 +1,143 @@ + + + + + + diff --git a/sechub-web-ui/src/components/Projects.vue b/sechub-web-ui/src/components/ProjectsList.vue similarity index 81% rename from sechub-web-ui/src/components/Projects.vue rename to sechub-web-ui/src/components/ProjectsList.vue index 3fc9a1479f..58087600fa 100644 --- a/sechub-web-ui/src/components/Projects.vue +++ b/sechub-web-ui/src/components/ProjectsList.vue @@ -21,6 +21,7 @@ class="ma-5" rounded="lg" :value="project" + @click="openProjectPage(project)" >