From b4ecdc716a787640f1e5e951c8d7b97898981fe8 Mon Sep 17 00:00:00 2001 From: kalvinparker <106995826+kalvinparker@users.noreply.github.com> Date: Tue, 10 Feb 2026 22:09:35 +0000 Subject: [PATCH 01/11] ci: add GitHub Actions workflow for Perl syntax check --- .github/workflows/ci.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..792fb67 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,26 @@ +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: 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 From 127785c63f3f47bb9aa139f413e93fb5b0409dc6 Mon Sep 17 00:00:00 2001 From: kalvinparker <106995826+kalvinparker@users.noreply.github.com> Date: Tue, 10 Feb 2026 22:49:52 +0000 Subject: [PATCH 02/11] ci: enhance GitHub Actions workflow to install Perl dependencies and verify modules --- .github/workflows/ci.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 792fb67..d7293e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,20 @@ jobs: 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: Check Perl syntax run: | set -e From e3d276f2f768d67a19d07c1b678b3b4e4c3ca787 Mon Sep 17 00:00:00 2001 From: kalvinparker <106995826+kalvinparker@users.noreply.github.com> Date: Tue, 10 Feb 2026 22:56:36 +0000 Subject: [PATCH 03/11] chore(readme): add recent changes and high-level policies --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 8811b4e..0f2a918 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,21 @@ 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. + +## High-level policies (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:). +- **Reviews:** At least one approving review required before merge. +- **Security:** Report vulnerabilities privately to the repository owner or + the email listed in the repo metadata. From fd93e026d38f3936ac7c8deb50729b085b4f2d39 Mon Sep 17 00:00:00 2001 From: kalvinparker <106995826+kalvinparker@users.noreply.github.com> Date: Tue, 10 Feb 2026 22:58:55 +0000 Subject: [PATCH 04/11] docs: update section header and remove review requirement from policies --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 0f2a918..0981d6d 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ For more information and questions please contact Cody Dumont cody@melcara.com - 2026-02-10 Local development: Strawberry Perl and required CPAN modules installed for developer convenience and local perl -c checks. -## High-level policies (kalvinparker) +## Todo by kalvinparker - **License:** Ensure a LICENSE file is present and matches project intent. - **Contributing:** Open pull requests from topic branches; include a clear @@ -25,6 +25,5 @@ For more information and questions please contact Cody Dumont cody@melcara.com - **CI required:** All PRs must pass CI (lint/tests) before merging. - **Commit messages:** Use concise, conventional-style commit messages (e.g. eat:, ix:, chore:). -- **Reviews:** At least one approving review required before merge. - **Security:** Report vulnerabilities privately to the repository owner or the email listed in the repo metadata. From bc5459f9d3ca37fe0789c18f7dbcc3deaf5f31d6 Mon Sep 17 00:00:00 2001 From: kalvinparker <106995826+kalvinparker@users.noreply.github.com> Date: Tue, 10 Feb 2026 23:21:33 +0000 Subject: [PATCH 05/11] ci: add Perl::Critic installation and linting to CI workflow --- .github/workflows/ci.yml | 13 +++++++++++++ parse_nessus_xml.v24.pl | 28 ++++++++++++++-------------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7293e6..f4773c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,19 @@ jobs: 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 diff --git a/parse_nessus_xml.v24.pl b/parse_nessus_xml.v24.pl index 056f839..59cfc7f 100644 --- a/parse_nessus_xml.v24.pl +++ b/parse_nessus_xml.v24.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){ From f83bcab761adea926c68f345cb5b294e2c52b9e0 Mon Sep 17 00:00:00 2001 From: kalvinparker <106995826+kalvinparker@users.noreply.github.com> Date: Tue, 10 Feb 2026 23:27:49 +0000 Subject: [PATCH 06/11] ci: add mock Nessus scan report for functional testing --- scan.nessus | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 scan.nessus diff --git a/scan.nessus b/scan.nessus new file mode 100644 index 0000000..bb78ef1 --- /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 + + + + + + + From 6d3fa4f938cdf7744fe593f14a94715c20523dbf Mon Sep 17 00:00:00 2001 From: kalvinparker <106995826+kalvinparker@users.noreply.github.com> Date: Tue, 10 Feb 2026 23:45:57 +0000 Subject: [PATCH 07/11] Add mock Nessus scan; enable Perl::Critic in CI; fix parser filehandles and unknown-plugin handling --- parse_nessus_xml.v24.pl | 2 +- scan.nessus | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/parse_nessus_xml.v24.pl b/parse_nessus_xml.v24.pl index 59cfc7f..1971117 100644 --- a/parse_nessus_xml.v24.pl +++ b/parse_nessus_xml.v24.pl @@ -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/scan.nessus b/scan.nessus index bb78ef1..5b2c77e 100644 --- a/scan.nessus +++ b/scan.nessus @@ -20,7 +20,7 @@ - + Summary of the mock scan results (no sensitive data). Total Hosts: 1\nTotal Findings: 3\nHigh: 0\nMedium: 1\nLow: 2 @@ -28,7 +28,7 @@ - + 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) @@ -36,7 +36,7 @@ - + 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 @@ -44,7 +44,7 @@ - + Mock open service used to exercise port scanning and service reporting. Service: ssh (mock) - banner suppressed From d42d9bec87c560100513004c6ba30bf74acfd68b Mon Sep 17 00:00:00 2001 From: kalvinparker <106995826+kalvinparker@users.noreply.github.com> Date: Tue, 10 Feb 2026 23:47:58 +0000 Subject: [PATCH 08/11] Add new Nessus report for February 2026 --- nessus_report_20260210233823.xlsx | Bin 0 -> 34825 bytes ~$nessus_report_20260210233823.xlsx | Bin 0 -> 165 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 nessus_report_20260210233823.xlsx create mode 100644 ~$nessus_report_20260210233823.xlsx diff --git a/nessus_report_20260210233823.xlsx b/nessus_report_20260210233823.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c3c8636097370f587e2560ad3451ef6d42f25eed GIT binary patch literal 34825 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

