diff --git a/.eslintignore b/.eslintignore
index 2d0c0644..9c3f2cfc 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,3 +1,4 @@
dist/
node_modules/
coverage/
+*.test.ts
\ No newline at end of file
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 00000000..b6a68c93
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1 @@
+!!! REMEMBER TO ADD A LABEL IN ORDER TO CREATE A RELEASE !!! (REMOVE THIS)
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 00000000..8e13fe40
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,41 @@
+name: 'CodeQL'
+
+on:
+ push:
+ branches: ['main']
+ pull_request:
+ branches: ['main']
+ schedule:
+ - cron: '25 21 * * 1'
+
+jobs:
+ analyze:
+ name: Analyze (${{ matrix.language }})
+ runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
+ timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
+ permissions:
+ security-events: write
+ packages: read
+ actions: read
+ contents: read
+
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - language: javascript-typescript
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: ${{ matrix.language }}
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: '/language:${{matrix.language}}'
diff --git a/README.md b/README.md
index 2c1384dd..0e208add 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,17 @@
[![OP Compliance Dashboard](https://img.shields.io/badge/OP%20Compliance%20Dashboard-click%20here-blue)](https://cydig.omegapoint.cloud/cydig)
-![Timestamp](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-comp-state-prod.azurewebsites.net%2Fapi%2FReadToReadme%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX%26teamName%3DCyDig%26teamProjectName%3DCyDig%26codeRepositoryName%3Dcydig-compliance-action%26stateType%3Dtimestamp)
-![threatModelingDate](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-comp-state-prod.azurewebsites.net%2Fapi%2FReadToReadme%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX%26teamName%3DCyDig%26teamProjectName%3DCyDig%26codeRepositoryName%3Dcydig-compliance-action%26stateType%3DthreatModelingDate)
-![numberOfReviewers](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-comp-state-prod.azurewebsites.net%2Fapi%2FReadToReadme%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX%26teamName%3DCyDig%26teamProjectName%3DCyDig%26codeRepositoryName%3Dcydig-compliance-action%26stateType%3DnumberOfReviewers)
-[![secureScore](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-comp-state-prod.azurewebsites.net%2Fapi%2FReadToReadme%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX%26teamName%3DCyDig%26teamProjectName%3DCyDig%26codeRepositoryName%3Dcydig-compliance-action%26stateType%3DsecureScore)](https://portal.azure.com/#view/Microsoft_Azure_Security/RecommendationsBladeV2/subscriptionIds~/%5B%2215c6235f-9e0f-4073-baf4-4fd0a7913d76%22%5D/source/SecurityPosture_ViewRecommendation)
-[![allowedLocationPolicy](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-comp-state-prod.azurewebsites.net%2Fapi%2FReadToReadme%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX%26teamName%3DCyDig%26teamProjectName%3DCyDig%26codeRepositoryName%3Dcydig-compliance-action%26stateType%3DallowedLocationPolicy)](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Compliance)
-![pentestDate](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-comp-state-prod.azurewebsites.net%2Fapi%2FReadToReadme%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX%26teamName%3DCyDig%26teamProjectName%3DCyDig%26codeRepositoryName%3Dcydig-compliance-action%26stateType%3DpentestDate)
-![numberOfDeployedVMs](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-comp-state-prod.azurewebsites.net%2Fapi%2FReadToReadme%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX%26teamName%3DCyDig%26teamProjectName%3DCyDig%26codeRepositoryName%3Dcydig-compliance-action%26stateType%3DnumberOfDeployedVMs)
-![usersInProduction](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-comp-state-prod.azurewebsites.net%2Fapi%2FReadToReadme%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX%26teamName%3DCyDig%26teamProjectName%3DCyDig%26codeRepositoryName%3Dcydig-compliance-action%26stateType%3DusersInProduction)
-
-
+![Timestamp](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-badge-service-prod.azurewebsites.net%2Fapi%2Fteams%2FCyDig%2Fsources%2FGitHub%2Fprojects%2Fnot-specified%2Frepositories%2Fcydig-compliance-action%2Fcontrols%2Ftimestamp%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX)
+![threatModelingDate](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-badge-service-prod.azurewebsites.net%2Fapi%2Fteams%2FCyDig%2Fsources%2FGitHub%2Fprojects%2Fnot-specified%2Frepositories%2Fcydig-compliance-action%2Fcontrols%2FthreatModelingDate%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX)
+![numberOfReviewers](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-badge-service-prod.azurewebsites.net%2Fapi%2Fteams%2FCyDig%2Fsources%2FGitHub%2Fprojects%2Fnot-specified%2Frepositories%2Fcydig-compliance-action%2Fcontrols%2FnumberOfReviewers%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX)
+![scaTool](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-badge-service-prod.azurewebsites.net%2Fapi%2Fteams%2FCyDig%2Fsources%2FGitHub%2Fprojects%2Fnot-specified%2Frepositories%2Fcydig-compliance-action%2Fcontrols%2FscaTool%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX)
+![sastTool](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-badge-service-prod.azurewebsites.net%2Fapi%2Fteams%2FCyDig%2Fsources%2FGitHub%2Fprojects%2Fnot-specified%2Frepositories%2Fcydig-compliance-action%2Fcontrols%2FsastTool%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX)
+[![secureScore](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-badge-service-prod.azurewebsites.net%2Fapi%2Fteams%2FCyDig%2Fsources%2FGitHub%2Fprojects%2Fnot-specified%2Frepositories%2Fcydig-compliance-action%2Fcontrols%2FsecureScore%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX)](https://portal.azure.com/#view/Microsoft_Azure_Security/RecommendationsBladeV2/subscriptionIds~/%5B%22***%22%5D/source/SecurityPosture_ViewRecommendation)
+[![allowedLocationPolicy](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-badge-service-prod.azurewebsites.net%2Fapi%2Fteams%2FCyDig%2Fsources%2FGitHub%2Fprojects%2Fnot-specified%2Frepositories%2Fcydig-compliance-action%2Fcontrols%2FallowedLocationPolicy%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX)](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Compliance)
+![pentestDate](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-badge-service-prod.azurewebsites.net%2Fapi%2Fteams%2FCyDig%2Fsources%2FGitHub%2Fprojects%2Fnot-specified%2Frepositories%2Fcydig-compliance-action%2Fcontrols%2FpentestDate%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX)
+![numberOfDeployedVMs](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-badge-service-prod.azurewebsites.net%2Fapi%2Fteams%2FCyDig%2Fsources%2FGitHub%2Fprojects%2Fnot-specified%2Frepositories%2Fcydig-compliance-action%2Fcontrols%2FnumberOfDeployedVMs%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX)
+![numberOfExposedSecrets](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-badge-service-prod.azurewebsites.net%2Fapi%2Fteams%2FCyDig%2Fsources%2FGitHub%2Fprojects%2Fnot-specified%2Frepositories%2Fcydig-compliance-action%2Fcontrols%2FnumberOfExposedSecrets%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX)
+![codeQualityTool](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-badge-service-prod.azurewebsites.net%2Fapi%2Fteams%2FCyDig%2Fsources%2FGitHub%2Fprojects%2Fnot-specified%2Frepositories%2Fcydig-compliance-action%2Fcontrols%2FcodeQualityTool%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX)
+![usersInProduction](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-badge-service-prod.azurewebsites.net%2Fapi%2Fteams%2FCyDig%2Fsources%2FGitHub%2Fprojects%2Fnot-specified%2Frepositories%2Fcydig-compliance-action%2Fcontrols%2FusersInProduction%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX)
+![userAccessToCode](https://img.shields.io/endpoint?url=https%3A%2F%2Ffunc-cydig-badge-service-prod.azurewebsites.net%2Fapi%2Fteams%2FCyDig%2Fsources%2FGitHub%2Fprojects%2Fnot-specified%2Frepositories%2Fcydig-compliance-action%2Fcontrols%2FentitiesInCode%3Fcode%3DxaEvCDsaK01y2Z6SBivwOKndN4o915lpOTt1VkmULgsxgsjkml7u1DOhgULzmAPX)
# CyDig Compliance Action
@@ -19,8 +22,11 @@ This repository contains a action with compliance controls.
The compliance controls that are currently available are listed below.
* Number of reviewers on a pull request
+* Number of exposed secrets
* Date of latest threat modeling
* Date of latest penetration test
+* Implemented SCA tool
+* Implemented SAST tool
## Development on already existing or new control.
diff --git a/package-lock.json b/package-lock.json
index 8a67f075..497a5388 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,8 +11,8 @@
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1",
- "@octokit/plugin-retry": "^7.0.3",
- "@octokit/rest": "^20.0.2",
+ "@octokit/plugin-retry": "^6.0.1",
+ "@octokit/rest": "^20.1.0",
"@vercel/ncc": "^0.36.1",
"azure-devops-node-api": "^12.4.0"
},
@@ -278,82 +278,93 @@
}
},
"node_modules/@octokit/auth-token": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.0.1.tgz",
- "integrity": "sha512-RTmWsLfig8SBoiSdgvCht4BXl1CHU89Co5xiQ5JF19my/sIRDFCQ1RPrmK0exgqUZuNm39C/bV8+/83+MJEjGg==",
- "peer": true,
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
+ "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/core": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.0.1.tgz",
- "integrity": "sha512-MIpPQXu8Y8GjHwXM81JLveiV+DHJZtLMcB5nKekBGOl3iAtk0HT3i12Xl8Biybu+bCS1+k4qbuKEq5d0RxNRnQ==",
- "peer": true,
- "dependencies": {
- "@octokit/auth-token": "^5.0.0",
- "@octokit/graphql": "^8.0.0",
- "@octokit/request": "^9.0.0",
- "@octokit/request-error": "^6.0.1",
- "@octokit/types": "^12.0.0",
- "before-after-hook": "^3.0.2",
- "universal-user-agent": "^7.0.0"
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz",
+ "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==",
+ "dependencies": {
+ "@octokit/auth-token": "^4.0.0",
+ "@octokit/graphql": "^7.1.0",
+ "@octokit/request": "^8.3.1",
+ "@octokit/request-error": "^5.1.0",
+ "@octokit/types": "^13.0.0",
+ "before-after-hook": "^2.2.0",
+ "universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
- "node_modules/@octokit/core/node_modules/before-after-hook": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz",
- "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==",
- "peer": true
+ "node_modules/@octokit/core/node_modules/@octokit/openapi-types": {
+ "version": "22.2.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
+ "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg=="
},
- "node_modules/@octokit/core/node_modules/universal-user-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
- "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==",
- "peer": true
+ "node_modules/@octokit/core/node_modules/@octokit/types": {
+ "version": "13.5.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz",
+ "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==",
+ "dependencies": {
+ "@octokit/openapi-types": "^22.2.0"
+ }
},
"node_modules/@octokit/endpoint": {
- "version": "10.0.0",
- "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.0.0.tgz",
- "integrity": "sha512-emBcNDxBdC1y3+knJonS5zhUB/CG6TihubxM2U1/pG/Z1y3a4oV0Gzz3lmkCvWWQI6h3tqBAX9MgCBFp+M68Jw==",
- "peer": true,
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.5.tgz",
+ "integrity": "sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw==",
"dependencies": {
- "@octokit/types": "^12.0.0",
- "universal-user-agent": "^7.0.2"
+ "@octokit/types": "^13.1.0",
+ "universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
- "node_modules/@octokit/endpoint/node_modules/universal-user-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
- "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==",
- "peer": true
+ "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": {
+ "version": "22.2.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
+ "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg=="
+ },
+ "node_modules/@octokit/endpoint/node_modules/@octokit/types": {
+ "version": "13.5.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz",
+ "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==",
+ "dependencies": {
+ "@octokit/openapi-types": "^22.2.0"
+ }
},
"node_modules/@octokit/graphql": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.0.1.tgz",
- "integrity": "sha512-lLDb6LhC1gBj2CxEDa5Xk10+H/boonhs+3Mi6jpRyetskDKNHe6crMeKmUE2efoLofMP8ruannLlCUgpTFmVzQ==",
- "peer": true,
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz",
+ "integrity": "sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==",
"dependencies": {
- "@octokit/request": "^9.0.0",
- "@octokit/types": "^12.0.0",
- "universal-user-agent": "^7.0.0"
+ "@octokit/request": "^8.3.0",
+ "@octokit/types": "^13.0.0",
+ "universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
- "node_modules/@octokit/graphql/node_modules/universal-user-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
- "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==",
- "peer": true
+ "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": {
+ "version": "22.2.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
+ "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg=="
+ },
+ "node_modules/@octokit/graphql/node_modules/@octokit/types": {
+ "version": "13.5.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz",
+ "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==",
+ "dependencies": {
+ "@octokit/openapi-types": "^22.2.0"
+ }
},
"node_modules/@octokit/openapi-types": {
"version": "20.0.0",
@@ -384,6 +395,17 @@
"@octokit/openapi-types": "^12.11.0"
}
},
+ "node_modules/@octokit/plugin-request-log": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz",
+ "integrity": "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==",
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "@octokit/core": "5"
+ }
+ },
"node_modules/@octokit/plugin-rest-endpoint-methods": {
"version": "5.16.2",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz",
@@ -410,11 +432,11 @@
}
},
"node_modules/@octokit/plugin-retry": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.0.3.tgz",
- "integrity": "sha512-T9l5Z7XnDZ7dkyNmhJPSUq0YjbqUT/xn4yQbhcSuv4WGC/LqM73/mKwkl68VDPoLw20e8oz4L7qQopWt9v6sow==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.0.1.tgz",
+ "integrity": "sha512-SKs+Tz9oj0g4p28qkZwl/topGcb0k0qPNX/i7vBKmDsjoeqnVfFUquqrE/O9oJY7+oLzdCtkiWSXLpLjvl6uog==",
"dependencies": {
- "@octokit/request-error": "^6.0.0",
+ "@octokit/request-error": "^5.0.0",
"@octokit/types": "^12.0.0",
"bottleneck": "^2.15.3"
},
@@ -422,100 +444,71 @@
"node": ">= 18"
},
"peerDependencies": {
- "@octokit/core": ">=6"
+ "@octokit/core": ">=5"
}
},
"node_modules/@octokit/request": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.0.1.tgz",
- "integrity": "sha512-kL+cAcbSl3dctYLuJmLfx6Iku2MXXy0jszhaEIjQNaCp4zjHXrhVAHeuaRdNvJjW9qjl3u1MJ72+OuBP0YW/pg==",
- "peer": true,
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.0.tgz",
+ "integrity": "sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==",
"dependencies": {
- "@octokit/endpoint": "^10.0.0",
- "@octokit/request-error": "^6.0.1",
- "@octokit/types": "^12.0.0",
- "universal-user-agent": "^7.0.2"
+ "@octokit/endpoint": "^9.0.1",
+ "@octokit/request-error": "^5.1.0",
+ "@octokit/types": "^13.1.0",
+ "universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/request-error": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.0.2.tgz",
- "integrity": "sha512-WtRVpoHcNXs84+s9s/wqfHaxM68NGMg8Av7h59B50OVO0PwwMx+2GgQ/OliUd0iQBSNWgR6N8afi/KjSHbXHWw==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz",
+ "integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==",
"dependencies": {
- "@octokit/types": "^12.0.0"
+ "@octokit/types": "^13.1.0",
+ "deprecation": "^2.0.0",
+ "once": "^1.4.0"
},
"engines": {
"node": ">= 18"
}
},
- "node_modules/@octokit/request/node_modules/universal-user-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
- "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==",
- "peer": true
+ "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": {
+ "version": "22.2.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
+ "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg=="
},
- "node_modules/@octokit/rest": {
- "version": "20.0.2",
- "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.0.2.tgz",
- "integrity": "sha512-Ux8NDgEraQ/DMAU1PlAohyfBBXDwhnX2j33Z1nJNziqAfHi70PuxkFYIcIt8aIAxtRE7KVuKp8lSR8pA0J5iOQ==",
+ "node_modules/@octokit/request-error/node_modules/@octokit/types": {
+ "version": "13.5.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz",
+ "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==",
"dependencies": {
- "@octokit/core": "^5.0.0",
- "@octokit/plugin-paginate-rest": "^9.0.0",
- "@octokit/plugin-request-log": "^4.0.0",
- "@octokit/plugin-rest-endpoint-methods": "^10.0.0"
- },
- "engines": {
- "node": ">= 18"
+ "@octokit/openapi-types": "^22.2.0"
}
},
- "node_modules/@octokit/rest/node_modules/@octokit/auth-token": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
- "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
- "engines": {
- "node": ">= 18"
- }
+ "node_modules/@octokit/request/node_modules/@octokit/openapi-types": {
+ "version": "22.2.0",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
+ "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg=="
},
- "node_modules/@octokit/rest/node_modules/@octokit/core": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.1.0.tgz",
- "integrity": "sha512-BDa2VAMLSh3otEiaMJ/3Y36GU4qf6GI+VivQ/P41NC6GHcdxpKlqV0ikSZ5gdQsmS3ojXeRx5vasgNTinF0Q4g==",
+ "node_modules/@octokit/request/node_modules/@octokit/types": {
+ "version": "13.5.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz",
+ "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==",
"dependencies": {
- "@octokit/auth-token": "^4.0.0",
- "@octokit/graphql": "^7.0.0",
- "@octokit/request": "^8.0.2",
- "@octokit/request-error": "^5.0.0",
- "@octokit/types": "^12.0.0",
- "before-after-hook": "^2.2.0",
- "universal-user-agent": "^6.0.0"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/@octokit/rest/node_modules/@octokit/endpoint": {
- "version": "9.0.4",
- "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.4.tgz",
- "integrity": "sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==",
- "dependencies": {
- "@octokit/types": "^12.0.0",
- "universal-user-agent": "^6.0.0"
- },
- "engines": {
- "node": ">= 18"
+ "@octokit/openapi-types": "^22.2.0"
}
},
- "node_modules/@octokit/rest/node_modules/@octokit/graphql": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz",
- "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==",
+ "node_modules/@octokit/rest": {
+ "version": "20.1.0",
+ "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.0.tgz",
+ "integrity": "sha512-STVO3itHQLrp80lvcYB2UIKoeil5Ctsgd2s1AM+du3HqZIR35ZH7WE9HLwUOLXH0myA0y3AGNPo8gZtcgIbw0g==",
"dependencies": {
- "@octokit/request": "^8.0.1",
- "@octokit/types": "^12.0.0",
- "universal-user-agent": "^6.0.0"
+ "@octokit/core": "^5.0.2",
+ "@octokit/plugin-paginate-rest": "^9.1.5",
+ "@octokit/plugin-request-log": "^4.0.0",
+ "@octokit/plugin-rest-endpoint-methods": "^10.2.0"
},
"engines": {
"node": ">= 18"
@@ -535,17 +528,6 @@
"@octokit/core": "5"
}
},
- "node_modules/@octokit/rest/node_modules/@octokit/plugin-request-log": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz",
- "integrity": "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==",
- "engines": {
- "node": ">= 18"
- },
- "peerDependencies": {
- "@octokit/core": "5"
- }
- },
"node_modules/@octokit/rest/node_modules/@octokit/plugin-rest-endpoint-methods": {
"version": "10.4.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz",
@@ -560,33 +542,6 @@
"@octokit/core": "5"
}
},
- "node_modules/@octokit/rest/node_modules/@octokit/request": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.2.0.tgz",
- "integrity": "sha512-exPif6x5uwLqv1N1irkLG1zZNJkOtj8bZxuVHd71U5Ftuxf2wGNvAJyNBcPbPC+EBzwYEbBDdSFb8EPcjpYxPQ==",
- "dependencies": {
- "@octokit/endpoint": "^9.0.0",
- "@octokit/request-error": "^5.0.0",
- "@octokit/types": "^12.0.0",
- "universal-user-agent": "^6.0.0"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/@octokit/rest/node_modules/@octokit/request-error": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz",
- "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==",
- "dependencies": {
- "@octokit/types": "^12.0.0",
- "deprecation": "^2.0.0",
- "once": "^1.4.0"
- },
- "engines": {
- "node": ">= 18"
- }
- },
"node_modules/@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
@@ -1042,12 +997,12 @@
}
},
"node_modules/braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"dependencies": {
- "fill-range": "^7.0.1"
+ "fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
@@ -1611,9 +1566,9 @@
}
},
"node_modules/fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
diff --git a/package.json b/package.json
index 591d564f..1d2cf257 100644
--- a/package.json
+++ b/package.json
@@ -25,8 +25,8 @@
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1",
- "@octokit/plugin-retry": "^7.0.3",
- "@octokit/rest": "^20.0.2",
+ "@octokit/plugin-retry": "^6.0.1",
+ "@octokit/rest": "^20.1.0",
"@vercel/ncc": "^0.36.1",
"azure-devops-node-api": "^12.4.0"
},
diff --git a/src/AccessToCode/AccessToCodeService.ts b/src/AccessToCode/AccessToCodeService.ts
new file mode 100644
index 00000000..fd4c3ff5
--- /dev/null
+++ b/src/AccessToCode/AccessToCodeService.ts
@@ -0,0 +1,62 @@
+import * as core from '@actions/core';
+import { Octokit } from '@octokit/rest';
+import { GetResponseDataTypeFromEndpointMethod, OctokitResponse } from '@octokit/types';
+
+export class AccessToCodeService {
+ public static async setAccessToCodeFindings(octokit: Octokit, owner: string, repo: string): Promise {
+ try {
+ console.log('--- Access To CodeService Control ---');
+
+ type listCollaboratorsForRepoResponseDataType = GetResponseDataTypeFromEndpointMethod<
+ typeof octokit.repos.listCollaborators
+ >;
+
+ // https://www.npmjs.com/package/octokit#pagination
+ const iterator: AsyncIterableIterator> =
+ octokit.paginate.iterator(octokit.repos.listCollaborators, {
+ owner: owner,
+ repo: repo,
+ per_page: 100,
+ affiliation: 'all',
+ });
+
+ let numberOfAdmins: number = 0;
+ let numberOfWriters: number = 0;
+ let numberOfReaders: number = 0;
+
+ for await (const { data: page } of iterator) {
+ for (const user of page) {
+ if (user.permissions!.admin) {
+ numberOfAdmins++;
+ } else if (user.permissions!.maintain!) {
+ numberOfWriters++;
+ } else if (user.permissions!.push!) {
+ numberOfWriters++;
+ } else if (user.permissions!.triage!) {
+ numberOfReaders++;
+ } else if (user.permissions!.pull!) {
+ numberOfReaders++;
+ }
+ }
+ }
+ console.log('numberOfAdmins: ' + numberOfAdmins);
+ console.log('numberOfWriters: ' + numberOfWriters);
+ console.log('numberOfReaders: ' + numberOfReaders);
+ core.exportVariable('numberOfCodeAdmins', numberOfAdmins);
+ core.exportVariable('numberOfCodeWriters', numberOfWriters);
+ core.exportVariable('numberOfCodeReaders', numberOfReaders);
+ } catch (error) {
+ core.info('Failed to fetch access to code for repo');
+ if (error.status === 401 || error.status === 403) {
+ // Removes link to REST API endpoint
+ const errorMessage: string = error.message.split('-')[0].trim();
+ core.warning(errorMessage, {
+ title: 'Failed to fetch acces to code for repo',
+ });
+ } else {
+ core.info(error.message);
+ }
+ }
+ console.log();
+ }
+}
diff --git a/src/azuredevopsboard/AzureDevOpsBoardService.ts b/src/azuredevopsboard/AzureDevOpsBoardService.ts
index 907ea6f1..569e5b0c 100644
--- a/src/azuredevopsboard/AzureDevOpsBoardService.ts
+++ b/src/azuredevopsboard/AzureDevOpsBoardService.ts
@@ -9,8 +9,7 @@ export class AzureDevOpsBoardService {
public static async getStateOfAzureDevOpsBoards(cydigConfig: CyDigConfig): Promise {
if (cydigConfig.azureDevOps.boards) {
try {
- console.log('\n Running Azure DevOps Boards control');
-
+ console.log('--- Azure DevOps Boards control ---');
const azureDevOpsConnection: AzureDevOpsConnection = new AzureDevOpsConnection(
cydigConfig.azureDevOps.boards.organizationName,
core.getInput('accessTokenAzureDevOps')
@@ -45,8 +44,11 @@ export class AzureDevOpsBoardService {
}
} catch (error) {
core.warning('Error getting tickets for Azure DevOps Board!');
- console.log('Error:', error.message);
+ console.log(
+ 'There is probably somethine wrong with your token, check that it has not expired or been revoked. Please check that you have the correct permissions (Work items: Read)'
+ );
}
+ console.log();
}
}
}
diff --git a/src/branchprotection/BranchProtectionService.ts b/src/branchprotection/BranchProtectionService.ts
index 5d904d45..4d143855 100644
--- a/src/branchprotection/BranchProtectionService.ts
+++ b/src/branchprotection/BranchProtectionService.ts
@@ -1,17 +1,13 @@
import * as core from '@actions/core';
-import * as github from '@actions/github';
-import { Endpoints } from '@octokit/types';
-import { GitHub } from '@actions/github/lib/utils';
+import { Octokit } from '@octokit/rest';
+import { BranchProtectionForRepoResponseDataType } from '../types/OctokitResponses';
+
export class BranchProtectionService {
- public static async getStateOfBranchProtection(): Promise {
+ public static async getStateOfBranchProtection(octokit: Octokit, owner: string, repo: string): Promise {
try {
- console.log('\n Running branch protection control');
- const { owner, repo }: { owner: string; repo: string } = github.context.repo;
- const token: string = core.getInput('PAT-token');
+ console.log('--- Branch protection control ---');
- const octokit: InstanceType = github.getOctokit(token);
- type branchProtectionRepsponse = Endpoints['GET /repos/{owner}/{repo}/branches/{branch}/protection']['response'];
- const response: branchProtectionRepsponse = await octokit.rest.repos.getBranchProtection({
+ const response: BranchProtectionForRepoResponseDataType = await octokit.rest.repos.getBranchProtection({
owner,
repo,
branch: 'main',
@@ -33,11 +29,41 @@ export class BranchProtectionService {
core.exportVariable('numberOfReviewers', numberOfReviewers);
} catch (error) {
- core.warning('Error getting branch protection!');
- console.log('Error:', error.message);
- if (error.status === 403) {
- core.exportVariable('numberOfReviewers', 0);
+ const errorMessage: string = error.message.split('-')[0].trim();
+ if (error.status === 401) {
+ core.info('Failed to get branch protection');
+ // Removes link to REST API endpoint
+ core.warning(errorMessage, {
+ title: 'Branch protection control failed',
+ });
+ } else if (error.status === 404) {
+ if (errorMessage === 'Branch not protected') {
+ core.notice(errorMessage, {
+ title: 'Branch protection control',
+ });
+ core.exportVariable('numberOfReviewers', 0);
+ } else {
+ core.info('Failed to get branch protection');
+ core.warning('Credentials probably lack necessary permissions', {
+ title: 'Branch protection control failed',
+ });
+ }
+ } else {
+ switch (errorMessage) {
+ case 'Upgrade to GitHub Pro or make this repository public to enable this feature.':
+ console.log('Branch protection is not enabled for repository:', repo);
+ core.exportVariable('numberOfReviewers', 0);
+ break;
+
+ default:
+ core.info('Failed to get branch protection');
+ core.notice(errorMessage, {
+ title: 'Branch protection control failed',
+ });
+ break;
+ }
}
}
+ console.log();
}
}
diff --git a/src/codequalitytools/CodeQualityService.ts b/src/codequalitytools/CodeQualityService.ts
index 348846f2..b4f9f386 100644
--- a/src/codequalitytools/CodeQualityService.ts
+++ b/src/codequalitytools/CodeQualityService.ts
@@ -2,17 +2,18 @@ import * as core from '@actions/core';
export class CodeQualityService {
public static async getStateOfCodeQualityTool(codeQualityTool: { nameOfTool: string }): Promise {
- console.log('\n Running Code Quality control');
+ console.log('--- Code Quality control ---');
if (process.env.codeQualityTool) {
- console.log(`Code Quality Tool: ${process.env.codeQualityTool}`);
+ console.log(`Tool:`, `${process.env.codeQualityTool}`);
core.exportVariable('codeQualityTool', process.env.codeQualityTool);
} else {
if (!codeQualityTool.nameOfTool || codeQualityTool.nameOfTool === 'name-of-tool') {
core.warning('Code Quality Tool is not set!');
return;
}
- console.log(`Code Quality Tool: ${codeQualityTool.nameOfTool}`);
+ console.log(`Tool:`, `${codeQualityTool.nameOfTool}`);
core.exportVariable('codeQualityTool', codeQualityTool.nameOfTool);
}
+ console.log();
}
}
diff --git a/src/index.ts b/src/index.ts
index 563d37f9..eeca892a 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,35 +1,52 @@
-import * as core from '@actions/core';
+import { getInput, setFailed } from '@actions/core';
+import { context } from '@actions/github';
+import { retry } from '@octokit/plugin-retry';
+import { Octokit } from '@octokit/rest';
+import { AzureDevOpsBoardService } from './azuredevopsboard/AzureDevOpsBoardService';
import { BranchProtectionService } from './branchprotection/BranchProtectionService';
-import { CyDigConfig } from './types/CyDigConfig';
+import { CodeQualityService } from './codequalitytools/CodeQualityService';
import { getContentOfFile } from './helpfunctions/JsonService';
import { PentestService } from './pentest/PentestService';
-import { ThreatModelingService } from './threatmodeling/ThreatModelingService';
-import { AzureDevOpsBoardService } from './azuredevopsboard/AzureDevOpsBoardService';
-import { CodeQualityService } from './codequalitytools/CodeQualityService';
import { SastService } from './sasttools/SastService';
import { ScaService } from './scatools/ScaService';
+import { SecretScanningService } from './secretscanning/SecretScanningService';
+import { AccessToCodeService } from './AccessToCode/AccessToCodeService';
+import { ThreatModelingService } from './threatmodeling/ThreatModelingService';
+import { CyDigConfig } from './types/CyDigConfig';
+
/**
* The main function for the action.
* @returns {Promise} Resolves when the action is complete.
*/
export async function run(): Promise {
try {
- console.log('\n Running controls on your repository');
- const cydigConfig: CyDigConfig = getContentOfFile(core.getInput('cydigConfigPath'));
+ console.log('Running compliance controls \n');
+ const cydigConfig: CyDigConfig = getContentOfFile(getInput('cydigConfigPath'));
+ const { owner, repo }: { owner: string; repo: string } = context.repo;
+ const token: string = getInput('PAT-token');
+ // eslint-disable-next-line
+ const OctokitRetry = Octokit.plugin(retry);
+ const octokit: Octokit = new OctokitRetry({
+ auth: token,
+ });
await CodeQualityService.getStateOfCodeQualityTool(cydigConfig.codeQualityTool);
- await SastService.getStateOfSastTool(cydigConfig.sastTool);
- await ScaService.getStateOfScaTool(cydigConfig.scaTool);
-
- await BranchProtectionService.getStateOfBranchProtection();
-
+ await SastService.getStateOfSastTool(cydigConfig.sastTool.nameOfTool, octokit, owner, repo);
+ await ScaService.getStateOfScaTool(cydigConfig.scaTool.nameOfTool, octokit, owner, repo);
+ await SecretScanningService.getStateOfExposedSecrets(
+ cydigConfig.secretScanningTool?.nameOfTool,
+ octokit,
+ owner,
+ repo
+ );
+ await BranchProtectionService.getStateOfBranchProtection(octokit, owner, repo);
+ await AccessToCodeService.setAccessToCodeFindings(octokit, owner, repo);
await PentestService.getStateOfPentest(cydigConfig.pentest);
await ThreatModelingService.getStateOfThreatModeling(cydigConfig.threatModeling);
-
await AzureDevOpsBoardService.getStateOfAzureDevOpsBoards(cydigConfig);
} catch (error) {
// Fail the workflow run if an error occurs
- if (error instanceof Error) core.setFailed(error.message);
+ if (error instanceof Error) setFailed(error.message);
}
}
diff --git a/src/pentest/PentestService.ts b/src/pentest/PentestService.ts
index 7688a388..64c2e7db 100644
--- a/src/pentest/PentestService.ts
+++ b/src/pentest/PentestService.ts
@@ -2,14 +2,18 @@ import * as core from '@actions/core';
export class PentestService {
public static async getStateOfPentest(pentest: { date: string; boardsTag?: string }): Promise {
+ console.log('--- Pentest control ---');
if (process.env.pentestDate) {
+ console.log('Pentest date was found');
core.exportVariable('pentestDate', process.env.pentestDate);
} else {
if (!pentest.date || pentest.date === 'date-of-pentest') {
core.warning('Pentest Date is not set!');
return;
}
+ console.log('Pentest date was found');
core.exportVariable('pentestDate', pentest.date);
}
+ console.log();
}
}
diff --git a/src/sasttools/CodeQLService.ts b/src/sasttools/CodeQLService.ts
new file mode 100644
index 00000000..ec11ba70
--- /dev/null
+++ b/src/sasttools/CodeQLService.ts
@@ -0,0 +1,77 @@
+import * as core from '@actions/core';
+import { Octokit } from '@octokit/rest';
+import GitHub_Tool_Severity_Level from '../types/GithubToolSeverityLevel';
+import { CodeScanningAlertsForRepoResponseDataType } from '../types/OctokitResponses';
+
+export class CodeQLService {
+ public static async setCodeQLFindings(
+ nameOfTool: string,
+ octokit: Octokit,
+ owner: string,
+ repo: string
+ ): Promise {
+ try {
+ // https://www.npmjs.com/package/octokit#pagination
+ const iterator: AsyncIterableIterator = octokit.paginate.iterator(
+ octokit.codeScanning.listAlertsForRepo,
+ {
+ owner: owner,
+ repo: repo,
+ per_page: 100,
+ state: 'open',
+ tool_name: 'CodeQL',
+ }
+ );
+
+ let sastNumberOfSeverity1: number = 0;
+ let sastNumberOfSeverity2: number = 0;
+ let sastNumberOfSeverity3: number = 0;
+ let sastNumberOfSeverity4: number = 0;
+
+ for await (const { data: alerts } of iterator) {
+ for (const alert of alerts) {
+ switch (alert.rule.security_severity_level) {
+ case GitHub_Tool_Severity_Level.LOW:
+ sastNumberOfSeverity1++;
+ break;
+ case GitHub_Tool_Severity_Level.MEDIUM:
+ sastNumberOfSeverity2++;
+ break;
+ case GitHub_Tool_Severity_Level.HIGH:
+ sastNumberOfSeverity3++;
+ break;
+ case GitHub_Tool_Severity_Level.CRITICAL:
+ sastNumberOfSeverity4++;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ console.log('Low: ' + sastNumberOfSeverity1);
+ console.log('Medium: ' + sastNumberOfSeverity2);
+ console.log('High: ' + sastNumberOfSeverity3);
+ console.log('Critical: ' + sastNumberOfSeverity4);
+
+ core.exportVariable('sastTool', nameOfTool);
+ core.exportVariable('SASTnumberOfSeverity1', sastNumberOfSeverity1);
+ core.exportVariable('SASTnumberOfSeverity2', sastNumberOfSeverity2);
+ core.exportVariable('SASTnumberOfSeverity3', sastNumberOfSeverity3);
+ core.exportVariable('SASTnumberOfSeverity4', sastNumberOfSeverity4);
+ } catch (error) {
+ core.info('Failed to get CodeQL severities');
+ if (error.status === 401 || error.status === 403 || error.status === 404) {
+ // Removes link to REST API endpoint
+ const errorMessage: string = error.message.split('-')[0].trim();
+ core.warning(errorMessage, {
+ title: 'SAST tool control failed',
+ });
+ } else {
+ core.notice(error.message, {
+ title: 'SAST tool control failed',
+ });
+ }
+ }
+ }
+}
diff --git a/src/sasttools/SastService.ts b/src/sasttools/SastService.ts
index a17c4c7f..b92ac464 100644
--- a/src/sasttools/SastService.ts
+++ b/src/sasttools/SastService.ts
@@ -1,18 +1,33 @@
import * as core from '@actions/core';
+import { Octokit } from '@octokit/rest';
+import { CodeQLService } from './CodeQLService';
+import GitHub_Tools from '../types/GitHubTools';
export class SastService {
- public static async getStateOfSastTool(sastTool: { nameOfTool: string }): Promise {
- console.log('\n Running SAST control');
+ public static async getStateOfSastTool(
+ nameOfTool: string,
+ octokit: Octokit,
+ owner: string,
+ repo: string
+ ): Promise {
+ console.log('--- SAST control ---');
+ let sast: string = nameOfTool;
if (process.env.sastTool) {
- console.log(`SAST Tool: ${process.env.sastTool}`);
- core.exportVariable('sastTool', process.env.sastTool);
- } else {
- if (!sastTool.nameOfTool || sastTool.nameOfTool === 'name-of-tool') {
- core.warning('SAST Tool is not set!');
- return;
- }
- console.log(`SAST Tool: ${sastTool.nameOfTool}`);
- core.exportVariable('sastTool', sastTool.nameOfTool);
+ sast = process.env.sastTool;
}
+ if (!sast || sast === '' || sast === 'name-of-tool') {
+ core.warning('SAST Tool is not set!');
+ return;
+ }
+ console.log(`Tool:`, `${sast}`);
+ switch (sast.toLowerCase()) {
+ case GitHub_Tools.CODEQL.toLowerCase():
+ await CodeQLService.setCodeQLFindings(sast, octokit, owner, repo);
+ break;
+ default:
+ core.exportVariable('sastTool', sast);
+ break;
+ }
+ console.log();
}
}
diff --git a/src/scatools/DependabotService.ts b/src/scatools/DependabotService.ts
index a6886d1c..80a4f6b4 100644
--- a/src/scatools/DependabotService.ts
+++ b/src/scatools/DependabotService.ts
@@ -1,20 +1,18 @@
import * as core from '@actions/core';
-import * as github from '@actions/github';
import { Octokit } from '@octokit/rest';
-import { OctokitResponse } from '@octokit/types';
+import GitHub_Tool_Severity_Level from '../types/GithubToolSeverityLevel';
+import { DependabotAlertsForRepoResponseDataType } from '../types/OctokitResponses';
export class DependabotService {
- public static async setDependabotFindings(): Promise {
+ public static async setDependabotFindings(
+ nameOfTool: string,
+ octokit: Octokit,
+ owner: string,
+ repo: string
+ ): Promise {
try {
- const { owner, repo }: { owner: string; repo: string } = github.context.repo;
- const token: string = core.getInput('PAT-token');
-
- const octokit: Octokit = new Octokit({
- auth: token,
- });
-
// https://www.npmjs.com/package/octokit#pagination
- const iterator: AsyncIterableIterator> = octokit.paginate.iterator(
+ const iterator: AsyncIterableIterator = octokit.paginate.iterator(
octokit.dependabot.listAlertsForRepo,
{
owner: owner,
@@ -32,37 +30,45 @@ export class DependabotService {
for await (const { data: alerts } of iterator) {
for (const alert of alerts) {
switch (alert.security_vulnerability.severity) {
- case 'low':
+ case GitHub_Tool_Severity_Level.LOW:
scaNumberOfSeverity1++;
break;
- case 'medium':
+ case GitHub_Tool_Severity_Level.MEDIUM:
scaNumberOfSeverity2++;
break;
- case 'high':
+ case GitHub_Tool_Severity_Level.HIGH:
scaNumberOfSeverity3++;
break;
- case 'critical':
+ case GitHub_Tool_Severity_Level.CRITICAL:
scaNumberOfSeverity4++;
break;
}
}
}
- console.log('SCAnumberOfSeverityLow: ' + scaNumberOfSeverity1);
- console.log('SCAnumberOfSeverityMedium: ' + scaNumberOfSeverity2);
- console.log('SCAnumberOfSeverityHigh: ' + scaNumberOfSeverity3);
- console.log('SCAnumberOfSeverityCritical: ' + scaNumberOfSeverity4);
+ console.log('Low: ' + scaNumberOfSeverity1);
+ console.log('Medium: ' + scaNumberOfSeverity2);
+ console.log('High: ' + scaNumberOfSeverity3);
+ console.log('Critical: ' + scaNumberOfSeverity4);
+ core.exportVariable('scaTool', nameOfTool);
core.exportVariable('SCAnumberOfSeverity1', scaNumberOfSeverity1);
core.exportVariable('SCAnumberOfSeverity2', scaNumberOfSeverity2);
core.exportVariable('SCAnumberOfSeverity3', scaNumberOfSeverity3);
core.exportVariable('SCAnumberOfSeverity4', scaNumberOfSeverity4);
} catch (error) {
- core.warning('Could not set Dependabot severities');
- core.exportVariable('SCAnumberOfSeverity1', 0);
- core.exportVariable('SCAnumberOfSeverity2', 0);
- core.exportVariable('SCAnumberOfSeverity3', 0);
- core.exportVariable('SCAnumberOfSeverity4', 0);
+ core.info('Failed to get Dependabot severities');
+ if (error.status === 401 || error.status === 403 || error.status === 404) {
+ // Removes link to REST API endpoint
+ const errorMessage: string = error.message.split('-')[0].trim();
+ core.warning(errorMessage, {
+ title: 'SCA tool control failed',
+ });
+ } else {
+ core.notice(error.message, {
+ title: 'SCA tool control failed',
+ });
+ }
}
}
}
diff --git a/src/scatools/ScaService.ts b/src/scatools/ScaService.ts
index 3334cefb..f83a58b5 100644
--- a/src/scatools/ScaService.ts
+++ b/src/scatools/ScaService.ts
@@ -1,23 +1,33 @@
import * as core from '@actions/core';
+import { Octokit } from '@octokit/rest';
import { DependabotService } from './DependabotService';
+import GitHub_Tools from '../types/GitHubTools';
export class ScaService {
- public static async getStateOfScaTool(scaTool: { nameOfTool: string }): Promise {
- console.log('\n Running SCA control');
- let sca: string = scaTool.nameOfTool;
+ public static async getStateOfScaTool(
+ nameOfTool: string,
+ octokit: Octokit,
+ owner: string,
+ repo: string
+ ): Promise {
+ console.log('--- SCA control ---');
+ let sca: string = nameOfTool;
if (process.env.scaTool) {
sca = process.env.scaTool;
}
- console.log(`SCA Tool: ${sca}`);
- core.exportVariable('scaTool', sca);
-
if (!sca || sca === '' || sca === 'name-of-tool') {
core.warning('SCA Tool is not set!');
return;
}
-
- if (sca.toLowerCase() === 'dependabot') {
- DependabotService.setDependabotFindings();
+ console.log(`Tool:`, `${sca}`);
+ switch (sca.toLowerCase()) {
+ case GitHub_Tools.DEPENDABOT.toLowerCase():
+ await DependabotService.setDependabotFindings(sca, octokit, owner, repo);
+ break;
+ default:
+ core.exportVariable('scaTool', sca);
+ break;
}
+ console.log();
}
}
diff --git a/src/secretscanning/GithubSecretScanningService.ts b/src/secretscanning/GithubSecretScanningService.ts
new file mode 100644
index 00000000..cb75be84
--- /dev/null
+++ b/src/secretscanning/GithubSecretScanningService.ts
@@ -0,0 +1,62 @@
+import * as core from '@actions/core';
+import { Octokit } from '@octokit/rest';
+import { SecretAlertsForRepoResponseDataType } from '../types/OctokitResponses';
+import GitHub_Tools from '../types/GitHubTools';
+
+export class GithubSecretScanningService {
+ public static async getStateOfExposedSecrets(octokit: Octokit, owner: string, repo: string): Promise {
+ try {
+ console.log('Tool: Github Secret Scanning');
+
+ // https://www.npmjs.com/package/octokit#pagination
+ const iterator: AsyncIterableIterator = octokit.paginate.iterator(
+ octokit.secretScanning.listAlertsForRepo,
+ {
+ owner: owner,
+ repo: repo,
+ per_page: 100,
+ state: 'open',
+ }
+ );
+
+ let numberOfExposedSecrets: number = 0;
+
+ for await (const { data: alerts } of iterator) {
+ numberOfExposedSecrets += alerts.length;
+ }
+
+ console.log('Exposed secrets:', numberOfExposedSecrets);
+ core.exportVariable('secretScanningTool', GitHub_Tools.GitHub_SECRET_SCANNING);
+ core.exportVariable('numberOfExposedSecrets', numberOfExposedSecrets);
+ } catch (error) {
+ core.info('Failed to get number of exposed secrets');
+ // Removes link to REST API endpoint
+ const errorMessage: string = error.message.split('-')[0].trim();
+ if (error.status === 401) {
+ core.warning(errorMessage, {
+ title: 'Number of exposed secrets control failed',
+ });
+ } else if (error.status === 404) {
+ switch (errorMessage) {
+ case 'Secret scanning is disabled on this repository.':
+ core.warning(errorMessage, {
+ title: 'Number of exposed secrets control failed',
+ });
+ break;
+
+ default:
+ console.log(error);
+ core.warning('Credentials probably lack necessary permissions', {
+ title: 'Number of exposed secrets control failed',
+ });
+ break;
+ }
+ } else {
+ core.notice(error.message, {
+ title: 'Number of exposed secrets control failed',
+ });
+ }
+ }
+ console.log();
+ }
+}
diff --git a/src/secretscanning/SecretScanningService.ts b/src/secretscanning/SecretScanningService.ts
new file mode 100644
index 00000000..415025f9
--- /dev/null
+++ b/src/secretscanning/SecretScanningService.ts
@@ -0,0 +1,35 @@
+import * as core from '@actions/core';
+import { Octokit } from '@octokit/rest';
+import GitHub_Tools from '../types/GitHubTools';
+import { GithubSecretScanningService } from './GithubSecretScanningService';
+
+export class SecretScanningService {
+ public static async getStateOfExposedSecrets(
+ nameOfTool: string,
+ octokit: Octokit,
+ owner: string,
+ repo: string
+ ): Promise {
+ console.log('--- Secret Scanning control ---');
+
+ if (nameOfTool === null || nameOfTool === undefined || nameOfTool === 'name-of-tool') {
+ core.warning('Secret Scanning Tool is not set! Will continue with GitHub Secret Scanning tool:');
+ await GithubSecretScanningService.getStateOfExposedSecrets(octokit, owner, repo);
+ return;
+ }
+
+ switch (nameOfTool.toLowerCase()) {
+ case GitHub_Tools.GitHub_SECRET_SCANNING.toLowerCase():
+ await GithubSecretScanningService.getStateOfExposedSecrets(octokit, owner, repo);
+ break;
+ default:
+ core.notice('Given secret scanning tool is not implemented: ' + nameOfTool, {
+ title: 'Number of exposed secrets control failed',
+ });
+ core.exportVariable('secretScanningTool', nameOfTool);
+ break;
+ }
+
+ console.log();
+ }
+}
diff --git a/src/threatmodeling/ThreatModelingService.ts b/src/threatmodeling/ThreatModelingService.ts
index 5278c85c..9f0308f2 100644
--- a/src/threatmodeling/ThreatModelingService.ts
+++ b/src/threatmodeling/ThreatModelingService.ts
@@ -1,14 +1,18 @@
import * as core from '@actions/core';
export class ThreatModelingService {
public static async getStateOfThreatModeling(threatModeling: { date: string; boardsTag?: string }): Promise {
+ console.log('--- Threat Modeling control ---');
if (process.env.threatModelingDate) {
+ console.log('Threat Modeling date was found');
core.exportVariable('threatModelingDate', process.env.threatModelingDate);
} else {
if (!threatModeling.date || threatModeling.date === 'date-of-threat-modeling') {
core.warning('Threat Modeling Date is not set!');
return;
}
+ console.log('Threat Modeling date was found');
core.exportVariable('threatModelingDate', threatModeling.date);
}
+ console.log();
}
}
diff --git a/src/types/CyDigConfig.ts b/src/types/CyDigConfig.ts
index 6ef8d191..4c41d8ac 100644
--- a/src/types/CyDigConfig.ts
+++ b/src/types/CyDigConfig.ts
@@ -5,6 +5,9 @@ export type CyDigConfig = {
date: string;
boardsTag: string;
};
+ secretScanningTool: {
+ nameOfTool: string;
+ };
pentest: {
date: string;
boardsTag: string;
diff --git a/src/types/GitHubTools.ts b/src/types/GitHubTools.ts
new file mode 100644
index 00000000..2d745a98
--- /dev/null
+++ b/src/types/GitHubTools.ts
@@ -0,0 +1,7 @@
+enum GitHub_Tools {
+ DEPENDABOT = 'Dependabot',
+ CODEQL = 'CodeQL',
+ GitHub_SECRET_SCANNING = 'GitHub',
+}
+
+export default GitHub_Tools;
diff --git a/src/types/GithubToolSeverityLevel.ts b/src/types/GithubToolSeverityLevel.ts
new file mode 100644
index 00000000..dc0202e7
--- /dev/null
+++ b/src/types/GithubToolSeverityLevel.ts
@@ -0,0 +1,8 @@
+enum GitHub_Tool_Severity_Level {
+ LOW = 'low',
+ MEDIUM = 'medium',
+ HIGH = 'high',
+ CRITICAL = 'critical',
+}
+
+export default GitHub_Tool_Severity_Level;
diff --git a/src/types/OctokitResponses.ts b/src/types/OctokitResponses.ts
new file mode 100644
index 00000000..e3a66377
--- /dev/null
+++ b/src/types/OctokitResponses.ts
@@ -0,0 +1,13 @@
+import { Endpoints } from '@octokit/types';
+
+export type DependabotAlertsForRepoResponseDataType =
+ Endpoints['GET /repos/{owner}/{repo}/dependabot/alerts']['response'];
+
+export type CodeScanningAlertsForRepoResponseDataType =
+ Endpoints['GET /repos/{owner}/{repo}/code-scanning/alerts']['response'];
+
+export type SecretAlertsForRepoResponseDataType =
+ Endpoints['GET /repos/{owner}/{repo}/secret-scanning/alerts']['response'];
+
+export type BranchProtectionForRepoResponseDataType =
+ Endpoints['GET /repos/{owner}/{repo}/branches/{branch}/protection']['response'];
diff --git a/tests/BranchProtectionService.test.ts b/tests/BranchProtectionService.test.ts
new file mode 100644
index 00000000..7cafb8ab
--- /dev/null
+++ b/tests/BranchProtectionService.test.ts
@@ -0,0 +1,104 @@
+import * as core from '@actions/core';
+import sinon, { SinonStub } from 'sinon';
+import { BranchProtectionService } from '../src/branchprotection/BranchProtectionService';
+
+describe('BranchProtectionService', function () {
+ let warningStub: SinonStub;
+ let noticeStub: SinonStub;
+ let infoStub: SinonStub;
+ let exportVariableStub: SinonStub;
+ let logStub: SinonStub;
+ let getBranchProtectionStub: SinonStub;
+ const octokitMock: any = {
+ rest: {
+ repos: {
+ getBranchProtection() {
+ return;
+ },
+ },
+ },
+ };
+
+ beforeEach(function () {
+ warningStub = sinon.stub(core, 'warning');
+ noticeStub = sinon.stub(core, 'notice');
+ infoStub = sinon.stub(core, 'info');
+ exportVariableStub = sinon.stub(core, 'exportVariable');
+ logStub = sinon.stub(console, 'log');
+ getBranchProtectionStub = sinon.stub(octokitMock.rest.repos, 'getBranchProtection');
+ });
+
+ afterEach(function () {
+ sinon.restore();
+ });
+
+ it('should handle successful branch protection retrieval', async function () {
+ getBranchProtectionStub.returns({
+ data: {
+ enforce_admins: { enabled: true },
+ required_pull_request_reviews: { required_approving_review_count: 1 },
+ },
+ });
+
+ await BranchProtectionService.getStateOfBranchProtection(octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnceWithExactly(exportVariableStub, 'numberOfReviewers', 1);
+ sinon.assert.notCalled(warningStub);
+ });
+
+ it('should warn when admins can bypass branch protection rules', async function () {
+ getBranchProtectionStub.returns({
+ data: {
+ enforce_admins: { enabled: false },
+ required_pull_request_reviews: { required_approving_review_count: 1 },
+ },
+ });
+
+ await BranchProtectionService.getStateOfBranchProtection(octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnce(warningStub);
+ sinon.assert.calledOnceWithExactly(exportVariableStub, 'numberOfReviewers', 0);
+ });
+
+ it('should handle a 401 error', async function () {
+ getBranchProtectionStub.rejects({
+ status: 401,
+ message: '401 error message',
+ });
+
+ await BranchProtectionService.getStateOfBranchProtection(octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnce(warningStub);
+ sinon.assert.notCalled(exportVariableStub);
+ });
+
+ it('should handle a 404 error (caused by branch protection not enabled)', async function () {
+ getBranchProtectionStub.rejects({
+ status: 404,
+ message: 'Branch not protected',
+ });
+
+ await BranchProtectionService.getStateOfBranchProtection(octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnce(noticeStub);
+ sinon.assert.calledOnceWithExactly(exportVariableStub, 'numberOfReviewers', 0);
+ });
+
+ it('should handle a normal 404 error', async function () {
+ getBranchProtectionStub.rejects({
+ status: 404,
+ message: 'Regular 404 error message',
+ });
+
+ await BranchProtectionService.getStateOfBranchProtection(octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnce(warningStub);
+ sinon.assert.notCalled(exportVariableStub);
+ });
+
+ it('should call warning when branch protection is enabled but receives 404 (status = 404)', async function () {
+ getBranchProtectionStub.rejects({
+ status: 500,
+ message: 'Default error case',
+ });
+
+ await BranchProtectionService.getStateOfBranchProtection(octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnce(noticeStub);
+ sinon.assert.notCalled(exportVariableStub);
+ });
+});
diff --git a/tests/CodeQLService.test.ts b/tests/CodeQLService.test.ts
new file mode 100644
index 00000000..318dda3b
--- /dev/null
+++ b/tests/CodeQLService.test.ts
@@ -0,0 +1,115 @@
+import * as core from '@actions/core';
+import sinon, { SinonStub } from 'sinon';
+import { CodeQLService } from '../src/sasttools/CodeQLService';
+import GitHub_Tools from '../src/types/GitHubTools';
+
+describe('CodeQLService', function () {
+ let warningStub: SinonStub;
+ let noticeStub: SinonStub;
+ let infoStub: SinonStub;
+ let exportVariableStub: SinonStub;
+ let logStub: SinonStub;
+ let iteratorStub: SinonStub;
+ const octokitMock: any = {
+ paginate: {
+ iterator() {
+ return;
+ },
+ },
+ codeScanning: {
+ listAlertsForRepo: '',
+ },
+ };
+
+ beforeEach(() => {
+ warningStub = sinon.stub(core, 'warning');
+ noticeStub = sinon.stub(core, 'notice');
+ infoStub = sinon.stub(core, 'info');
+ exportVariableStub = sinon.stub(core, 'exportVariable');
+ logStub = sinon.stub(console, 'log');
+ iteratorStub = sinon.stub(octokitMock.paginate, 'iterator');
+ });
+
+ afterEach(() => {
+ sinon.restore();
+ });
+
+ it('should handle successful code scanning retrieval', async function () {
+ iteratorStub.returns([
+ {
+ data: [
+ {
+ rule: {
+ security_severity_level: 'low',
+ },
+ },
+ {
+ rule: {
+ security_severity_level: 'medium',
+ },
+ },
+ {
+ rule: {
+ security_severity_level: 'high',
+ },
+ },
+ {
+ rule: {
+ security_severity_level: 'critical',
+ },
+ },
+ ],
+ },
+ ]);
+
+ await CodeQLService.setCodeQLFindings(GitHub_Tools.CODEQL, octokitMock, 'owner', 'repo');
+ sinon.assert.callCount(exportVariableStub, 5);
+ sinon.assert.calledWithExactly(exportVariableStub, 'sastTool', GitHub_Tools.CODEQL);
+ sinon.assert.calledWithExactly(exportVariableStub, 'SASTnumberOfSeverity1', 1);
+ sinon.assert.calledWithExactly(exportVariableStub, 'SASTnumberOfSeverity2', 1);
+ sinon.assert.calledWithExactly(exportVariableStub, 'SASTnumberOfSeverity3', 1);
+ sinon.assert.calledWithExactly(exportVariableStub, 'SASTnumberOfSeverity4', 1);
+ sinon.assert.notCalled(warningStub);
+ });
+
+ it('should handle a 401 error', async function () {
+ iteratorStub.throws({
+ status: 401,
+ message: '401 error message',
+ });
+
+ await CodeQLService.setCodeQLFindings(GitHub_Tools.CODEQL, octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnce(warningStub);
+ });
+
+ it('should handle a 403 error', async function () {
+ iteratorStub.throws({
+ status: 403,
+ message: '403 error message',
+ });
+
+ await CodeQLService.setCodeQLFindings(GitHub_Tools.CODEQL, octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnce(warningStub);
+ });
+
+ it('should handle a 404 error', async function () {
+ iteratorStub.throws({
+ status: 404,
+ message: '404 error message',
+ });
+
+ await CodeQLService.setCodeQLFindings(GitHub_Tools.CODEQL, octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnce(warningStub);
+ });
+
+ it('should handle error other than 401, 403, 404', async function () {
+ iteratorStub.throws({
+ status: 500,
+ message: 'Default error case',
+ });
+
+ await CodeQLService.setCodeQLFindings(GitHub_Tools.CODEQL, octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnce(noticeStub);
+ sinon.assert.notCalled(warningStub);
+ });
+});
diff --git a/tests/DependabotService.test.ts b/tests/DependabotService.test.ts
new file mode 100644
index 00000000..af9ab84a
--- /dev/null
+++ b/tests/DependabotService.test.ts
@@ -0,0 +1,115 @@
+import * as core from '@actions/core';
+import sinon, { SinonStub } from 'sinon';
+import { DependabotService } from '../src/scatools/DependabotService';
+import GitHub_Tools from '../src/types/GitHubTools';
+
+describe('CodeQLService', function () {
+ let warningStub: SinonStub;
+ let noticeStub: SinonStub;
+ let infoStub: SinonStub;
+ let exportVariableStub: SinonStub;
+ let logStub: SinonStub;
+ let iteratorStub: SinonStub;
+ const octokitMock: any = {
+ paginate: {
+ iterator() {
+ return;
+ },
+ },
+ dependabot: {
+ listAlertsForRepo: '',
+ },
+ };
+
+ beforeEach(function () {
+ warningStub = sinon.stub(core, 'warning');
+ noticeStub = sinon.stub(core, 'notice');
+ infoStub = sinon.stub(core, 'info');
+ exportVariableStub = sinon.stub(core, 'exportVariable');
+ logStub = sinon.stub(console, 'log');
+ iteratorStub = sinon.stub(octokitMock.paginate, 'iterator');
+ });
+
+ afterEach(function () {
+ sinon.restore();
+ });
+
+ it('should handle successful Dependabot alerts retrieval', async function () {
+ iteratorStub.returns([
+ {
+ data: [
+ {
+ security_vulnerability: {
+ severity: 'low',
+ },
+ },
+ {
+ security_vulnerability: {
+ severity: 'medium',
+ },
+ },
+ {
+ security_vulnerability: {
+ severity: 'high',
+ },
+ },
+ {
+ security_vulnerability: {
+ severity: 'critical',
+ },
+ },
+ ],
+ },
+ ]);
+
+ await DependabotService.setDependabotFindings(GitHub_Tools.DEPENDABOT, octokitMock, 'owner', 'repo');
+ sinon.assert.callCount(exportVariableStub, 5);
+ sinon.assert.calledWithExactly(exportVariableStub, 'scaTool', GitHub_Tools.DEPENDABOT);
+ sinon.assert.calledWithExactly(exportVariableStub, 'SCAnumberOfSeverity1', 1);
+ sinon.assert.calledWithExactly(exportVariableStub, 'SCAnumberOfSeverity1', 1);
+ sinon.assert.calledWithExactly(exportVariableStub, 'SCAnumberOfSeverity1', 1);
+ sinon.assert.calledWithExactly(exportVariableStub, 'SCAnumberOfSeverity1', 1);
+ sinon.assert.notCalled(warningStub);
+ });
+
+ it('should handle a 401 error', async function () {
+ iteratorStub.throws({
+ status: 401,
+ message: '401 error message',
+ });
+
+ await DependabotService.setDependabotFindings(GitHub_Tools.DEPENDABOT, octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnce(warningStub);
+ });
+
+ it('should handle a 403 error', async function () {
+ iteratorStub.throws({
+ status: 403,
+ message: '403 error message',
+ });
+
+ await DependabotService.setDependabotFindings(GitHub_Tools.DEPENDABOT, octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnce(warningStub);
+ });
+
+ it('should handle a 404 error', async function () {
+ iteratorStub.throws({
+ status: 404,
+ message: '404 error message',
+ });
+
+ await DependabotService.setDependabotFindings(GitHub_Tools.DEPENDABOT, octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnce(warningStub);
+ });
+
+ it('should handle error other than 401, 403, 404', async function () {
+ iteratorStub.throws({
+ status: 500,
+ message: 'Default error case',
+ });
+
+ await DependabotService.setDependabotFindings(GitHub_Tools.DEPENDABOT, octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnce(noticeStub);
+ sinon.assert.notCalled(warningStub);
+ });
+});
diff --git a/tests/SastService.test.ts b/tests/SastService.test.ts
new file mode 100644
index 00000000..caa25d74
--- /dev/null
+++ b/tests/SastService.test.ts
@@ -0,0 +1,38 @@
+import * as core from '@actions/core';
+import sinon, { SinonStub } from 'sinon';
+import { CodeQLService } from '../src/sasttools/CodeQLService';
+import { SastService } from '../src/sasttools/SastService';
+import GitHub_Tools from '../src/types/GitHubTools';
+
+describe('SastService', function () {
+ let warningStub: SinonStub;
+ let noticeStub: SinonStub;
+ let infoStub: SinonStub;
+ let exportVariableStub: SinonStub;
+ let logStub: SinonStub;
+ let setCodeQLFindingsStub: SinonStub;
+ const octokitMock: any = {};
+
+ beforeEach(function () {
+ warningStub = sinon.stub(core, 'warning');
+ noticeStub = sinon.stub(core, 'notice');
+ infoStub = sinon.stub(core, 'info');
+ exportVariableStub = sinon.stub(core, 'exportVariable');
+ logStub = sinon.stub(console, 'log');
+ setCodeQLFindingsStub = sinon.stub(CodeQLService, 'setCodeQLFindings');
+ });
+
+ afterEach(function () {
+ sinon.restore();
+ });
+
+ it('should run CodeQL suite if toolName is "CodeQl"', async function () {
+ await SastService.getStateOfSastTool(GitHub_Tools.CODEQL, octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnce(setCodeQLFindingsStub);
+ });
+
+ it('should only export variable "sastTool" if tool is not supported', async function () {
+ await SastService.getStateOfSastTool('unsupported tool', octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnceWithExactly(exportVariableStub, 'sastTool', 'unsupported tool');
+ });
+});
diff --git a/tests/ScaService.test.ts b/tests/ScaService.test.ts
new file mode 100644
index 00000000..41c4731c
--- /dev/null
+++ b/tests/ScaService.test.ts
@@ -0,0 +1,38 @@
+import * as core from '@actions/core';
+import sinon, { SinonStub } from 'sinon';
+import { DependabotService } from '../src/scatools/DependabotService';
+import { ScaService } from '../src/scatools/ScaService';
+import GitHub_Tools from '../src/types/GitHubTools';
+
+describe('DependabotService', function () {
+ let warningStub: SinonStub;
+ let noticeStub: SinonStub;
+ let infoStub: SinonStub;
+ let exportVariableStub: SinonStub;
+ let logStub: SinonStub;
+ let setDependabotFindingsStub: SinonStub;
+ const octokitMock: any = {};
+
+ beforeEach(function () {
+ warningStub = sinon.stub(core, 'warning');
+ noticeStub = sinon.stub(core, 'notice');
+ infoStub = sinon.stub(core, 'info');
+ exportVariableStub = sinon.stub(core, 'exportVariable');
+ logStub = sinon.stub(console, 'log');
+ setDependabotFindingsStub = sinon.stub(DependabotService, 'setDependabotFindings');
+ });
+
+ afterEach(function () {
+ sinon.restore();
+ });
+
+ it('should run Dependabot suite if toolName is "Dependabot"', async function () {
+ await ScaService.getStateOfScaTool(GitHub_Tools.DEPENDABOT, octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnce(setDependabotFindingsStub);
+ });
+
+ it('should only export variable "scaTool" if tool is not supported', async function () {
+ await ScaService.getStateOfScaTool('unsupported tool', octokitMock, 'owner', 'repo');
+ sinon.assert.calledOnceWithExactly(exportVariableStub, 'scaTool', 'unsupported tool');
+ });
+});
diff --git a/tests/branchprotection.test.ts b/tests/branchprotection.test.ts
deleted file mode 100644
index 5dc9c189..00000000
--- a/tests/branchprotection.test.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import * as core from '@actions/core';
-import * as github from '@actions/github';
-import sinon, { SinonSandbox, SinonStub } from 'sinon';
-import { expect } from 'chai';
-import { BranchProtectionService } from '../src/branchprotection/BranchProtectionService';
-describe('BranchProtectionService', () => {
- let sandbox: SinonSandbox;
- let warningStub: SinonStub;
- let exportVariableStub: SinonStub;
- let getOctokitStub: SinonStub;
-
- beforeEach(() => {
- sandbox = sinon.createSandbox();
- warningStub = sandbox.stub(core, 'warning');
- exportVariableStub = sandbox.stub(core, 'exportVariable');
- getOctokitStub = sandbox.stub(github, 'getOctokit');
- sandbox.stub(github.context, 'repo').value({
- owner: 'owner',
- repo: 'repo',
- });
- });
-
- afterEach(() => {
- sandbox.restore();
- });
-
- it('should handle successful branch protection retrieval', async () => {
- getOctokitStub.returns({
- rest: {
- repos: {
- getBranchProtection: sinon.stub().resolves({
- data: {
- enforce_admins: { enabled: true },
- required_pull_request_reviews: { required_approving_review_count: 1 },
- },
- }),
- },
- },
- });
-
- await BranchProtectionService.getStateOfBranchProtection();
- expect(warningStub.called).to.be.false;
- expect(exportVariableStub.calledWith('numberOfReviewers', 1)).to.be.true;
- });
- it('should call warning when admins can byypass branch protection rules', async () => {
- getOctokitStub.returns({
- rest: {
- repos: {
- getBranchProtection: sinon.stub().resolves({
- data: {
- enforce_admins: { enabled: false },
- required_pull_request_reviews: { required_approving_review_count: 1 },
- },
- }),
- },
- },
- });
-
- await BranchProtectionService.getStateOfBranchProtection();
- expect(warningStub.called).to.be.true;
- expect(exportVariableStub.calledWith('numberOfReviewers', 0)).to.be.true;
- });
- it('should call warning and set numberOfReviewers to 0 when github repo is private (status = 403)', async () => {
- getOctokitStub.returns({
- rest: {
- repos: {
- getBranchProtection: sinon.stub().rejects({
- status: 403,
- message: 'Forbidden',
- }),
- },
- },
- });
-
- await BranchProtectionService.getStateOfBranchProtection();
- expect(warningStub.called).to.be.true;
- expect(exportVariableStub.calledWith('numberOfReviewers', 0)).to.be.true;
- });
-});