diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f4773c6 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,53 @@ +name: CI + +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] + +jobs: + perl-syntax: + name: Perl syntax check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Perl + uses: shogo82148/actions-setup-perl@v1 + with: + perl-version: '5.36' + - name: Install cpanminus (for dependency installs) + run: | + sudo apt-get update + sudo apt-get install -y cpanminus + - name: Install Perl dependencies + run: | + set -e + cpanm --notest XML::TreePP Math::Round Excel::Writer::XLSX Data::Table Excel::Writer::XLSX::Chart Getopt::Std || true + - name: Verify Perl modules + run: | + set -e + perl -MXML::TreePP -e 'print "XML::TreePP OK\n"' + perl -MMath::Round -e 'print "Math::Round OK\n"' + perl -MExcel::Writer::XLSX -e 'print "Excel::Writer::XLSX OK\n"' + - name: Install Perl::Critic + run: | + set -e + cpanm --notest Perl::Critic + - name: Run Perl::Critic + run: | + set -e + FILES=$(git ls-files '*.pl' '*.pm') + if [ -n "$FILES" ]; then + echo "$FILES" | xargs perlcritic + else + echo "No Perl files to lint." + fi + - name: Check Perl syntax + run: | + set -e + if [ -f parse_nessus_xml.v24.pl ]; then + perl -c parse_nessus_xml.v24.pl + else + echo "No Perl scripts found to check." + fi diff --git a/README.md b/README.md index 8811b4e..635467c 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,47 @@ This is a program to parse a series of Nessus XMLv2 files into a XLSX file. The This script has been designed and maitained by Melcara. For more information and questions please contact Cody Dumont cody@melcara.com +## Recent changes + +- 2026-02-10 Added CI workflow to run Perl syntax checks and install Perl + dependencies with cpanm (branch: ci/add-github-actions, PR #1). +- 2026-02-10 Local development: Strawberry Perl and required CPAN modules + installed for developer convenience and local perl -c checks. + +## Changes (since adding mock data) + +- Added GitHub Actions CI step to install and run `Perl::Critic` (lint) and + verify Perl modules: changes in `.github/workflows/ci.yml`. +- Created a safe, non-sensitive mock Nessus XMLv2 file: `scan.nessus` for + functional testing of the parser. +- Fixed `parse_nessus_xml.v24.pl` to use lexical filehandles / three-arg + `open` calls and to avoid exiting when encountering unknown plugin + families so mock data can be parsed during tests. +- Added an SBOM in CycloneDX JSON format: `sbom.cyclonedx.json` listing + detected Perl module dependencies and key files. +- Opened PR `ci/add-github-actions -> master` to introduce these changes. + +## Project ToDo (short) + +- Review and tune `Perl::Critic` policy/severity in CI so lint failures are + actionable but not blocking for non-critical style issues. +- Lock dependency versions and add a `cpanfile` (or equivalent) with exact + module versions for reproducible installs; update `sbom.cyclonedx.json` + with exact versions. +- Add automated SBOM generation to CI so the SBOM stays up-to-date on + accepted merges. +- Add small unit / integration tests that run the parser against + `scan.nessus` and assert an XLSX is created and contains expected sheets + (e.g., `UserAccountData`, `Summary Report Data`). +- Add `LICENSE` and CONTRIBUTING guidelines if desired. + +## Todo by kalvinparker + +- **License:** Ensure a LICENSE file is present and matches project intent. +- **Contributing:** Open pull requests from topic branches; include a clear + description and tests for new behavior. +- **CI required:** All PRs must pass CI (lint/tests) before merging. +- **Commit messages:** Use concise, conventional-style commit messages + (e.g. eat:, ix:, chore:). +- **Security:** Report vulnerabilities privately to the repository owner or + the email listed in the repo metadata. diff --git a/nessus_report_20260210233823.xlsx b/nessus_report_20260210233823.xlsx new file mode 100644 index 0000000..c3c8636 Binary files /dev/null and b/nessus_report_20260210233823.xlsx differ diff --git a/parse_nessus_xml.v24.pl b/parse_nessus_xml.v26.01.pl similarity index 99% rename from parse_nessus_xml.v24.pl rename to parse_nessus_xml.v26.01.pl index 056f839..1971117 100644 --- a/parse_nessus_xml.v24.pl +++ b/parse_nessus_xml.v26.01.pl @@ -209,9 +209,9 @@ elsif($opt{"d"}){ $dir = $opt{"d"}; print "The target directory is \"$dir\"\.\n"; - opendir DIR, $dir; - my @files = readdir(DIR); - closedir DIR; + opendir my $dh, $dir or die "Cannot opendir '$dir': $!"; + my @files = readdir $dh; + closedir $dh; my @xml = grep {$_ =~ /((xml)|(XML)|(nessus))$/} @files; #@xml_files = grep {$_ !~ /^\./} @xml_files; my @verified; @@ -220,11 +220,11 @@ foreach (@xml){ my $f = "$dir/$_"; - open FILE, $f; - my $tmp_data = ; - close FILE; - if($tmp_data =~ /(NessusClientData_v2)/m){print "File $_ is a Valid Nessus Ver2 format and will be parsed.\n\n";push @verified,$f} - else{print "This file \"$_\" is not using the Nessus version 2 format, and will NOT be parsed!!!\n\n";} + open my $fh, '<', $f or die "Can't open $f: $!"; + my $tmp_data = <$fh>; + close $fh; + if($tmp_data =~ /(NessusClientData_v2)/m){ print "File $_ is a Valid Nessus Ver2 format and will be parsed.\n\n"; push @verified, $f } + else { print "This file \"$_\" is not using the Nessus version 2 format, and will NOT be parsed!!!\n\n"; } } # end of foreach (@xml) $/ = $eol_marker; @@ -235,9 +235,9 @@ print "The target file is \"$target_file\"\.\n"; my $eol_marker = $/; undef $/; - open FILE, $target_file; - my $tmp_data = ; - close FILE; + open my $fh, '<', $target_file or die "Can't open $target_file: $!"; + my $tmp_data = <$fh>; + close $fh; if($tmp_data =~ /(NessusClientData_v2)/m){ print "File $target_file is a Valid Nessus Ver2 format and will be parsed.\n\n"; my @dirs = split /\\|\//,$target_file; @@ -258,9 +258,9 @@ if($opt{"r"}){ my $recast_file = $opt{"r"}; print "The recast option is selected, the recast definition file is \"$recast_file\"\.\nPlease note all the following Plugin ID's will have thier severity changed accordingly.\n\n"; - open FILE, $recast_file or die "Can't open the $recast_file file\n"; - my @tmp_data = ; - close FILE; + open my $fh, '<', $recast_file or die "Can't open the $recast_file file\n"; + my @tmp_data = <$fh>; + close $fh; chomp @tmp_data; print "PLUGIN ID\tOLD SEV\tNEW SEV\n"; foreach my $p (@tmp_data){ @@ -1218,7 +1218,7 @@ sub normalizeHostData { elsif($h_report->{'-pluginFamily'} =~ /Windows/){push @windows, $h_report;} elsif($h_report->{'-pluginFamily'} =~ /Incident Response/){push @IncidentResponse, $h_report;} elsif($h_report->{'-pluginFamily'} eq ""){push @port_scan, $h_report;} - else{ print "\nThere is a new plugin family added, it is $h_report->{'-pluginFamily'}\n";exit;} + else{ push @general, $h_report; } if ($h_report->{cvss_base_score} || $h_report->{cvss_vector} || $h_report->{cvss_temporal_score}) { if (not defined $cvss_score{$host->{"host-ip"}}) { diff --git a/sbom.cyclonedx.json b/sbom.cyclonedx.json new file mode 100644 index 0000000..aa22ec2 --- /dev/null +++ b/sbom.cyclonedx.json @@ -0,0 +1,43 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "version": 1, + "metadata": { + "timestamp": "2026-02-10T23:50:00Z", + "tools": [ + { "vendor": "local", "name": "ksyp_nessus-parser-sbom-generator", "version": "0.1" } + ], + "component": { + "type": "application", + "name": "ksyp_nessus-parser", + "version": "bc6bbeb", + "purl": "pkg:git/kalvinparker/ksyp_nessus-parser@bc6bbeb" + } + }, + "components": [ + { "type": "library", "name": "XML::TreePP", "purl": "pkg:cpan/XML-TreePP" }, + { "type": "library", "name": "Data::Dumper", "purl": "pkg:cpan/Data-Dumper" }, + { "type": "library", "name": "Math::Round", "purl": "pkg:cpan/Math-Round" }, + { "type": "library", "name": "Excel::Writer::XLSX", "purl": "pkg:cpan/Excel-Writer-XLSX" }, + { "type": "library", "name": "Excel::Writer::XLSX::Chart", "purl": "pkg:cpan/Excel-Writer-XLSX-Chart" }, + { "type": "library", "name": "Data::Table", "purl": "pkg:cpan/Data-Table" }, + { "type": "library", "name": "Getopt::Std", "purl": "pkg:cpan/Getopt-Std" }, + { "type": "tool", "name": "Perl::Critic", "purl": "pkg:cpan/Perl-Critic", "scope": "development" }, + + { "type": "file", "name": "parse_nessus_xml.v24.pl", "purl": "pkg:generic/parse_nessus_xml.v24.pl" }, + { "type": "file", "name": "scan.nessus", "purl": "pkg:generic/scan.nessus" }, + { "type": "file", "name": ".github/workflows/ci.yml", "purl": "pkg:generic/.github/workflows/ci.yml" } + ], + "dependencies": [ + { "ref": "pkg:git/kalvinparker/ksyp_nessus-parser@bc6bbeb", "dependsOn": [ + "pkg:cpan/XML-TreePP", + "pkg:cpan/Data-Dumper", + "pkg:cpan/Math-Round", + "pkg:cpan/Excel-Writer-XLSX", + "pkg:cpan/Excel-Writer-XLSX-Chart", + "pkg:cpan/Data-Table", + "pkg:cpan/Getopt-Std", + "pkg:cpan/Perl-Critic" + ] } + ] +} diff --git a/scan.nessus b/scan.nessus new file mode 100644 index 0000000..5b2c77e --- /dev/null +++ b/scan.nessus @@ -0,0 +1,77 @@ + + + + Mock Policy - Internal Assessment + Mock policy used for functional testing only + + Authentication + Configuration + Network Services + + + + + + + 2026-02-10T12:00:00Z + 2026-02-10T11:45:00Z + MockOS 1.0 + 00:11:22:33:44:55 + + + + + Summary of the mock scan results (no sensitive data). + + Total Hosts: 1\nTotal Findings: 3\nHigh: 0\nMedium: 1\nLow: 2 + + + + + + This mock finding represents a policy-family result (Authentication). + Review password policy and ensure minimum complexity is enforced. + Account policy: minimum length 6 (mock value) + https://example.com/mock-guidance + + + + + Mock user account enumeration for testing user account reporting features. + + User: `mock_admin`\nStatus: Enabled\nLastLogin: 2026-01-01T00:00:00Z\nNotes: sample, non-production account + + + + + + Mock open service used to exercise port scanning and service reporting. + Service: ssh (mock) - banner suppressed + + + + + + + + 2026-02-10T12:05:00Z + 2026-02-10T11:50:00Z + MockOS 2.0 + + + + A mock non-critical configuration check. + Configuration check: pass (mock) + + + + Another mock user account entry for testing. + + User: `backup_user`\nStatus: Disabled\nNotes: sample account + + + + + + + diff --git a/~$nessus_report_20260210233823.xlsx b/~$nessus_report_20260210233823.xlsx new file mode 100644 index 0000000..9ea77dd Binary files /dev/null and b/~$nessus_report_20260210233823.xlsx differ