#_JVW_2yzF$&ty%>ouI8 zoo}0$_FxchJF;7jl{I>)JBr+pvM^-H^}Cpck|k>?f+aKdu23f^rPDNwv{};-MM&kt zejqsKk@l>#Y?nK`_a2D$jzcylI~x(IoKb21VG&W&?+_rJ;~y)oaU#mlP|NGd%2g&g zxk&r$+HFZU=P11)jq4x*$v{3XF?)^h2oz>ZB4Z>81y})JApPSf@i~7lHbKXEjsvhq zd6n`Ap~l`kcb$v~pGN_spXud$oM@Zx`6hiP&Vq($REqVt=W_&Jl)xt)jhVy z+T8=;-9Z>>p&SW9A_n-lcvY4K|D1~{+rgvG2hD!lsveNAzyLn+;Fj8!`>BPAb+SdI z4%RdxN!}_0TI+3>hPg<@d<<4{q}Iwzi9oDY=^7Vo3eZwNN?6AMMst_I2gnU9>^@G; zZR534@qiLtVdQci38A-xU!Hb`!<8n@gQ2r?rVOv^W!@#3_Fc{jFP7*}!gUI`6c-XC zR*#?@ zLJ+#6WF0Bq#`PUmFilq*DH$@zvPo&)hI+sPm=n#{-Jq38&fS1K~w<$TPX1=sd;9prJWBgsbFF9ypj4BoZK-c1p4!a;A22 z48tU_=N~f|3LyvYgu@n!B0+xe@Jvm%CDtkBH>;75yg$tl>oGUX63a6;1c!JZ-~|TD z)6s?kODp9SJdamDk5@fU2I<(SR8cgv2=6o(ne*nuUC&L}Jl?hw=61V)o+46%&VOL* z4kyiyrh3K0cnTuRu@eg-3$YX11&8!Mg1if7JO+IywS{5rykYGOa6O4ydR8q)sn7+@ zlTp2~UwiC1pm*ecp6e`zVikeuXdz-lZHRi|n z*9^kAi{H9Pq3*ZI=zfMEfQ!RyOS*X;kJjSD1=Uctgt^@%E$ZHx;jSRuEdGeJzs0oa zL!{8+m#p?|t#J)(^&_I|Th8lF8dLvqt$SK~zH0vAIEi!?jbPtjQB~Ve^gDs2<`Yop zRH0>rVin*;4gqMB^AAhT=M>D?_<6;(ztl&q`LbYfa=oUTw5M_k6?p_yCzY?TCwC-e z5X}5(uXh{IgSb8J{&%NX2{gjmme$58^TdL?3tpK~oDI>z3P@L1dNJ1I3XK#xVmD{* z>#H}}LJ^i?$Z=zk9;0tP4jvxVoV%~3%5#(ncZg_+n^1|5IAoc0#$!-jY4a0P5?Rua zi3n~wsO)9?q(jELPIPJya!7}5cQQMjPue%nzDS18p1;pcq)fFK8VDgCPKB3+cl{} z>lhGOISjjxN&m`+N-a{!)Seih!lwK71cP z#79N^!AUKN^ZhG49X&7+BMuWmKz7n_?9JXs!n_-|CXV+P_&BBp4od4{C?G&T4Gy}_ z%tm-8v)<$B8Q1WhFeCSHKev&ckT5<71f-q^02o+f8wYwGLBa`}a=aG-9lGoMo-!NhfO+#tYXbbh z&dk}>=!2Nk`rP->gWfQ_`f3vAGWn}fMlXK|(=WdM`AL~$2buNr!mhP8Bp?E}`pqgE zSIWn?yli!EdL^#;7x6o+1N@0tQ?P%7H7@=K*``6Nah`^oxq@Wc$_6+via0GHLg?Y8A4gmjlI3d<;W*|(uVt9D!JAD+=8%T8dNc;`QOKSr#E|ve;xO`4{jg4KF12o6_ zYlF_?_`YKzNzX@U3YAG6!ZaH3&Z~+&S{obY*RW-6?%kfV)A(qF>@NOX+QHG*FkgD< z#Mzc)1e$4Qth5;`G!;l>7QO3_(dX!0e>Xqndr^d^s^97GK0NMY;!A& zz{c5RSs{(>p7PeKGb_!8?S}9IO(_*!kYub4um|(_PUYY&Dh*JaBR=G&Ev%5snFZpT zlY8-a6ob`Lg;C8mC8$p6Wt7Deibyh`NU9s(Kp!Ry3};uUs$97#Xr{A;IQXf02;5#1 z5<1|JRjXwetIl1UeiI9PyyX7ifbbjCE_vGrLjoe}My?DhXh1ODN5?fexgO9kj+_(K zl4`$MQ_n}ZT=F|!8kPP%dtn_7o+S0^qu))Su%$<%T#Nk9GqDk zbDgw{M)yvcZyGZ!I4Y%6eYW}d9wxuN|E3)w3yxj7^CR_gQAG;Wv8-|dyT}Bwmz(4mpQg%L<^=%D z*-8{eO6gAszLgIT3$t5iVWqm%f~pyCYRtI`3^|~NdG_=eR1YWL9#5i>4H5?v<&hN| zK!i)lkCwxxCHa$)+mO!*WhA96_7o5ke)t9_rWFaUN@hBC)52d6GggDgxfUP`;`(C< zcGu;E1w`_72T((&wM!cfK<8Mcm?93YKFai22ve-3tR-+dZVux9O!j|rNkx`1= zw8a(rf6Hi-+yBUD(zA?C17x)H-!hswFdPk#QH1|hM$aJslF=E6{8wbO`Cl^Xqq*!2 zkkMQCkr>8DDe6PXfT-Z+>!sfRW>^gl#28zjzrlRV$LhH50H zfubzQckScG!U{qke0cb3`LAp|o?YA0YsZ4XE41l=U_(77Ze?xQE3$M1G9Oz#W`5$Y zDBGKX>-RET-l>BXxhf9ywA(fMUDOiL7;joXDCjDIo(nss zLY167Xan21Jzocs$im>ZE-AnQ=?2iq@*gDfIiogKRYz%+5%t@0;se5L)Bc3!EfJ)! zoi^GDj)I->@+YX#a;pXj)J45k3CZ&2upf7xq+4kY(J2cn2S=@WP%fF;U9>RejT0S# z#z@KCv&3Cj&Yc9Vm{1ZGSQ6@D!uMieO9XBY<9bYwq;WBlIn0K{T~btMX={O|5=z8Q z>ar|of31xQ^W6RfrYJG9WU<;csTM!4jUqFD@kjd%ZP|&0tDmQgOQTD0$OJ0iVGM9! zd>Kverp`m~PK{)B z9y;f5P93rbE2k2FbdB=BB@vx3D>lC7*O_JNihikK5L0$LL!$|No2Fu0>zt7&HP{Mt zDk>Gl8%R>VHzDKd@$h0)U*9hme<6?W|M6S)m(}NoVS2H#N_i=MR1jx62Hz#kZ7biy z!+hUI&F$9GYfivY=V@S(+E!t32&+rR zpR9hNA2c^cqr2P;T;aErJ7Pz(7inXSXw8jdI6Wieoy9Eh$*`@%9Ole(i(DIZ5 zmixV3Xf6l*2?YkM&ebCGjq_1b4885uk`TzDEFJ2|BZu4F+Of>rR?XYv&oQX^Zm{TZ zZgBDr3zLps#*ptR(BbONBaLV>8f2w{H~yrn7zjAGh&n5Td1*B+AUAq{X!38ve9MlQ zAh3e|))}!bpsnidWH4zNhQ#p^G1SLMb=ETRjLo{joel!=0#2{Cg2XO}XaGHpbwo?Ko87;Gn zX<%!(y5yMfEP=;UUT3BHI@H(74;NxZI}Q~Y@xBbJ(itKbUyG;~cRqH7Tn>b)9GQw0 z9Z=3S*w-l2v2D2I?S2|gprvq$l$qeW2C6fSR$9bU4T!#`0VGJ0{w)goU+IzmO7VF1 zjX!5chK&qBcQ7J?jW)0F`_3j;(Tn7mu@gJeT)gu)ylrEHl5jge8WJmC>DU-F8nzAD z~9pfMKn&NH5xT2kMo}P`W2c|$JN7L?<=!CEaz){va(fb zgQV|IgiOQ3Cut+Mxr328JdXJEhf!d%XuGmL-2l}zR%&z^xBy&oH^8?8NNagxXk#F6 zZ)5Agpl@sYoWTz$*$H?9x~Eq?eY|PL+-8-Dl~56+p#F)i>B*~F;y|Q|r)NljwLd-L z73EJ?Fn)S|b2t!xMDH9|+c;ig&0k+3VFDgD{KNvvV zoU0$^PHfw1px2|*xAlZT1O$WGzIf_bVu9+&N)nyIg zkc0Rn{?fzQZp2-{FKuQ&supX~u1sCY!^RzdAUSJbXfvye+Ou^6yY@T1{kW9|b*snp zu!{xWmkUeF6j8BWeDwW`#gNOTF+QB{tv&j|M!DL!E@N3XJs%V2eoQcp^{o(;=|QHDtwyU2~Y3P?8R=y`#7RvfW+EWZTFA zBBB6TFaf-OFCt=KV{h~ zTR)=VUnPxDiOY7~8cY@3*d?6bM)-lEsX{5Gi0btz38uPu^h4$McuM;;YFVwH8A2ay zO;z~$Vg?cO=$bUF+FU^Bp-DF&S383Tw@u4~ zp>0=^bt)xWxU?i9B|+b;4Z(Z@%rnhS9+WS1AyCK|26Di08GG>`j^IfR#U(Ofty6X z45R_9K>oR9G%yFa{qYMY5ui-~oWEiu{-^0Q@L=E`voC|~0Dbdb>o^Nc0PeH*LMVm* z7vcZwx(7TIxKG;4P-=jL15W;*LYG zug(IvgUkzJ2C#?(p2c&B3h*p|yM4SMYyipZz=-E}A20&AMZ*i?0q^gK=M6hx1aN8n z7epZ8-x1F{4ZsNCis&y0RFc0Vp1qC02;d^tFNhqnzayUA7{CbNQo}C@LCU`&UVR|I zg<@X_-PC_0y!t$VYkIyAwCVmvc=d4rS89AA^fLU7@aoe5uDAC>5Mlls;njx$Ts7^5 z(8l^V!mG~$xER(8frkBWglBsn_-O*yIC>$(asG|)Y@Y!WfXmao5b$`O39lBb0j2;~ z?s%by^Zz&HAL@7j1Az-xya2NVo`JySD}VvOxzI0wK(S{4aD3dW`U^Pq@rAMs$TR~e zpTALF)uh1bX)hRWnb$F|&IUNS-~|Jt{5s~<4=`|e^$SK`?RCtnpJL#c!xxOV_N$mz zCj%Vo^Foo=f0gp;T!7;+UMRN)uToysD!_ZlFBD&sS1GS*7T~R<7fPqatCUx@3$UB| zh4RzpRm!Ux2H21ILQ%ATmGY{V0rvg8P}Ut^rM#+XfDPCeiqRKf%IoGV@Mj#@Hhdv! ufBg?L;NO-a@MvJO;bpXk>wk>?A0t9u>aViEZ{AP?z6bzLut~SSe)~Uc5QTRD literal 0 HcmV?d00001 diff --git a/~$nessus_report_20260210233823.xlsx b/~$nessus_report_20260210233823.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..9ea77dd700aeecac239686bc00b64e4d01a744c2 GIT binary patch literal 165 zcmd<}PRuFG%u@(REXqzTQXm%aGI%p2GUPCnF=R62F(@zu0C`0W*$k;bQi19a0ArvO AF8}}l literal 0 HcmV?d00001 From bc6bbeb7def286a4242fe64fddbcfd8687a00157 Mon Sep 17 00:00:00 2001 From: kalvinparker <106995826+kalvinparker@users.noreply.github.com> Date: Tue, 10 Feb 2026 23:51:19 +0000 Subject: [PATCH 09/11] Refactor code structure for improved readability and maintainability --- parse_nessus_xml.v24.pl => parse_nessus_xml.v26.01.pl | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename parse_nessus_xml.v24.pl => parse_nessus_xml.v26.01.pl (100%) diff --git a/parse_nessus_xml.v24.pl b/parse_nessus_xml.v26.01.pl similarity index 100% rename from parse_nessus_xml.v24.pl rename to parse_nessus_xml.v26.01.pl From b887c49a87af69a49e3728b9f3bac11de3657f43 Mon Sep 17 00:00:00 2001 From: kalvinparker <106995826+kalvinparker@users.noreply.github.com> Date: Tue, 10 Feb 2026 23:56:21 +0000 Subject: [PATCH 10/11] docs: update README with recent changes and TODOs (CI, mock scan, SBOM) --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index 0981d6d..635467c 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,33 @@ For more information and questions please contact Cody Dumont cody@melcara.com - 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. From f568b9feadbef350d32a7c8768d5ee84c1ddf451 Mon Sep 17 00:00:00 2001 From: kalvinparker <106995826+kalvinparker@users.noreply.github.com> Date: Tue, 10 Feb 2026 23:57:56 +0000 Subject: [PATCH 11/11] feat: add CycloneDX SBOM generation for Nessus parser --- sbom.cyclonedx.json | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 sbom.cyclonedx.json 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" + ] } + ] +}