From 739e112e384d35d9f7ed3d2970ddd7636a3d33b2 Mon Sep 17 00:00:00 2001 From: fisehara Date: Sat, 29 Jun 2024 09:00:14 +0200 Subject: [PATCH] Merge GitHub reporting repository defaults with custom GitHub reporting configuration (#86) * Merge GitHub reporting repository defaults with custom GitHub reporting configuration Report GitHub configuration is merged with the default GitHub reporting configuration from the action context: ``` github: username: GITHUB_ACTOR, owner: GITHUB_REPOSITORY_OWNER, token, "project-name": GITHUB_REPOSITORY, ``` Before the default github reporting function was used and did not take the custom configuration into account. Especially the duplication detection and the severity as label was not used when set the github-report flag. Signed-off-by: fisehara * typo --------- Signed-off-by: fisehara Co-authored-by: Ramana Reddy --- README.md | 38 ++++++++++++++++++++++++++++ dist/index.js | 68 +++++++++++++++++++++++++++++++-------------------- src/index.js | 25 ++++++++++--------- src/yaml.js | 43 ++++++++++++++++++++------------ 4 files changed, 120 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 7fd12f7..c6ce147 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,8 @@ jobs: report-config: issues.yaml ``` + + **GitHub Example Action running Nuclei with GitHub Issue reporting** Setting permissions for `GITHUB_TOKEN`, according to the [github actions docs](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token). @@ -130,6 +132,42 @@ permissions: github-token: ${{ secrets.GITHUB_TOKEN }} ``` +Using the `github-report` creates a default configuration to enable reporting to Github Issues + + +**GitHub Example Action running Nuclei with custom GitHub Issue reporting** + +```yaml + - name: Nuclei - Vulnerability Scan + uses: projectdiscovery/nuclei-action@main + with: + target: https://example.com + report-config: github-issue-config.yaml + github-token: ${{ secrets.GITHUB_TOKEN }} +``` + +Create a `yaml` file to define the nuclei github issue reporting behavior: + +```yaml +github: + duplicate-issue-check: true + severity-as-label: true + issue-label: '' +``` + +The `nuclie-action` will fill in the repository settings into the custom configuration. The file don't need to be augmented with these information manually. + +```yaml +github: + username: GITHUB_ACTOR, + owner: GITHUB_REPOSITORY_OWNER, + token, + "project-name": GITHUB_REPOSITORY, +``` + + + + **GitHub Example Action running Nuclei with GitHub Security Dashboard reporting** ```yaml diff --git a/dist/index.js b/dist/index.js index 84201e6..a514ed8 100644 --- a/dist/index.js +++ b/dist/index.js @@ -10747,24 +10747,35 @@ const GITHUB_REPOSITORY_OWNER = process.env.GITHUB_REPOSITORY_OWNER; const GITHUB_REPOSITORY = process.env.GITHUB_REPOSITORY.replace(`${GITHUB_REPOSITORY_OWNER}/`, ''); const GITHUB_WORKSPACE = process.env.GITHUB_WORKSPACE; -async function generateGithubReportFile(token) { - const content = { - "github" : { - "username": GITHUB_ACTOR, - "owner": GITHUB_REPOSITORY_OWNER, - token, - "project-name": GITHUB_REPOSITORY, - "issue-label": "Nuclei Report" - } +async function generateGithubReportFile(token, reportConfigFileName = 'github-report.yaml') { + const gitHubRepoConfig = { + username: GITHUB_ACTOR, + owner: GITHUB_REPOSITORY_OWNER, + token, + "project-name": GITHUB_REPOSITORY, + }; + + let content = {}; + + if (reportConfigFileName) { + try { + const data = await external_fs_.promises.readFile(external_path_.join(GITHUB_WORKSPACE, reportConfigFileName), 'utf8'); + const { github, ...rest } = load(data); + content = { ...rest, github: { ...gitHubRepoConfig, ...github } }; + } catch (err) { + throw new Error(`Error reading the passed report config file: ${err.message}`); } - const githubConfigYml = dump(content, { - flowLevel: 3 - }); + } else { + content.github = gitHubRepoConfig; + } - external_fs_.writeFileSync(external_path_.join(GITHUB_WORKSPACE, 'github-report.yaml'), githubConfigYml, err => { - if (err) - reject(err); - }); + const githubConfigYml = dump(content, { flowLevel: 3 }); + + try { + await external_fs_.promises.writeFile(external_path_.join(GITHUB_WORKSPACE, reportConfigFileName), githubConfigYml); + } catch (err) { + throw new Error(`Error writing the report config file: ${err.message}`); + } } ;// CONCATENATED MODULE: ./src/utils.js function parseFlagsToArray(rawFlags) { @@ -10813,9 +10824,9 @@ options.listeners = { }; async function run() { - try { - // download and install - const binPath = await downloadAndInstall(nucleiVersion); + try { + // download and install + const binPath = await downloadAndInstall(nucleiVersion); const params = []; if (!target && !urls) { @@ -10831,17 +10842,16 @@ async function run() { new URL(templates) params.push(`-turl=${templates}`); } - catch(_) { + catch (_) { params.push(`-t=${templates}`); } } if (workflows) params.push(`-w=${workflows}`); params.push(`-se=${sarifExport ? sarifExport : 'nuclei.sarif'}`); if (markdownExport) params.push(`-me=${markdownExport}`); - if (reportConfig) params.push(`-rc=${reportConfig}`); if (config) params.push(`-config=${config}`); if (userAgent) params.push(`-H=${userAgent}`); - params.push(`-o=${ output ? output : 'nuclei.log' }`); + params.push(`-o=${output ? output : 'nuclei.log'}`); if (src_json) params.push('-json'); if (includeRR) params.push('-irr'); if (omitRaw) params.push('-or'); @@ -10849,17 +10859,21 @@ async function run() { if (flags) params.push(...parseFlagsToArray(flags)); // If everything is fine and github-report is set, generate the yaml config file. - if (githubReport) { + if (githubReport == true) { + // create default config file with name `github-report.yaml` await generateGithubReportFile(githubToken); params.push(`-rc=github-report.yaml`); + } else if (reportConfig != null) { + await generateGithubReportFile(githubToken, reportConfig); + params.push(`-rc=${reportConfig}`); } - // run tool + // run tool delete process.env.GITHUB_TOKEN exec.exec(binPath, params, options); - } catch (error) { - core.setFailed(error.message); - } + } catch (error) { + core.setFailed(error.message); + } } run(); diff --git a/src/index.js b/src/index.js index 62bc11f..1b26234 100644 --- a/src/index.js +++ b/src/index.js @@ -39,9 +39,9 @@ options.listeners = { }; async function run() { - try { - // download and install - const binPath = await installer.downloadAndInstall(nucleiVersion); + try { + // download and install + const binPath = await installer.downloadAndInstall(nucleiVersion); const params = []; if (!target && !urls) { @@ -57,17 +57,16 @@ async function run() { new URL(templates) params.push(`-turl=${templates}`); } - catch(_) { + catch (_) { params.push(`-t=${templates}`); } } if (workflows) params.push(`-w=${workflows}`); params.push(`-se=${sarifExport ? sarifExport : 'nuclei.sarif'}`); if (markdownExport) params.push(`-me=${markdownExport}`); - if (reportConfig) params.push(`-rc=${reportConfig}`); if (config) params.push(`-config=${config}`); if (userAgent) params.push(`-H=${userAgent}`); - params.push(`-o=${ output ? output : 'nuclei.log' }`); + params.push(`-o=${output ? output : 'nuclei.log'}`); if (json) params.push('-json'); if (includeRR) params.push('-irr'); if (omitRaw) params.push('-or'); @@ -75,17 +74,21 @@ async function run() { if (flags) params.push(...parseFlagsToArray(flags)); // If everything is fine and github-report is set, generate the yaml config file. - if (githubReport) { + if (githubReport == true) { + // create default config file with name `github-report.yaml` await generateGithubReportFile(githubToken); params.push(`-rc=github-report.yaml`); + } else if (reportConfig != null) { + await generateGithubReportFile(githubToken, reportConfig); + params.push(`-rc=${reportConfig}`); } - // run tool + // run tool delete process.env.GITHUB_TOKEN exec.exec(binPath, params, options); - } catch (error) { - core.setFailed(error.message); - } + } catch (error) { + core.setFailed(error.message); + } } run(); diff --git a/src/yaml.js b/src/yaml.js index 3b7ed9b..d148a50 100644 --- a/src/yaml.js +++ b/src/yaml.js @@ -7,22 +7,33 @@ const GITHUB_REPOSITORY_OWNER = process.env.GITHUB_REPOSITORY_OWNER; const GITHUB_REPOSITORY = process.env.GITHUB_REPOSITORY.replace(`${GITHUB_REPOSITORY_OWNER}/`, ''); const GITHUB_WORKSPACE = process.env.GITHUB_WORKSPACE; -export async function generateGithubReportFile(token) { - const content = { - "github" : { - "username": GITHUB_ACTOR, - "owner": GITHUB_REPOSITORY_OWNER, - token, - "project-name": GITHUB_REPOSITORY, - "issue-label": "Nuclei Report" - } +export async function generateGithubReportFile(token, reportConfigFileName = 'github-report.yaml') { + const gitHubRepoConfig = { + username: GITHUB_ACTOR, + owner: GITHUB_REPOSITORY_OWNER, + token, + "project-name": GITHUB_REPOSITORY, + }; + + let content = {}; + + if (reportConfigFileName) { + try { + const data = await fs.promises.readFile(path.join(GITHUB_WORKSPACE, reportConfigFileName), 'utf8'); + const { github, ...rest } = yaml.load(data); + content = { ...rest, github: { ...gitHubRepoConfig, ...github } }; + } catch (err) { + throw new Error(`Error reading the passed report config file: ${err.message}`); } - const githubConfigYml = yaml.dump(content, { - flowLevel: 3 - }); + } else { + content.github = gitHubRepoConfig; + } + + const githubConfigYml = yaml.dump(content, { flowLevel: 3 }); - fs.writeFileSync(path.join(GITHUB_WORKSPACE, 'github-report.yaml'), githubConfigYml, err => { - if (err) - reject(err); - }); + try { + await fs.promises.writeFile(path.join(GITHUB_WORKSPACE, reportConfigFileName), githubConfigYml); + } catch (err) { + throw new Error(`Error writing the report config file: ${err.message}`); + } } \ No newline at end of file