CPAN-Audit-20250115.001/0000755000076500000240000000000014741775322013031 5ustar brianstaffCPAN-Audit-20250115.001/LICENSE0000644000076500000240000004373214741775320014045 0ustar brianstaffThis software is copyright (c) 2018 by vti . This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2018 by vti . This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Suite 500, Boston, MA 02110-1335 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2018 by vti . This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End CPAN-Audit-20250115.001/cpanfile0000644000076500000240000000115114741775320014531 0ustar brianstaffrequires 'perl', '5.010001'; requires 'CPAN::DistnameInfo'; requires 'Encode', '3.12'; requires 'IO::Interactive'; requires 'JSON'; requires 'Module::CPANfile'; requires 'Module::CoreList', '5.20181020'; requires 'Module::Extract::VERSION'; requires 'PerlIO::gzip'; requires 'version'; requires 'CPANSA::DB'; on 'test' => sub { requires 'Capture::Tiny', '0.24'; requires 'File::Temp'; requires 'Test::CPAN::Changes'; requires 'Test::Manifest'; requires 'Test::More', '0.98'; }; on 'development' => sub { requires 'HTTP::Tiny'; requires 'Data::Dumper'; requires 'File::Basename'; }; CPAN-Audit-20250115.001/Changes0000644000076500000240000002704114741775320014326 0ustar brianstaffRevision history for Perl extension CPAN-Audit 20250115.001 2025-01-15T18:12:43Z * remove YAML::Tiny build dependency left over from distributing the database in the same distro (#66) 20250109.001 2025-01-10T04:10:05Z * pod fix from the Debian crew (#65) 20250103.001 2025-01-04T02:17:20Z * refresh distro and move to BRIANDFOY 20241208.001 2024-12-08T21:10:30Z * The database of advisories now exists as a separate distribution so it can update itself frequently without requiring new releases of this distribution. The code will look for CPANSA::DB or CPAN::Audit::DB, and the CPAN::Audit::DB now comes with CPANSA:DB. CPAN::Audit::DB will eventually be phased out. * The `installed` command now looks only at the versions you have installed. This changes the comparison from '>=' to '=='. (#62) * The default range operator is now `==` instead of `>=`. You can always specify which way you want the check to work by using an explicit range operator * Since these are significant changes, please report any weird situations that might arise. 20241121.001_001 2024-11-21T22:45:15Z * test release to move CPAN::Audit::DB to a separate module so it can be updated independently. 20240911.001_01 2024-09-10T16:51:05Z * check `cpan-audit dist perl 5.024004` for #62 20240910.001 2024-09-10T15:07:37Z * data update for 2024-09-10 * fix --version message for cpan-audit so it does not show warning 20240908.001 2024-09-09T08:35:55Z * Data upate for 2024-09-08. This inclues CVE-2024-45321 for App::cpanminus. 20240826.002 2024-08-26T06:11:07Z * data update for 2024-08-26 * new report for Mozilla::CA (briandfoy/cpan-security-advisory#161) 20240824.003 2024-08-24T06:51:28Z * data update for 2024-08-24 * now uses the v2 version of the cpan-security-advisory, which allows for arrays of values for affected_versions and fixed versions. * this is the first step toward breaking out the CPAN::Audit::DB module into a separate distribution 20240824.001 2024-08-23T16:06:49Z * data update for 2024-08-24 * some additional reports for Image::ExifTool 20240822.001 2024-08-22T06:32:12Z * Data update for 2024-08-22 20240718.001 2024-07-18T17:32:37Z * data update, and fix for briandfoy/cpan-security-advisory#157 20240715.001 2024-07-15T05:54:32Z * data update for 2024-07-15 20240626.001 2024-06-26T14:35:29Z * data update for 2024-06-26 (mainly polyfill.io compromise) https://stackdiary.com/polyfill-compromise-hits-100000-sites-in-a-supply-chain-attack/ 20240615.002 2024-06-15T15:57:57Z * update the POSIX::2008 advisories 20240615.001 2024-06-15T05:41:25Z * Data update for 2024-06-15 * Added advisory for POSIX::2008 (briandfoy/cpan-security-advisory#154) 20240601.001 2024-06-01T20:15:25Z * data update for 2024-06-01 20240503.001 2024-05-03T17:25:39Z * Data update for 2024-05-03; inlcudes CVE-2024-4140 for Email::MIME 20240430.001 2024-04-30T23:00:42Z * data update for 2024-04-30 * includes CVE-2024-2467 - Crypt::OpenSSL::RSA 20240414.001 2024-04-15T00:01:30Z * data update for 2024-04-14 20240410.001 2024-04-10T17:51:12Z * data update for 2024-04-10 20240401.002 2024-04-01T12:27:17Z * Fix some incorrect data in CPANSA-HTTP-Body-2013-4407 (CVE report is wrong). From Stig in briandfoy/cpan-security-advisory#150 . 20240401.001 2024-04-01T11:50:11Z * data update for 2024-04-01 * fix data issue for Mojolicious report (briandfoy/cpan-security-advisory#149) (Timothy Legge) 20240329.002 2024-03-29T12:08:01Z * Data update for 2024-03-29 20240318.001 2024-03-19T01:54:37Z * Data update for 2024-03-18 * CVE-2013-4184 for Data::UUID is resolved by 1.227 20240307.001 2024-03-09T01:47:48Z * Latest updates to reports and CPAN versions 20240302.001 2024-03-03T00:40:47Z * Data update for 2024-03-02 20240215.001 2024-02-16T04:10:22Z * data update for 2024-02-15 * add --exit-zero option to always exit with unix true even if there are advisories (#57 from Mario Minati) 20240209.001 2024-02-10T06:44:21Z * Fix docs for the --fresh option (mariominati22, #56) 20240117.001 2024-01-17T18:00:26Z * Update for Spreadsheet::ParseXLSX XXE bug. (GitHub #134) 20240110.002 2024-01-10T21:33:57Z * data update for 2024-01-10 * A CVE was assigned for Spreadsheet::Parse::XLSX, so a report was updated (briandfoy/cpan-security-advisory#131) 20240110.001 2024-01-10T16:22:34Z * Data update for 2024-01-10 20240103.002 2024-01-04T02:55:45Z * Update database (#55) 20240103.001 2024-01-03T18:23:43Z * Database update for 2024-01-03 20231226.001 2023-12-26T12:58:18Z Data update for 2023-12-26 20231129.001 2023-11-29T20:14:52Z * Update for 2023-11-29. This includes the CVE-2023-47038 and CVE-2023-47039, both on perl. 20230826.001 2023-08-26T08:48:19Z * Update for CVE-2022-48522 (perl) 20230709.001 2023-07-09T23:24:24Z * Renée Bäcker added 'queried_module' to the JSON output so yoou can tie what you asked about to the distribution the report gave you. GitHub #50. 20230601.002 2023-06-02T15:43:55Z * Fix a problem that masked some reports from Mojolicious * Fixed a report for PGObject::Util::DBAdmin that used the wrong namespace * Moved MojoX::Dispatch::Static report to Mojolicious * Data update for 2023-06-02 20230601.001 2023-06-02T01:21:17Z * Database update up to 2023-06-01 * Many improvements to util/generate from the Perl Toolchain Summit and garu 20230309.004 2023-03-09T12:01:45Z * Fix the GPG signature 20230309.003 2023-03-09T11:52:21Z * Fix the GPG signature 20230309.002 2023-03-09T10:13:33Z * Data cleansing for HTTP::Daemon and App::cpanminus. Thanks to Salve Nilsen and Robert Rothenberg. 20230309.001 2023-03-09T06:44:23Z * Make the 'dist' option do the same thing as 'release', from Salve Nilsen. * No updates to the database 20230308.001 2023-03-08T23:49:32Z * Latest database with some new reports and some fixes to existing reports. Thanks to Salve Nilsen, Robert Rothenberg, and others for the updates. 20230205.001 2023-02-05T14:20:15Z * fix test that checks for exit value of advisory count. Max is now 126 so we don't bump into 127. 20230202.003 2023-02-03T02:48:17Z * Advisories for Apache-Session-Browseable and Apache-Session-LDAP 20230125.002 2023-01-26T00:55:49Z * fixes a test and a missing method. The previous 202301* releases are no good. 20230125.001_002 2023-01-25T19:18:38Z * Github #34 - missing message() method (Robert Rothenberg) 20230125.001_001 2023-01-25T18:03:16Z * Fix json testing bug (Robert Rothenberg, #35) * no updates to DB 20230104.001 2023-01-24T19:56:41Z * January update 20230104.001 2023-01-04T20:58:18Z * Add --json to get output in JSON (Renée Bäcker, #24) * Updated for latest advisories 20220817.001 2022-08-18T22:27:26Z * Added the --exclude-file option to cpan-audit (Graham TerMarsch) * No database updates just yet as we straighten out some things in cpan-security-advisory 20220729.001 2022-07-29T06:29:54Z * Added feature to exclude reports, mostly for those persistent vulnerabilities, such as File::Temp, that won't go away. * Added a freshness check. You can check if your database is old. * There's no database update in this release. That's coming soon. 20220713.001_001 2022-07-15T16:38:39Z * Try out a way to exclude some reports (say, like File::Temp) from Graham TerMarsch (Github #5). This feature might change. * No database updates in this release. 20220708.001 2022-07-08T08:51:14Z * Many more reports (thanks to Robert Rothenberg) 20220705.001 2022-07-05T16:44:45Z * check for simple "freshness" of DB with `cpan-audit -f` * weekly update for the data - too many additions to list (thanks to Robert Rothenberg) 20220629.003 2022-06-29T17:56:53Z * This is the same as the last release, where I forgot to update the version in CPAN::Audit to match that in CPAN::Audit::DB. 20220627.003 2022-06-29T15:44:34Z * Updates for CPANSA-App-revealup, Mozilla-CA, Plack-Middleware-StaticShared, and CPANSA-Socket (Robert Rothenberg) * Starting to track which problems are embedded, non-Perl libraries (Robert Rothenberg) * The lib/CPAN/Audit/DB.pm file is now GPG-signed, although we don't do anything with that just yet. See GPG_README.md. * There are several discussions on GitHub where people can note their preferences on future development. 20220625.001 2022-06-25T19:44:05Z * Updates to File::Slurp and JavaScript::Duktape(::XS)? * New reports for Crypt 20220624.001 2022-06-25T00:35:07Z * reports for JavaScript-Duktape-XS, File-Slurp, RPC-XML, CBOX-XS, IPC-Run, XML-Simple, Sys-Syslog, WWW-Mechanize, LWP, Imager, GD, CryptX, Mojolicious, all from Robert Rothenberg. 20220622.002 2022-06-22T23:33:43Z * I put the docs in the wrong file! 20220622.001 2022-06-22T20:59:18Z * Advisories for Plack, DBD::SQLite from Robert Rothenberg * Refactored and documented util/generated - can now output JSON, although that probably isn't useful yet 20220620.001 2022-06-21T03:14:25Z * Add CVE-2020-8927 for IO-Compress-Brotli (Robert Rothenberg) briandfoy/cpan-security-advisory#18 * Fix to perl versions so they don't appear as if they are in the future (#4) 20220613.001 2022-06-13T18:10:47Z * Fix DB for Perl versions by specify all versions as semantic versions (noted by Robert Rothenberg) 20220611 2022-06-12T22:58:50Z * Use GNU tar instead of bsdtar. Upgrading macOS apparently breaks the established way of avoiding weird Mac tarballs. * Added a couple of ancient security reports to CPANSA. 20220608 2022-06-08T15:08:53Z * Update for the latest CVEs * Now also tracks CVEs in perl too * now maintained by brian d foy 0.15 2019-03-09T09:47:36Z - regenerate database fixing Plack-Middleware-Session distribution name 0.14 2019-01-26T10:23:21Z [ADVISORIES] CPANSA-Dancer2 CPANSA-HTTP-Session2 CPANSA-Plack-Middleware-Session-Cookie 0.13 2018-11-22T20:38:09Z - --no-corelist option by MCRayRay - test fixes 0.12 2018-11-11T19:43:25Z - require Module::CoreList latest version 0.11 2018-11-11T18:57:53Z - check core modules by James Raspass 0.10 2018-11-07T20:17:30Z - --quiet option - small refactoring - require the latest version of Pod::Usage 0.09 2018-11-05T21:17:35Z - do not hide db from pause (#7) 0.08 2018-10-17T18:10:41Z [ADVISORIES] - CPANSA-Net-DNS - CPANSA-PAR - CPANSA-PAR-Packer - CPANSA-RT-Authen-ExternalAuth - CPANSA-Tk - CPANSA-UI-Dialog (updated) - CPANSA-XML-LibXML 0.07 2018-10-16T21:37:20Z - test fixes 0.06 2018-10-16T19:19:22Z - use name instead of fullname - fix installed modules discovery 0.05 2018-10-15T19:36:39Z [ADVISORIES] - CPANSA-MHonArc - CPANSA-Module-Signature - CPANSA-libapreq2 - CPANSA-mod_perl - CPANSA-Compress-Raw-Bzip2 - CPANSA-Compress-Raw-Zlib [IMPROVEMENTS] - kritika.io and metacpan badges 0.04 2018-10-14T10:56:27Z [FEATURES] - install command accepts path to installations [IMPROVEMENTS] - get rid of Carton dependency - more test coverage - CI integrations - perl 5.8 compat 0.03 2018-10-13T12:59:36Z [ADVISORIES] - CPANSA-App-Github-Email - CPANSA-Crypt-OpenSSL-DSA - CPANSA-Crypt-Passwd-XS - CPANSA-DBD-MariaDB - CPANSA-Dancer - CPANSA-Data-Dumper - CPANSA-Email-Address - CPANSA-Encode - CPANSA-ExtUtils-MakeMaker - CPANSA-FCGI - CPANSA-Fake-Encode - CPANSA-Fake-Our - CPANSA-File-DataClass - CPANSA-File-Path - CPANSA-HTTP-Tiny - CPANSA-Imager - CPANSA-PathTools [FEATURES] - new installed command to audit all installed modules - cpan.snapshot support by Takumi Akiyama (github.com/akiym) 0.02 2018-10-09T08:24:36Z - support perl 5.8 0.01 2018-10-08T06:39:07Z - original version CPAN-Audit-20250115.001/MANIFEST0000644000076500000240000000166314741775322014170 0ustar brianstaffChanges CONTRIBUTING.md cpanfile lib/CPAN/Audit.pm lib/CPAN/Audit/Discover.pm lib/CPAN/Audit/Discover/Cpanfile.pm lib/CPAN/Audit/Discover/CpanfileSnapshot.pm lib/CPAN/Audit/Filter.pm lib/CPAN/Audit/FreshnessCheck.pm lib/CPAN/Audit/Installed.pm lib/CPAN/Audit/Query.pm lib/CPAN/Audit/Version.pm LICENSE Makefile.PL MANIFEST This list of files MANIFEST.SKIP META.json README.md script/cpan-audit SECURITY.md t/cli.t t/cli/deps.t t/cli/dist.t t/cli/installed.t t/cli/module.t t/cli/modules.t t/cli/release.t t/cli/show.t t/data/carton/cpanfile.snapshot t/data/cpanfiles/cpanfile t/data/excludes t/data/installed/perl5/lib/perl5/Catalyst.pm t/data/modules_excludes t/data/queried_modules/cpanfile t/discover/cpanfile.t t/discover/cpanfile_snapshot.t t/excludes.t t/installed.t t/json.t t/lib/TestCommand.pm t/queried_modules.t t/query.t t/test_manifest t/version.t META.yml Module YAML meta-data (added by MakeMaker) CPAN-Audit-20250115.001/t/0000755000076500000240000000000014741775321013273 5ustar brianstaffCPAN-Audit-20250115.001/t/discover/0000755000076500000240000000000014741775321015111 5ustar brianstaffCPAN-Audit-20250115.001/t/discover/cpanfile.t0000644000076500000240000000056614741775320017065 0ustar brianstaffuse strict; use warnings; use Test::More; use CPAN::Audit::Discover::Cpanfile; subtest 'discover' => sub { my @deps = _build()->discover('t/data/cpanfiles/cpanfile'); is_deeply \@deps, [ { 'module' => 'Catalyst', 'version' => '5' }, ]; }; done_testing; sub _build { CPAN::Audit::Discover::Cpanfile->new(@_) } CPAN-Audit-20250115.001/t/discover/cpanfile_snapshot.t0000644000076500000240000000100014741775320020764 0ustar brianstaffuse strict; use warnings; use Test::More; use CPAN::Audit::Discover::CpanfileSnapshot; subtest 'discover' => sub { my @deps = _build()->discover('t/data/carton/cpanfile.snapshot'); is_deeply \@deps, [ { 'dist' => 'Apache-LogFormat-Compiler', 'version' => '0.35' }, { 'version' => '1.32', 'dist' => 'Class-Inspector' } ]; }; done_testing; sub _build { CPAN::Audit::Discover::CpanfileSnapshot->new(@_) } CPAN-Audit-20250115.001/t/version.t0000644000076500000240000000344214741775320015147 0ustar brianstaffuse strict; use warnings; use Test::More; use CPAN::Audit::Version; subtest 'in_range' => sub { my $checker = _build(); ok( !$checker->in_range() ); ok( !$checker->in_range('1.2') ); ok( !$checker->in_range( 'abc', 'def' ) ); ok( !$checker->in_range( 'abc', '1.2' ) ); ok( !$checker->in_range( '1.2', 'def' ) ); ok( !$checker->in_range( '1.2', '^1.2' ) ); ok( $checker->in_range( '1.2', '' ) ); ok( $checker->in_range( '1.2', '0' ) ); ok( $checker->in_range( '1.2', '1.1' ) ); ok( $checker->in_range( '1.2', '1.2' ) ); ok( !$checker->in_range( '1.2', '1.5' ) ); ok( $checker->in_range( '1.0', '<=1.1' ) ); ok( $checker->in_range( '1.1', '<=1.1' ) ); ok( !$checker->in_range( '1.2', '<=1.1' ) ); ok( $checker->in_range( '1.0', '<1.1' ) ); ok( !$checker->in_range( '1.1', '<1.1' ) ); ok( !$checker->in_range( '1.2', '<1.1' ) ); ok( !$checker->in_range( '1.0', '>=1.1' ) ); ok( $checker->in_range( '1.1', '>=1.1' ) ); ok( $checker->in_range( '1.2', '>=1.1' ) ); ok( $checker->in_range( '1.2', '>1.1' ) ); ok( !$checker->in_range( '1.1', '>1.1' ) ); ok( !$checker->in_range( '1.0', '>1.1' ) ); ok( $checker->in_range( '1.0', '==1.0' ) ); ok( !$checker->in_range( '1.0', '==1.1' ) ); ok( $checker->in_range( '1.0', '!=1.1' ) ); ok( !$checker->in_range( '1.0', '!=1.0' ) ); ok( $checker->in_range( '5', '>= 1.1, < 6' ) ); ok( !$checker->in_range( '5', '>= 1.1, < 4' ) ); }; subtest 'affected_versions' => sub { my $checker = _build(); is_deeply( [ $checker->affected_versions( [ '1.2', '1.3', '2.0' ], '>= 1.2, <= 1.5' ) ], [ '1.2', '1.3' ] ); }; done_testing; sub _build { CPAN::Audit::Version->new } CPAN-Audit-20250115.001/t/query.t0000644000076500000240000000246414741775320014632 0ustar brianstaffuse strict; use warnings; use Test::More; use CPAN::Audit::Query; subtest 'advisories_for' => sub { my $query = _build( db => { dists => { Foo => { advisories => [ { id => 'SA-1', package => 'Foo', affected_versions => '<1.1' }, { id => 'SA-2', package => 'Foo', affected_versions => '<1.2' }, ], versions => [ { version => '0.9' }, { version => '1.1' }, { version => '1.2' }, { version => '1.3' } ] }, } } ); is_deeply [ $query->advisories_for('Unknown') ], []; is scalar $query->advisories_for('Foo'), 2; is scalar $query->advisories_for( 'Foo', '1.1' ), 1; is_deeply [ $query->advisories_for( 'Foo', '1.3' ) ], []; is_deeply [ $query->advisories_for( 'Foo', '5' ) ], []; }; done_testing; sub _build { CPAN::Audit::Query->new(@_) } CPAN-Audit-20250115.001/t/cli/0000755000076500000240000000000014741775321014042 5ustar brianstaffCPAN-Audit-20250115.001/t/cli/dist.t0000644000076500000240000000226314741775320015174 0ustar brianstaffuse strict; use warnings; use lib 't/lib'; use Test::More; use TestCommand; # this will be the same as ==5.024004 # CPANSA-perl-2018-6798 # CPANSA-perl-2018-6913 # CPANSA-perl-2018-6797 # CPANSA-perl-2017-12814 # CPANSA-perl-2017-12837 subtest 'command: dist perl version' => sub { my $expected_reports = 14; my( $stdout, $stderr, $exit ) = TestCommand->command( 'dist', 'perl', '5.024004' ); is $exit, 64 + $expected_reports, "there are $expected_reports reports"; }; =pod subtest 'command: dist perl ==version' => sub { my $expected_reports = 14; my( $stdout, $stderr, $exit ) = TestCommand->command( 'dist', 'perl', '==5.024004' ); unlike $stdout, qr/CPANSA-perl-2023-47(?:100|038)/, 'CVE-2023-47100 nor CVE-2023-47038 are in the reports'; is $exit, 64 + $expected_reports, "there are $expected_reports reports"; }; subtest 'command: dist perl >=version' => sub { my $expected_reports = 14; my( $stdout, $stderr, $exit ) = TestCommand->command( 'dist', 'perl', '>=5.024004' ); unlike $stdout, qr/CPANSA-perl-2023-47(?:100|038)/, 'CVE-2023-47100 nor CVE-2023-47038 are in the reports'; is $exit, 64 + $expected_reports, "there are $expected_reports reports"; }; =cut done_testing; CPAN-Audit-20250115.001/t/cli/deps.t0000644000076500000240000000073714741775320015170 0ustar brianstaffuse strict; use warnings; use lib 't/lib'; use Test::More; use TestCommand; # exclude CVE-2011-4116 explicitly. It's a known issue in File::Temp wrt symlinks. # It should be safe to use the module the way we use it though. subtest 'command: deps' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command('deps', '.', '--exclude', 'CVE-2011-4116'); like $stderr, qr/Discovered \d+ dependencies/; is "$stdout", ''; is $exit, 0; }; done_testing; CPAN-Audit-20250115.001/t/cli/release.t0000644000076500000240000000262714741775320015655 0ustar brianstaffuse strict; use warnings; use lib 't/lib'; use Test::More; use TestCommand; subtest 'command: release' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'release', 'CPAN' ); like $stdout, qr/CPANSA-CPAN-2009-01/; like $stdout, qr/CPANSA-CPAN-2020-16156/; is $stderr, ''; isnt $exit, 0; }; subtest 'command: release, with excluded result' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'release', 'CPAN', '--exclude' => 'CPANSA-CPAN-2009-01' ); unlike $stdout, qr/CPANSA-CPAN-2009-01/; like $stdout, qr/CPANSA-CPAN-2020-16156/; is $stderr, ''; isnt $exit, 0; }; subtest 'command: module, with excluded results from file' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'release', 'CPAN', '--exclude-file' => 't/data/excludes' ); unlike $stdout, qr/CPANSA-CPAN-2009-01/; like $stdout, qr/CPANSA-CPAN-2020-16156/; is $stderr, ''; isnt $exit, 0; }; subtest 'command: unknown release' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'release', 'Unknown' ); like $stderr, qr/Distribution 'Unknown' is not in database/; is $stdout, ''; isnt $exit, 0; }; subtest 'command: invalid invocation' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'release' ); like $stderr, qr/Error: Usage: /; is $stdout, ''; isnt $exit, 0; }; done_testing; CPAN-Audit-20250115.001/t/cli/modules.t0000644000076500000240000000543714741775320015707 0ustar brianstaffuse strict; use warnings; use lib 't/lib'; use Test::More; use TestCommand; subtest 'command: modules' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'modules', 'CPAN' ); like $stdout, qr/CPANSA-CPAN-2009-01/; like $stdout, qr/CPANSA-CPAN-2020-16156/; is $stderr, ''; isnt $exit, 0; }; subtest 'command: modules with two modules' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'modules', 'CPAN', 'Mojolicious;>8.40,<9.20' ); like $stdout, qr/CPANSA-CPAN-2009-01/; like $stdout, qr/CPANSA-CPAN-2020-16156/; like $stdout, qr/CPANSA-Mojolicious-2022-03/; is $stderr, ''; isnt $exit, 0; }; subtest 'command: modules, with excluded result' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'modules', 'CPAN', 'Mojolicious;>8.40,<9.20','--exclude' => 'CPANSA-CPAN-2009-01', '--exclude' => 'CPANSA-Mojolicious-2022-03' ); unlike $stdout, qr/CPANSA-CPAN-2009-01/; like $stdout, qr/CPANSA-CPAN-2020-16156/; unlike $stdout, qr/CPANSA-Mojolicious-2022-03/; is $stderr, ''; isnt $exit, 0; }; subtest 'command: modules, with excluded results from file' => sub { my $file = 't/data/modules_excludes'; ok( -e $file, 'File that should be there is there' ); my ( $stdout, $stderr, $exit ) = TestCommand->command( 'modules', 'CPAN', 'Mojolicious;>8.40,<9.20', '--exclude-file' => $file ); unlike $stdout, qr/CPANSA-CPAN-2009-01/; like $stdout, qr/CPANSA-CPAN-2020-16156/; unlike $stdout, qr/CPANSA-Mojolicious-2022-03/; is $stderr, ''; isnt $exit, 0; }; subtest 'command: modules, with excluded results from non-existent file' => sub { my $file = 't/data/not-there'; ok( ! -e $file, 'File that should not exist is not there' ); my ( $stdout, $stderr, $exit ) = TestCommand->command( 'modules', 'CPAN', 'Mojolicious;>8.40,<9.20', '--exclude-file' => $file ); like $stdout, qr/CPANSA-CPAN-2009-01/; like $stdout, qr/CPANSA-CPAN-2020-16156/; like $stdout, qr/CPANSA-Mojolicious-2022-03/; like $stderr, qr/unable to open exclude_file/; }; subtest 'command: unknown modules' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'modules', 'Unknown' ); like $stderr, qr/Module 'Unknown' is not in database/; is $stdout, ''; }; subtest 'command: unknown modules (mixed)' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'modules', 'CPAN', 'Unknown' ); like $stderr, qr/Module 'Unknown' is not in database/; like $stdout, qr/CPANSA-CPAN-2009-01/; like $stdout, qr/CPANSA-CPAN-2020-16156/; }; subtest 'command: invalid invocation' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'modules' ); is $stdout, ''; like $stderr, qr/Error: Usage: /; isnt $exit, 0; }; done_testing; CPAN-Audit-20250115.001/t/cli/show.t0000644000076500000240000000137314741775320015212 0ustar brianstaffuse strict; use warnings; use lib 't/lib'; use Test::More; use TestCommand; subtest 'command: show' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'show', 'CPANSA-Catalyst-Runtime-2013-01' ); like $stdout, qr/CPANSA-Catalyst-Runtime-2013-01/; is $stderr, ''; is $exit, 0; }; subtest 'command: show unknown advisory' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'show', 'CPANSA-UNKNOWN' ); is $stdout, ''; like $stderr, qr/Invalid advisory id/; isnt $exit, 0; }; subtest 'command: show invalid invocation' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command('show'); is $stdout, ''; like $stderr, qr/Error: Usage:/; isnt $exit, 0; }; done_testing; CPAN-Audit-20250115.001/t/cli/installed.t0000644000076500000240000000047114741775320016207 0ustar brianstaffuse strict; use warnings; use lib 't/lib'; use Test::More; use TestCommand; subtest 'command: installed' => sub { my( $stdout, $stderr, $exit ) = TestCommand->command( 'installed', 'lib' ); like $stderr, qr/Collecting all installed modules/; is $stdout, ''; is $exit, 0; }; done_testing; CPAN-Audit-20250115.001/t/cli/module.t0000644000076500000240000000365614741775320015525 0ustar brianstaffuse strict; use warnings; use lib 't/lib'; use Test::More; use TestCommand; subtest 'command: module' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'module', 'CPAN' ); like $stdout, qr/CPANSA-CPAN-2009-01/; like $stdout, qr/CPANSA-CPAN-2020-16156/; is $stderr, ''; isnt $exit, 0; }; subtest 'command: module, with excluded result' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'module', 'CPAN', '--exclude' => 'CPANSA-CPAN-2009-01' ); unlike $stdout, qr/CPANSA-CPAN-2009-01/; like $stdout, qr/CPANSA-CPAN-2020-16156/; is $stderr, ''; isnt $exit, 0; }; subtest 'command: module, with excluded results from file' => sub { my $file = 't/data/excludes'; ok( -e $file, 'File that should be there is there' ); my ( $stdout, $stderr, $exit ) = TestCommand->command( 'module', 'CPAN', '--exclude-file' => $file ); unlike $stdout, qr/CPANSA-CPAN-2009-01/; like $stdout, qr/CPANSA-CPAN-2020-16156/; is $stderr, ''; isnt $exit, 0; }; subtest 'command: module, with excluded results from non-existent file' => sub { my $file = 't/data/not-there'; ok( ! -e $file, 'File that should not exist is not there' ); my ( $stdout, $stderr, $exit ) = TestCommand->command( 'module', 'CPAN', '--exclude-file' => $file ); like $stdout, qr/CPANSA-CPAN-2009-01/; like $stdout, qr/CPANSA-CPAN-2020-16156/; like $stderr, qr/unable to open exclude_file/; isnt $exit, 0; }; use Data::Dumper; subtest 'command: unknown module' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'module', 'Unknown' ); like $stderr, qr/Module 'Unknown' is not in database/; is $stdout, ''; isnt $exit, 0; }; subtest 'command: invalid invocation' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command( 'module' ); is $stdout, ''; like $stderr, qr/Error: Usage: /; isnt $exit, 0; }; done_testing; CPAN-Audit-20250115.001/t/cli.t0000644000076500000240000000307314741775320014231 0ustar brianstaffuse strict; use warnings; use lib 't/lib'; use Test::More; use TestCommand; subtest 'help is printed' => sub { my @args = ( [], [qw(--help)] ); foreach my $args ( @args ) { subtest "help is printed with <@$args>" => sub { local $ENV{PERL5OPTS} = do { no warnings; "-w $ENV{PERL5OPTS}" }; my ( $stdout, $stderr, $exit ) = TestCommand->command(@$args); is $stdout, ''; like $stderr, qr/Usage:.*cpan-audit/ms; unlike $stderr, qr/^Argument "main" isn't numeric/m; # GitHub #41 is $exit, 2; }; } }; subtest 'version is printed' => sub { local $ENV{PERL5OPTS} = do { no warnings; "-w $ENV{PERL5OPTS}" }; my ( $stdout, $stderr, $exit ) = TestCommand->command('--version'); like $stdout, qr/cpan-audit version \d+\.\d+/; unlike $stderr, qr/^Argument "main" isn't numeric/m; # GitHub #41 is $exit, 0; }; subtest 'Github #34 - no message method' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command('installed', '--verbose'); # should exit with 64 + N, where N is the number of advisories. # there shouldn't be that many. It certainly shouldn't exit with # 255. ok( $exit >= 64 && $exit <= 126, 'installed --verbose does not have a run time fatal error' ) or diag( "exit value was <$exit>" ); }; subtest 'exit-zero option sets exitvalue to 0' => sub { my ( $stdout, $stderr, $exit ) = TestCommand->command('installed', '--exit-zero'); # should exit with 0. ok( $exit == 0, 'installed --exit-zero exits with a normal exit value (0)' ) or diag( "exit value was <$exit>" ); }; done_testing; CPAN-Audit-20250115.001/t/json.t0000644000076500000240000000505714741775320014437 0ustar brianstaffuse strict; use warnings; use lib 'lib', 't/lib'; use Capture::Tiny qw(capture); use JSON; use Test::More; my $class = "CPAN::Audit"; subtest 'setup' => sub { use_ok( $class ) or BAIL_OUT( "$class did not compile: $@" ); }; subtest 'json, corelist' => sub { my( $stdout, $stderr, $exit ) = capture { system( $^X, '-Ilib', 'script/cpan-audit', '--json', 'deps', 't/data/cpanfiles' ); }; unlike $stdout, qr/Discovered \d+/; is $stderr, ''; my $result_hash = JSON::decode_json( $stdout ); isa_ok( $result_hash, ref {} ); isa_ok( $result_hash->{meta}, ref {} ); ok( $result_hash->{meta}{total_advisories} >= 1, "found one or more advisories" ); }; subtest 'json, no corelist' => sub { my( $stdout, $stderr, $exit ) = capture { system( $^X, '-Ilib', 'script/cpan-audit', '--json', '--no-corelist', 'deps', 't/data/cpanfiles' ); }; unlike $stdout, qr/Discovered \d+/; is $stderr, ''; my $result_hash = JSON::decode_json( $stdout ); isa_ok( $result_hash, ref {} ); isa_ok( $result_hash->{meta}, ref {} ); is( $result_hash->{meta}{total_advisories}, 1, "found exactly one advisory" ); }; done_testing; BEGIN { use CPAN::Audit::DB; no warnings 'redefine'; sub CPAN::Audit::DB::db { my $db = { 'dists' => { 'Catalyst-Runtime' => { 'advisories' => [ { 'affected_versions' => '<5.90020', 'cves' => [], 'description' => 'A sample advisory for a test', 'distribution' => 'Catalyst-Runtime', 'fixed_versions' => '>=5.90020', 'id' => 'CPANSATest-Catalyst-Runtime-2013-01', 'references' => [ ], 'reported' => '2013-01-23' }, ], 'main_module' => 'Catalyst::Runtime', 'versions' => [ { 'date' => '2021-01-01T18:10:00', 'version' => '5.00', }, { 'date' => '2022-01-01T18:10:00', 'version' => '5.70', }, ], }, }, module2dist => { 'Catalyst' => 'Catalyst-Runtime', }, }; return $db; } } CPAN-Audit-20250115.001/t/installed.t0000644000076500000240000000137714741775320015446 0ustar brianstaffuse strict; use warnings; use Test::More; use CPAN::Audit::Installed; subtest 'installed' => sub { my @deps = _build( db => { module2dist => { Catalyst => 'Catalyst-Runtime' }, dists => { 'Catalyst-Runtime' => { main_module => 'Catalyst', advisories => [ { id => 'CPANSA-Catalyst-2018-01' } ] } } } )->find('t/data/installed'); is_deeply \@deps, [ { 'dist' => 'Catalyst-Runtime', 'version' => '5.0' }, ]; }; done_testing; sub _build { CPAN::Audit::Installed->new(@_) } CPAN-Audit-20250115.001/t/lib/0000755000076500000240000000000014741775321014041 5ustar brianstaffCPAN-Audit-20250115.001/t/lib/TestCommand.pm0000644000076500000240000000071414741775320016616 0ustar brianstaffpackage TestCommand; use strict; use warnings; use Capture::Tiny qw(capture); sub command { my( $class, @args ) = @_; my ( $stdout, $stderr, $rc ) = capture { system $^X, '-Ilib', 'script/cpan-audit', '--no-corelist', @args; }; my( $ran, $signal, $exit, $coredump ); $ran = $rc > -1; if( $ran ) { $exit = $rc >> 8; $coredump = $rc & 128; $signal = $rc & 127; } return ( $stdout, $stderr, $exit, $signal, $coredump, $ran ); } 1; CPAN-Audit-20250115.001/t/queried_modules.t0000644000076500000240000001051114741775320016643 0ustar brianstaffuse strict; use warnings; use lib 'lib', 't/lib'; use Capture::Tiny qw(capture); use JSON; use Test::More; my $class = "CPAN::Audit"; subtest 'setup' => sub { use_ok( $class ) or BAIL_OUT( "$class did not compile: $@" ); }; subtest 'deps queried_modules' => sub { my( $stdout, $stderr, $exit ) = capture { system( $^X, '-Ilib', 'script/cpan-audit', '--json', 'deps', 't/data/queried_modules' ); }; is $stderr, ''; my $result_hash = JSON::decode_json( $stdout ); isa_ok( $result_hash, ref {} ); isa_ok( $result_hash->{dists}, ref {} ); is_deeply( $result_hash->{dists}{'Catalyst-Runtime'}{queried_modules}, ['Catalyst'], "Queried 'Catalyst'" ); my %check = map { $_ => 1 } @{ $result_hash->{dists}{'Mojolicious'}{queried_modules} || [] }; is_deeply( \%check, { 'Mojo::File' => 1, 'Mojo::UserAgent' => 1 }, "Queried 'Mojo::File' and 'Mojo::UserAgent'" ); }; subtest 'module queried_modules Mojolicious' => sub { my( $stdout, $stderr, $exit ) = capture { system( $^X, '-Ilib', 'script/cpan-audit', '--json', 'module', 'Mojo::File' ); }; is $stderr, ''; my $result_hash = JSON::decode_json( $stdout ); isa_ok( $result_hash, ref {} ); isa_ok( $result_hash->{dists}, ref {} ); is( $result_hash->{dists}{'Catalyst-Runtime'}{queried_modules}, undef, "Did not query 'Catalyst'" ); is_deeply( $result_hash->{dists}{'Mojolicious'}{queried_modules}, ['Mojo::File'], "Queried 'Mojo::File'" ); }; subtest 'module queried_modulesi Catalyst' => sub { my( $stdout, $stderr, $exit ) = capture { system( $^X, '-Ilib', 'script/cpan-audit', '--json', 'module', 'Catalyst' ); }; is $stderr, ''; my $result_hash = JSON::decode_json( $stdout ); isa_ok( $result_hash, ref {} ); isa_ok( $result_hash->{dists}, ref {} ); is_deeply( $result_hash->{dists}{'Catalyst-Runtime'}{queried_modules}, ['Catalyst'], "Queried 'Catalyst'" ); is( $result_hash->{dists}{'Mojolicious'}{queried_modules}, undef, "Did not query 'Mojo::File' and 'Mojo::UserAgent'" ); }; subtest 'modules queried_modules' => sub { my( $stdout, $stderr, $exit ) = capture { system( $^X, '-Ilib', 'script/cpan-audit', '--json', 'modules', 'Catalyst', 'Mojo::File', 'Mojo::UserAgent' ); }; is $stderr, ''; my $result_hash = JSON::decode_json( $stdout ); isa_ok( $result_hash, ref {} ); isa_ok( $result_hash->{dists}, ref {} ); is_deeply( $result_hash->{dists}{'Catalyst-Runtime'}{queried_modules}, ['Catalyst'], "Queried 'Catalyst'" ); my %check = map { $_ => 1 } @{ $result_hash->{dists}{'Mojolicious'}{queried_modules} || [] }; is_deeply( \%check, { 'Mojo::File' => 1, 'Mojo::UserAgent' => 1 }, "Queried 'Mojo::File' and 'Mojo::UserAgent'" ); }; done_testing; BEGIN { use CPAN::Audit::DB; no warnings 'redefine'; sub CPAN::Audit::DB::db { my $db = { 'dists' => { 'Catalyst-Runtime' => { 'advisories' => [ { 'affected_versions' => '<5.90020', 'cves' => [], 'description' => 'A sample advisory for a test', 'distribution' => 'Catalyst-Runtime', 'fixed_versions' => '>=5.90020', 'id' => 'CPANSATest-Catalyst-Runtime-2013-01', 'references' => [ ], 'reported' => '2013-01-23' }, ], 'main_module' => 'Catalyst::Runtime', 'versions' => [ { 'date' => '2021-01-01T18:10:00', 'version' => '5.00', }, { 'date' => '2022-01-01T18:10:00', 'version' => '5.70', }, ], }, 'Mojolicious' => { 'advisories' => [ id => 1, fixed_versions => '>=8', ] } }, module2dist => { 'Catalyst' => 'Catalyst-Runtime', 'Mojo::File' => 'Mojolicious', 'Mojo::UserAgent' => 'Mojolicious', }, }; return $db; } } CPAN-Audit-20250115.001/t/excludes.t0000644000076500000240000000250214741775320015272 0ustar brianstaffuse v5.10; use Test::More; my $class = 'CPAN::Audit::Filter'; my @class_methods = qw(new); my @instance_methods = qw(excludes ignored_count); subtest sanity => sub { use_ok( $class ); can_ok( $class, @class_methods ); }; subtest 'no args' => sub { my $filter = $class->new; isa_ok( $filter, $class ); can_ok( $filter, @instance_methods ); }; subtest 'one args' => sub { my $warning; local $SIG{__WARN__} = sub { $warning .= $_[0] }; my $filter = $class->new( 'excludes' ); # diag( "Warning was <$warning>" ); like( $warning, qr/Odd number/, 'Odd number of elements warns' ); isa_ok( $filter, $class ); can_ok( $filter, @instance_methods ); }; subtest 'two args' => sub { my $id = 'Some-Package-2022-001'; my $filter = $class->new( exclude => [ $id ] ); isa_ok( $filter, $class ); can_ok( $filter, @instance_methods ); subtest 'nothing to ignore' => sub { my $rc = $filter->excludes( { id => 'xyz' } ); ok( ! $rc, 'excludes returns false when it does not exclude' ); is( $filter->ignored_count, 0, 'ignored_count returns 0 when it does not exclude' ); }; subtest 'something to ignore' => sub { my $rc = $filter->excludes( { id => $id } ); ok( $rc, 'excludes returns true when it does exclude' ); is( $filter->ignored_count, 1, 'ignored_count returns 1 when it does exclude' ); }; }; done_testing(); CPAN-Audit-20250115.001/t/test_manifest0000644000076500000240000000030514741775320016060 0ustar brianstaff./discover/cpanfile.t ./discover/cpanfile_snapshot.t ./json.t ./version.t ./query.t ./cli/deps.t ./cli/installed.t ./cli/module.t ./cli/modules.t ./cli/release.t ./cli/show.t ./cli.t ./installed.t CPAN-Audit-20250115.001/t/data/0000755000076500000240000000000014741775321014204 5ustar brianstaffCPAN-Audit-20250115.001/t/data/installed/0000755000076500000240000000000014741775321016163 5ustar brianstaffCPAN-Audit-20250115.001/t/data/installed/perl5/0000755000076500000240000000000014741775321017212 5ustar brianstaffCPAN-Audit-20250115.001/t/data/installed/perl5/lib/0000755000076500000240000000000014741775321017760 5ustar brianstaffCPAN-Audit-20250115.001/t/data/installed/perl5/lib/perl5/0000755000076500000240000000000014741775321021007 5ustar brianstaffCPAN-Audit-20250115.001/t/data/installed/perl5/lib/perl5/Catalyst.pm0000644000076500000240000000014414741775320023127 0ustar brianstaff=pod =head1 NAME Catalyst =cut package Catalyst ; our $VERSION = '5.0'; # No BumpVersion CPAN-Audit-20250115.001/t/data/carton/0000755000076500000240000000000014741775321015472 5ustar brianstaffCPAN-Audit-20250115.001/t/data/carton/cpanfile.snapshot0000644000076500000240000000116214741775320021033 0ustar brianstaff# carton snapshot format: version 1.0 DISTRIBUTIONS Apache-LogFormat-Compiler-0.35 pathname: K/KA/KAZEBURO/Apache-LogFormat-Compiler-0.35.tar.gz provides: Apache::LogFormat::Compiler 0.35 requirements: Module::Build::Tiny 0.035 POSIX 0 POSIX::strftime::Compiler 0.30 Time::Local 0 perl 5.008001 Class-Inspector-1.32 pathname: P/PL/PLICEASE/Class-Inspector-1.32.tar.gz provides: Class::Inspector 1.32 Class::Inspector::Functions 1.32 requirements: ExtUtils::MakeMaker 0 File::Spec 0.80 perl 5.006 Invalid-1.0 pathname: Invalid CPAN-Audit-20250115.001/t/data/cpanfiles/0000755000076500000240000000000014741775321016150 5ustar brianstaffCPAN-Audit-20250115.001/t/data/cpanfiles/cpanfile0000644000076500000240000000006614741775320017655 0ustar brianstaffrequires 'perl', '5.8'; requires 'Catalyst', '5'; CPAN-Audit-20250115.001/t/data/excludes0000644000076500000240000000023614741775320015743 0ustar brianstaff# Comments are ignored # as well as blank lines (previous line) CPANSA-CPAN-2009-01 # trailing comments are removed, as is leading/trailing whitespace CPAN-Audit-20250115.001/t/data/modules_excludes0000644000076500000240000000027114741775320017472 0ustar brianstaff# Comments are ignored # as well as blank lines (previous line) CPANSA-CPAN-2009-01 # trailing comments are removed, as is leading/trailing whitespace CPANSA-Mojolicious-2022-03 CPAN-Audit-20250115.001/t/data/queried_modules/0000755000076500000240000000000014741775321017372 5ustar brianstaffCPAN-Audit-20250115.001/t/data/queried_modules/cpanfile0000644000076500000240000000011114741775320021066 0ustar brianstaffrequires 'Mojo::File'; requires 'Mojo::UserAgent'; requires 'Catalyst'; CPAN-Audit-20250115.001/script/0000755000076500000240000000000014741775321014334 5ustar brianstaffCPAN-Audit-20250115.001/script/cpan-audit0000755000076500000240000004421014741775320016307 0ustar brianstaff#!/usr/bin/env perl use v5.10; use strict; use warnings; use IO::Interactive qw(is_interactive); use CPAN::Audit; our $VERSION = "1.503"; __PACKAGE__->run( @ARGV ) unless caller; # The exit code indicates the number of advisories, up to this max # since we have a limited number of exit codes. use constant ADVISORY_COUNT_MAX => 62; use constant EXIT_NORMAL => 0; use constant EXIT_ZERO => 0; use constant EXIT_USAGE => 2; use constant EXIT_BASE => 64; my $output_table; BEGIN { $output_table = { text => \&format_text, dumper => \&format_dump, json => \&format_json, default => \&format_text, }; } sub format_advisory { my ($advisory) = @_; my $s = " __BOLD__* $advisory->{id}__RESET__\n"; $s .= " $advisory->{description}\n"; if ( $advisory->{affected_versions} ) { my @v = ref $advisory->{affected_versions} ? @{$advisory->{affected_versions}} : $advisory->{affected_versions}; my $first = shift @v; $s .= " Affected range: $first\n"; $s .= " $_\n" for @v; } if ( $advisory->{fixed_versions} ) { my @v = ref $advisory->{fixed_versions} ? @{$advisory->{fixed_versions}} : $advisory->{fixed_versions}; my $first = shift @v; $first //= ''; $s .= " Fixed range: $first\n"; $s .= " $_\n" for @v; } if ( $advisory->{cves} ) { $s .= "\n CVEs: "; $s .= join ', ', @{ $advisory->{cves} }; $s .= "\n"; } if ( $advisory->{references} ) { $s .= "\n References:\n"; foreach my $reference ( @{ $advisory->{references} || [] } ) { $s .= " $reference\n"; } } $s .= "\n"; return $s; } use Data::Dumper; sub dumper { Data::Dumper->new([@_])->Indent(1)->Sortkeys(1)->Terse(1)->Useqq(1)->Dump } sub format_dump { my( $result ) = @_; return dumper($result); } sub format_json { state $rc = require JSON; my( $result ) = @_; return JSON::encode_json($result); } sub format_text { my( $result, $opts ) = @_; my $s = ''; foreach my $distname ( keys %{ $result->{dists} } ) { my $advisories = $result->{dists}{$distname}{advisories}; $s .= sprintf("__RED__%s (%s %s) has %d advisor%s__RESET__\n", $distname, ($result->{meta}{command} eq 'installed' ? 'have' : 'requires'), $result->{dists}{$distname}{version}, scalar(@$advisories), (scalar(@$advisories) == 1 ? 'y' : 'ies'), ); foreach my $advisory ( @$advisories ) { $s .= format_advisory( $advisory ); } } $s .= "\n" if length $s; if ( $opts->{'no-color'} or $opts->{'ascii'} ) { $s =~ s{__BOLD__}{}g; $s =~ s{__GREEN__}{}g; $s =~ s{__RED__}{}g; $s =~ s{__RESET__}{}g; } else { $s =~ s{__BOLD__}{\e[39;1m}g; $s =~ s{__GREEN__}{\e[32m}g; $s =~ s{__RED__}{\e[31m}g; $s =~ s{__RESET__}{\e[0m}g; $s .= "\e[0m" if length $s; } return $s; } sub output_version { my( $class, $exit_code ) = @_; print <<"HERE"; $0 version $VERSION using: \tCPAN::Audit @{[ CPAN::Audit->VERSION ]} \tCPAN::Audit::DB @{[ CPAN::Audit::DB->VERSION // '' ]} \tCPANSA::DB @{[ ( eval { require CPANSA::DB } && CPANSA::DB->VERSION) // '' ]} HERE exit($exit_code); } sub run { my( $class, @args ) = @_; my( $opts ) = $class->process_options( \@args ); unless( ! $opts->{interactive} ) { $opts->{ascii} = 1; $opts->{no_color} //= 1; } $class->usage(EXIT_NORMAL) if $opts->{help}; $class->output_version(EXIT_NORMAL) if $opts->{version}; if( $opts->{fresh_check} ) { require CPAN::Audit::FreshnessCheck; CPAN::Audit::FreshnessCheck->import } my $command = shift @args; $class->usage(EXIT_USAGE) unless defined $command; my %extra = ( interactive => is_interactive(), ); my $audit = CPAN::Audit->new( %$opts, %extra ); my $result = $audit->command( $command, @args ); if( @{ $result->{errors} } > 0 ) { my $message = join "\n", map "Error: $_", @{ $result->{errors} }; unless( $opts->{'no-color'} ) { $message = "\e[31m" . $message . "\e[0m" } print STDERR $message; exit 255; } my( $output_type ) = grep { $opts->{$_} } qw(json); my $sub = $output_table->{$output_type // 'default'}; my $output = $sub->( $result, $opts ); if( $command eq 'show' ) { $output =~ s/\A.*\n//; } print $output; my $advisory_count = $result->{meta}{total_advisories}; $advisory_count = ADVISORY_COUNT_MAX if $advisory_count > ADVISORY_COUNT_MAX; my $exit_code = do { if( $opts->{exit_zero} ) { EXIT_ZERO } elsif( $advisory_count == 0 ) { EXIT_NORMAL } else { EXIT_BASE + $advisory_count } }; exit( $exit_code ); } sub process_options { my( $class, $args ) = @_; require Getopt::Long; my $options = {}; my %params = (); my $params = { 'ascii' => \$params{ascii}, 'f|fresh' => \$params{fresh_check}, 'help|h' => \$params{help}, 'json' => \$params{json}, 'no-color' => \$params{no_color}, 'no-corelist' => \$params{no_corelist}, 'perl' => \$params{include_perl}, 'quiet|q' => \$params{quiet}, 'verbose|v' => \$params{verbose}, 'version' => \$params{version}, 'exclude=s@' => \$params{exclude}, 'exclude-file=s@' => \$params{exclude_file}, 'modules=s@' => \$params{modules}, 'exit-zero' => \$params{exit_zero}, }; my $ret = Getopt::Long::GetOptionsFromArray( $args, $options, %$params ) or $class->usage(EXIT_USAGE); $params{quiet} = 1 if $params{json}; \%params; } sub usage { require Pod::Usage; require FindBin; my( $class, $exit_code ) = @_; no warnings qw(once); Pod::Usage::pod2usage( -input => $FindBin::Bin . "/" . $FindBin::Script ); print <<'HERE'; NAME cpan-audit - Audit CPAN modules SYNOPSIS cpan-audit [command] [options] Commands: module [version range] audit module with optional version range (all by default) modules [version range] audit module list with optional version range (all by default) dist|release [version range] audit distribution with optional version range (all by default) deps [directory] audit dependencies from the directory (. by default) installed audit all installed modules show [advisory id] show information about specific advisory Options: --ascii use ascii output --fresh|f check the database for freshness (CPAN::Audit::FreshnessCheck) --help|h show the help message and exit --no-color switch off colors --no-corelist ignore modules bundled with perl version --perl include perl advisories --quiet be quiet (overrules --verbose) --verbose be verbose (off if --quiet in effect) --version show the version and exit --exit-zero always exit with 0 even if advisories are reported --exclude exclude/ignore the specified advisory/cve (multiple) --exclude-file read exclude/ignore patterns from file --json output JSON Examples: cpan-audit dist Catalyst-Runtime cpan-audit dist Catalyst-Runtime 7.0 cpan-audit dist Catalyst-Runtime '>5.48' cpan-audit module Catalyst 7.0 cpan-audit modules "Catalyst;7.0" "Mojolicious;>8.40,<9.20" cpan-audit deps . cpan-audit deps /path/to/distribution cpan-audit installed cpan-audit installed local/ cpan-audit installed local/ --exclude CVE-2011-4116 cpan-audit installed local/ --exclude CVE-2011-4116 --exclude CVE-2011-123 cpan-audit installed local/ --exclude-file ignored-cves.txt cpan-audit installed --json cpan-audit installed --json --exit-zero cpan-audit show CPANSA-Mojolicious-2018-03 DESCRIPTION "cpan-audit" is a command line application that checks the modules or distributions for known vulnerabilities. It is using its internal database that is automatically generated from a hand-picked database . "cpan-audit" does not connect to anything, that is why it is important to keep it up to date. Every update of the internal database is released as a new version. Ensure that you have the latest database by updating CPAN::Audit frequently; the database can change daily. You can use enable a warning for a possibly out-of-date database by adding "--fresh", which warns if the database version is older than a month: % cpan-audit --fresh ... % cpan-audit -f ... % env CPAN_AUDIT_FRESH_DAYS=7 cpan-audit -f ... Finding dependencies "cpan-audit" can automatically detect dependencies from the following sources: "Carton" Parses cpanfile.snapshot file and checks the distribution versions. cpanfile Parses cpanfile taking into account the required versions. It is assumed that if the required version of the module is less than a version of a release with a known vulnerability fix, then the module is considered affected. JSON data If you request JSON output, the data looks like { "meta" : { ... meta information ... "dists": { "": { ... distribution info ... } } "errors" : [ ... list of errors - if any ... ] } Meta information The meta data contains information about the run of "cpan-audit". { "args": [ "Mojo::File", "Mojo::UserAgent", "LWP::UserAgent" ], "cpan_audit": { "version": "20230601.002" }, "total_advisories": 19, "command": "modules" } These information are shown * cpan_audit The version of "cpan_audit" that is used for the audit * command The command of "cpan_audit" that was run * args Arguments for the command * total_advisories Number of found advisories Distribution information For each distribution where at least one advisory was found, the JSON looks like: "Dist-Name": { "queried_modules": [ "Queried::Namespace" ], "version": "Any", "advisories": [ { ... advisory data as in the audit database ... }, ... more advisories ... ] }, The advisory data is basically the data from the database. So this depends on what is known for the given advisory. The distribution information contains: * version The version (range) that is checked for advisories. If there's no version specified, all versions are checked and the version is report as "Any". * queried_modules The actual namespaces queried, either from the command line or another source, such as a cpanfile. * advisories A list of all vulnerabilities found for the version range Exit values In prior versions, "cpan-audit" exited with the number of advisories it found. Starting with 1.001, if there are advisories found, "cpan-audit" exits with 64 added to that number. The maximum number of reported advisories is 62, since values over 126 are spoken for. If the option "--exit-zero" is set "cpan-audit" exits always with a normal exit code (0). This allows to use "cpan-audit" in build environments together with bash exit mode activated ("set -e"). * 0 - no advisories found * 2 - problem with program invocation, such as bad switches or values * 64+n - advisories found. Subtract 64 to get the advisory count, up to 62 advisories * 255 - unspecified program error LICENSE Copyright (C) Viacheslav Tykhanovskyi. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.HERE HERE exit( $exit_code ); } __END__ =head1 NAME cpan-audit - Audit CPAN modules =head1 SYNOPSIS cpan-audit [command] [options] Commands: module [version range] audit module with optional version range (all by default) modules [version range] audit module list with optional version range (all by default) dist|release [version range] audit distribution with optional version range (all by default) deps [directory] audit dependencies from the directory (. by default) installed audit all installed modules show [advisory id] show information about specific advisory Options: --ascii use ascii output --fresh|f check the database for freshness (CPAN::Audit::FreshnessCheck) --help|h show the help message and exit --no-color switch off colors --no-corelist ignore modules bundled with perl version --perl include perl advisories --quiet be quiet (overrules --verbose) --verbose be verbose (off if --quiet in effect) --version show the version and exit --exit-zero always exit with 0 even if advisories are reported --exclude exclude/ignore the specified advisory/cve (multiple) --exclude-file read exclude/ignore patterns from file --json output JSON Examples: cpan-audit dist Catalyst-Runtime cpan-audit dist Catalyst-Runtime 7.0 cpan-audit dist Catalyst-Runtime '>5.48' cpan-audit module Catalyst 7.0 cpan-audit modules "Catalyst;7.0" "Mojolicious;>8.40,<9.20" cpan-audit deps . cpan-audit deps /path/to/distribution cpan-audit installed cpan-audit installed local/ cpan-audit installed local/ --exclude CVE-2011-4116 cpan-audit installed local/ --exclude CVE-2011-4116 --exclude CVE-2011-123 cpan-audit installed local/ --exclude-file ignored-cves.txt cpan-audit installed --json cpan-audit installed --json --exit-zero cpan-audit show CPANSA-Mojolicious-2018-03 =head1 DESCRIPTION C is a command line application that checks the modules or distributions for known vulnerabilities. It is using its internal database that is automatically generated from a hand-picked database L. C does not connect to anything, that is why it is important to keep it up to date. Every update of the internal database is released as a new version. Ensure that you have the latest database by updating L frequently; the database can change daily. You can use enable a warning for a possibly out-of-date database by adding C<--fresh>, which warns if the database version is older than a month: % cpan-audit --fresh ... % cpan-audit -f ... % env CPAN_AUDIT_FRESH_DAYS=7 cpan-audit -f ... =head2 Finding dependencies C can automatically detect dependencies from the following sources: =over =item C Parses F file and checks the distribution versions. =item F Parses F taking into account the required versions. =back It is assumed that if the required version of the module is less than a version of a release with a known vulnerability fix, then the module is considered affected. =head2 JSON data If you request JSON output, the data looks like: { "meta" : { ... meta information ... "dists": { "": { ... distribution info ... } } "errors" : [ ... list of errors - if any ... ] } =head3 Meta information The meta data contains information about the run of C. { "args": [ "Mojo::File", "Mojo::UserAgent", "LWP::UserAgent" ], "cpan_audit": { "version": "20230601.002" }, "total_advisories": 19, "command": "modules" } These information are shown: =over 4 =item * cpan_audit The version of C that is used for the audit =item * command The command of C that was run =item * args Arguments for the command =item * total_advisories Number of found advisories =back =head3 Distribution information For each distribution where at least one advisory was found, the JSON looks like: "Dist-Name": { "queried_modules": [ "Queried::Namespace" ], "version": "Any", "advisories": [ { ... advisory data as in the audit database ... }, ... more advisories ... ] }, The advisory data is basically the data from the database. So this depends on what is known for the given advisory. The distribution information contains: =over 4 =item * version The version (range) that is checked for advisories. If there's no version specified, all versions are checked and the version is report as "Any". =item * queried_modules The actual namespaces queried, either from the command line or another source, such as a F. =item * advisories A list of all vulnerabilities found for the version range =back =head2 Exit values In prior versions, C exited with the number of advisories it found. Starting with 1.001, if there are advisories found, C exits with 64 added to that number. The maximum number of reported advisories is 62, since values over 126 are spoken for. If the option C<--exit-zero> is set C exits always with a normal exit code (0). This allows you to use C in build environments together with bash exit mode activated (C). =over 4 =item * 0 - no advisories found =item * 2 - problem with program invocation, such as bad switches or values =item * 64+n - advisories found. Subtract 64 to get the advisory count, up to 62 advisories =item * 255 - unspecified program error =back =head1 LICENSE Copyright (C) Viacheslav Tykhanovskyi. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut CPAN-Audit-20250115.001/README.md0000644000076500000240000000715214741775320014313 0ustar brianstaff# NAME cpan-audit - Audit CPAN modules # SYNOPSIS cpan-audit \[command\] \[options\] Commands: module [version range] audit module with optional version range (all by default) dist|release [version range] audit distribution with optional version range (all by default) deps [directory] audit dependencies from the directory (. by default) installed audit all installed modules show [advisory id] show information about specific advisory Options: --ascii use ascii output --freshcheck|f check the database for freshness (CPAN::Audit::FreshnessCheck) --help|h show the help message and exit --no-color switch off colors --no-corelist ignore modules bundled with perl version --perl include perl advisories --quiet be quiet --verbose be verbose --version show the version and exit --exclude exclude/ignore the specified advisory/cve (multiple) --exclude-file read exclude/ignore patterns from file --json save audit results in JSON format in a file Examples: cpan-audit dist Catalyst-Runtime cpan-audit dist Catalyst-Runtime 7.0 cpan-audit dist Catalyst-Runtime '>5.48' cpan-audit module Catalyst 7.0 cpan-audit deps . cpan-audit deps /path/to/distribution cpan-audit installed cpan-audit installed local/ cpan-audit installed local/ --exclude CVE-2011-4116 cpan-audit installed local/ --exclude CVE-2011-4116 --exclude CVE-2011-123 cpan-audit installed local/ --exclude-file ignored-cves.txt cpan-audit installed --json audit.json cpan-audit show CPANSA-Mojolicious-2018-03 # DESCRIPTION `cpan-audit` is a command line application that checks the modules or distributions for known vulnerabilities. It is using its internal database that is automatically generated from a hand-picked database [https://github.com/briandfoy/cpan-security-advisory](https://github.com/briandfoy/cpan-security-advisory). `cpan-audit` does not connect to anything, that is why it is important to keep it up to date. Every update of the internal database is released as a new version. Ensure that you have the latest database by updating [CPAN::Audit](https://metacpan.org/pod/CPAN%3A%3AAudit) frequently; the database can change daily. You can use enable a warning for a possibly out-of-date database by adding `--freshcheck`, which warns if the database version is older than a month: % cpan-audit --freshcheck ... % cpan-audit -f ... % env CPAN_AUDIT_FRESH_DAYS=7 cpan-audit -f ... ## Finding dependencies `cpan-audit` can automatically detect dependencies from the following sources: - `Carton` Parses `cpanfile.snapshot` file and checks the distribution versions. - `cpanfile` Parses `cpanfile` taking into account the required versions. It is assumed that if the required version of the module is less than a version of a release with a known vulnerability fix, then the module is considered affected. ## Exit values In prior versions, `cpan-audit` exited with the number of advisories it found. Starting with 1.001, if there are advisories found, `cpan-audit` exits with 64 added to that number. - 0 - normal operation - 2 - problem with program invocation, such as bad switches or values - 64+n - advisories found. Subtract 64 to get the advisory count # LICENSE Copyright (C) Viacheslav Tykhanovskyi. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. CPAN-Audit-20250115.001/MANIFEST.SKIP0000644000076500000240000000227314741775320014731 0ustar brianstaff #!start included /usr/local/perls/perl-5.18.1/lib/5.18.1/ExtUtils/MANIFEST.SKIP # Avoid version control files. \bRCS\b \bCVS\b \bSCCS\b ,v$ \B\.svn\b \B\.git\b \B\.gitignore\b \b_darcs\b \B\.cvsignore$ # Avoid VMS specific MakeMaker generated files \bDescrip.MMS$ \bDESCRIP.MMS$ \bdescrip.mms$ # Avoid Makemaker generated and utility files. \bMANIFEST\.bak \bMakefile$ \bblib/ \bMakeMaker-\d \bpm_to_blib\.ts$ \bpm_to_blib$ \bblibdirs\.ts$ # 6.18 through 6.25 generated this # Avoid Module::Build generated and utility files. \bBuild$ \b_build/ \bBuild.bat$ \bBuild.COM$ \bBUILD.COM$ \bbuild.com$ # Avoid temp and backup files. ~$ \.old$ \#$ \b\.# \.bak$ \.tmp$ \.# \.rej$ # Avoid OS-specific files/dirs # Mac OSX metadata \B\.DS_Store # Mac OSX SMB mount metadata files \B\._ # Avoid Devel::Cover and Devel::CoverX::Covered files. \bcover_db\b \bcovered\b # Avoid MYMETA files ^MYMETA\. #!end included /usr/local/perls/perl-5.18.1/lib/5.18.1/ExtUtils/MANIFEST.SKIP \.?appveyor.yml \.releaserc \.lwpcookies Test-Manifest-.* hacks/ \bMANIFEST\s\d \bChanges\s\d \.icloud$ \A\.github\b .perltidyrc .proverc Build.PL minil.toml CPAN-Audit-2* .gitmodules cpan-security-advisory/ \.gitattributes\b CPAN-Audit-20250115.001/CONTRIBUTING.md0000644000076500000240000000062214741775320015260 0ustar brianstaff# Contributing If you have any questions, no matter how big or small, [raise an issue](https://github.com/briandfoy/cpan-audit). ## Updating the advisories The advisories are actually in a separate GitHub repo, [briandfoy/cpan-security-advisory](https://github.com/briandfoy/cpan-security-advisory) that's a submodule of this repo. Follow the instructions for that repo to add or update advisories. CPAN-Audit-20250115.001/META.yml0000664000076500000240000000204214741775321014301 0ustar brianstaff--- abstract: 'Audit CPAN distributions for known vulnerabilities' author: - 'Viacheslav Tykhanovskyi ' build_requires: Capture::Tiny: '0' File::Temp: '0' HTTP::Tiny: '0' Test::More: '0.98' configure_requires: ExtUtils::MakeMaker: '6.64' File::Spec::Functions: '0' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: CPAN-Audit no_index: directory: - t - inc requires: CPAN::DistnameInfo: '0' CPANSA::DB: '20241121.001' IO::Interactive: '0' JSON: '0' Module::CPANfile: '0' Module::CoreList: '5.20181020' Module::Extract::VERSION: '0' PerlIO::gzip: '0' perl: '5.020' resources: bugtracker: https://github.com/briandfoy/cpan-audit/issues homepage: https://github.com/briandfoy/cpan-audit repository: https://github.com/briandfoy/cpan-audit version: '20250115.001' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' CPAN-Audit-20250115.001/lib/0000755000076500000240000000000014741775321013576 5ustar brianstaffCPAN-Audit-20250115.001/lib/CPAN/0000755000076500000240000000000014741775321014317 5ustar brianstaffCPAN-Audit-20250115.001/lib/CPAN/Audit.pm0000644000076500000240000002002114741775320015715 0ustar brianstaffpackage CPAN::Audit; use v5.10.1; use strict; use warnings; use version; use Carp qw(carp); use Module::CoreList; use CPAN::Audit::Installed; use CPAN::Audit::Discover; use CPAN::Audit::Filter; use CPAN::Audit::Version; use CPAN::Audit::Query; use CPANSA::DB; our $VERSION = '20250115.001'; sub new { my( $class, %params ) = @_; my @allowed_keys = qw(ascii db exclude exclude_file include_perl interactive no_corelist quiet verbose version); my %args = map { $_, $params{$_} } @allowed_keys; my $self = bless \%args, $class; $self->_handle_exclude_file if $self->{exclude_file}; $self->{db} //= $self->_get_db(%args); $self->{filter} = CPAN::Audit::Filter->new( exclude => $args{exclude} ); $self->{query} = CPAN::Audit::Query->new( db => $self->{db} ); $self->{discover} = CPAN::Audit::Discover->new( db => $self->{db} ); return $self; } sub _get_db { my( $self, %params ) = @_; if ( $params{'json_db'} ) { my $data = do { local $/; open my($fh), '<:raw', $params{'json_db'} or die "could not read file <$params{json_db}>\n"; <$fh>; }; state $rc = require JSON; my $decoded = eval { JSON::decode_json($data) }; die "could not decode JSON from <$params{json_db}>: @_\n" unless defined $decoded; return $decoded; } my $rc = eval { require CPANSA::DB }; if ( $rc ) { return CPANSA::DB->db; } $rc = eval { require CPAN::Audit::DB }; if ( $rc ) { return CPAN::Audit::DB->db; } die "could not find a CPANSA database in CPANSA::DB or CPAN::Audit::DB\n"; } sub _handle_exclude_file { my( $self ) = @_; foreach my $file (@{$self->{exclude_file}}) { my $fh; unless( open $fh, "<", $file ) { carp "unable to open exclude_file [$file]: $!\n"; return; } my @excludes = grep { !/^\s*$/ } # no blank lines map { s{^\s+|\s+$}{}g; $_ } # strip leading/trailing whitespace map { s{#.*}{}; $_ } # strip comments <$fh>; push @{$self->{exclude}}, @excludes; } } sub command_module { my ( $self, $dists, $queried, $module, $version_range ) = @_; return "Usage: module [version-range]" unless $module; my $distname = $self->{db}->{module2dist}->{$module}; if ( !$distname ) { return "Module '$module' is not in database"; } push @{ $queried->{$distname} }, $module; $dists->{$distname} = $version_range // ''; return; } sub command_release { my ( $self, $dists, $queried, $distname, $version_range ) = @_; return "Usage: dist|release [version-range]" unless $distname; if ( !$self->{db}->{dists}->{$distname} ) { return "Distribution '$distname' is not in database"; } $dists->{$distname} = $version_range // ''; return; } sub command_show { my ( $self, $dists, $queried, $advisory_id ) = @_; return "Usage: show " unless $advisory_id; my ($release) = $advisory_id =~ m/^CPANSA-(.*?)-(\d+)-(\d+)$/; return "Invalid advisory id" unless $release; my $dist = $self->{db}->{dists}->{$release}; return "Unknown advisory id" unless $dist; my ($advisory) = grep { $_->{id} eq $advisory_id } @{ $dist->{advisories} }; return "Unknown advisory id" unless $advisory; my $distname = $advisory->{distribution} // 'Unknown distribution name'; $dists->{$distname}{advisories} = [ $advisory ]; $dists->{$distname}{version} = 'Any'; return; } sub command_modules { my ($self, $dists, $queried, @modules) = @_; return "Usage: modules '[;version-range]' '[;version-range]'" unless @modules; foreach my $module ( @modules ) { my ($name, $version) = split /;/, $module; my $failed = $self->command_module( $dists, $queried, $name, $version // '' ); if ( $failed ) { $self->verbose( $failed ); next; } } return; } sub command_deps { my ($self, $dists, $queried, $dir) = @_; $dir = '.' unless defined $dir; return "Usage: deps " unless -d $dir; my @deps = $self->{discover}->discover($dir); $self->verbose( sprintf 'Discovered %d dependencies', scalar(@deps) ); foreach my $dep (@deps) { my $dist = $dep->{dist} || $self->{db}->{module2dist}->{ $dep->{module} }; next unless $dist; push @{ $queried->{$dist} }, $dep->{module} if !$dep->{dist}; $dists->{$dist} = $dep->{version}; } return; } sub command_installed { my ($self, $dists, $queried, @args) = @_; $self->verbose('Collecting all installed modules. This can take a while...'); my $verbose_callback = sub { my ($info) = @_; $self->verbose( sprintf '%s: %s-%s', $info->{path}, $info->{distname}, $info->{version} ); }; my @deps = CPAN::Audit::Installed->new( db => $self->{db}, include_perl => $self->{include_perl}, ( $self->{verbose} ? ( cb => $verbose_callback ) : () ), )->find(@args); foreach my $dep (@deps) { my $dist = $dep->{dist} || $self->{db}->{module2dist}->{ $dep->{module} }; next unless $dist; $dists->{ $dep->{dist} } = '==' . $dep->{version}; } return; } sub command { state $command_table = { dependencies => 'command_deps', deps => 'command_deps', installed => 'command_installed', module => 'command_module', modules => 'command_modules', release => 'command_release', dist => 'command_release', show => 'command_show', }; my( $self, $command, @args ) = @_; my %report = ( meta => { command => $command, args => [ @args ], cpan_audit => { version => $VERSION }, total_advisories => 0, }, errors => [], dists => {}, ); my $dists = $report{dists}; my $queried = {}; if (!$self->{no_corelist} && ( $command eq 'dependencies' || $command eq 'deps' || $command eq 'installed' ) ) { # Find core modules for this perl version first. # This way explictly installed versions will overwrite. if ( my $core = $Module::CoreList::version{$]} ) { while ( my ( $mod, $ver ) = each %$core ) { my $dist = $self->{db}{module2dist}{$mod} or next; $dists->{$dist} = $ver if( ! defined $dists->{$dist} or version->parse($ver) > $dists->{$dist} ); } } } if ( exists $command_table->{$command} ) { my $method = $command_table->{$command}; push @{ $report{errors} }, $self->$method( $dists, $queried, @args ); return \%report if $command eq 'show'; } else { push @{ $report{errors} }, "unknown command: $command. See -h"; } if (%$dists) { my $query = $self->{query}; foreach my $distname ( keys %$dists ) { my $version_range = $dists->{$distname}; my @advisories = grep { ! $self->{filter}->excludes($_) } $query->advisories_for( $distname, $version_range ); $version_range = 'Any' if $version_range eq '' || $version_range eq '0'; $report{meta}{total_advisories} += @advisories; if ( @advisories ) { $dists->{$distname} = { advisories => \@advisories, version => $version_range, queried_modules => $queried->{$distname} || [], }; } else { delete $dists->{$distname} } } } return \%report; } sub verbose { my ( $self, $message ) = @_; return if $self->{quiet}; $self->_print( *STDERR, $message ); } sub _print { my ( $self, $fh, $message ) = @_; if ( $self->{no_color} ) { $message =~ s{__BOLD__}{}g; $message =~ s{__GREEN__}{}g; $message =~ s{__RED__}{}g; $message =~ s{__RESET__}{}g; } else { $message =~ s{__BOLD__}{\e[39;1m}g; $message =~ s{__GREEN__}{\e[32m}g; $message =~ s{__RED__}{\e[31m}g; $message =~ s{__RESET__}{\e[0m}g; $message .= "\e[0m" if length $message; } print $fh "$message\n"; } 1; __END__ =encoding utf8 =head1 NAME CPAN::Audit - Audit CPAN distributions for known vulnerabilities =head1 SYNOPSIS use CPAN::Audit; =head1 DESCRIPTION CPAN::Audit is a module and a database at the same time. It is used by L command line application to query for vulnerabilities. =head1 LICENSE Copyright (C) Viacheslav Tykhanovskyi. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 AUTHOR Viacheslav Tykhanovskyi Eviacheslav.t@gmail.comE =head1 CREDITS Takumi Akiyama (github.com/akiym) James Raspass (github.com/JRaspass) MCRayRay (github.com/MCRayRay) =cut CPAN-Audit-20250115.001/lib/CPAN/Audit/0000755000076500000240000000000014741775321015365 5ustar brianstaffCPAN-Audit-20250115.001/lib/CPAN/Audit/Discover/0000755000076500000240000000000014741775321017143 5ustar brianstaffCPAN-Audit-20250115.001/lib/CPAN/Audit/Discover/Cpanfile.pm0000644000076500000240000000136014741775320021221 0ustar brianstaffpackage CPAN::Audit::Discover::Cpanfile; use strict; use warnings; use Module::CPANfile; our $VERSION = "1.001"; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } sub discover { my $self = shift; my ($cpanfile_path) = @_; my $cpanfile = Module::CPANfile->load($cpanfile_path); my $prereqs = $cpanfile->prereqs->as_string_hash; my @deps; foreach my $phase ( keys %$prereqs ) { foreach my $type ( keys %{ $prereqs->{$phase} } ) { foreach my $module ( keys %{ $prereqs->{$phase}->{$type} } ) { my $version = $prereqs->{$phase}->{$type}->{$module}; next if $module eq 'perl'; push @deps, { module => $module, version => $version, }; } } } return @deps; } 1; CPAN-Audit-20250115.001/lib/CPAN/Audit/Discover/CpanfileSnapshot.pm0000644000076500000240000000121214741775320022735 0ustar brianstaffpackage CPAN::Audit::Discover::CpanfileSnapshot; use strict; use warnings; use CPAN::DistnameInfo; our $VERSION = "1.001"; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } sub discover { my $self = shift; my ($cpanfile_snapshot_path) = @_; open my $fh, '<', $cpanfile_snapshot_path or die $!; my @deps; while ( defined( my $line = <$fh> ) ) { if ( $line =~ m/pathname: ([^\s]+)/ ) { next unless my $d = CPAN::DistnameInfo->new($1); next unless $d->dist && $d->version; push @deps, { dist => $d->dist, version => $d->version, }; } } close $fh; return @deps; } 1; CPAN-Audit-20250115.001/lib/CPAN/Audit/Query.pm0000755000076500000240000000520314741775320017032 0ustar brianstaffpackage CPAN::Audit::Query; use strict; use warnings; use CPAN::Audit::Version; our $VERSION = "1.001"; =encoding utf8 =head1 NAME CPAN::Audit::Query - filter the database for advisories that interest you =head1 SYNOPSIS use CPAN::Audit::Query; my $query = CPAN::Audit::Query->new( db => ... ); my @advisories = $query->advisories_for( $dist_name, $version_range ); =head1 DESCRIPTION =head2 Class methods =over 4 =item * new(HASH) The only parameter is the hash reference from L or L. With no C parameter, it uses the empty hash, which means that you'll find no advisories. =cut sub new { my($class, %params) = @_; $params{db} ||= {}; my $self = bless {}, $class; $self->{db} = $params{db}; return $self; } =back =head2 Instance methods =over 4 =item * advisories_for( DISTNAME, VERSION_RANGE ) Returns a list of advisories for DISTNAME in VERSION_RANGE. my @advisories = $query->advisories_for( 'Business::ISBN', '1.23' ); my @advisories = $query->advisories_for( 'Business::ISBN', '>1.23,<2.45' ); my @advisories = $query->advisories_for( 'Business::ISBN', '<1.23' ); =cut sub advisories_for { my( $self, $distname, $dist_version_range ) = @_; $dist_version_range = '>0' unless defined $dist_version_range && 0 < length $dist_version_range; my $dist = $self->{db}->{dists}->{$distname}; return unless $dist; # select only the known distribution versions from the database, # ignoring all others. For example, if $dist_version_range is # ">5.1", we don't care about any versions less than or equal to 5.1. # If $dist_version_range is "5.1", that really means ">=5.1" my %advisories = map { $_->{id}, $_ } map { my $dist_version = $_; grep { my $affected = _includes( $_->{affected_versions}, $dist_version ); my $f = $_->{fixed_versions}; if( exists $_->{fixed_versions} and defined $f and length $f ) { my $fixed = _includes( $f, $dist_version ); $fixed ? 0 : $affected } else { $affected } } @{ $dist->{advisories} }; } grep { CPAN::Audit::Version->in_range( $_, $dist_version_range ) } map { $_->{version}} @{ $dist->{versions} }; values %advisories; } sub _includes { my( $range, $version ) = @_; $range = [$range] unless ref $range; my $rc = 0; foreach my $r ( @$range ) { no warnings 'uninitialized'; $rc += CPAN::Audit::Version->in_range( $version, $r ); } return $rc; } =back =head1 LICENSE Copyright (C) Viacheslav Tykhanovskyi. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 AUTHOR Viacheslav Tykhanovskyi Eviacheslav.t@gmail.comE =cut 1; CPAN-Audit-20250115.001/lib/CPAN/Audit/Version.pm0000644000076500000240000000554714741775320017362 0ustar brianstaffpackage CPAN::Audit::Version; use strict; use warnings; use version; our $VERSION = "1.002"; =encoding utf8 =head1 NAME CPAN::Audit::Version - the infrastructure to compare versions and version ranges =head1 SYNOPSIS use CPAN::Audit::Version; my $cav = CPAN::Audit::Version->new; $cav->in_range( $version, $range ); =head1 DESCRIPTION =head2 Class methods =over 4 =item * new Create a new object. This ignores all arguments. =cut sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } =back =head2 Instance methods =over 4 =item * affected_versions( ARRAY_REF, RANGE ) Given an array reference of versions, return a list of all of the versions in ARRAY_REF that are in RANGE. This is really a filter on ARRAY_REF using the values for which C returns true. my @matching = $cav->affected_versions( \@versions, $range ); =cut BEGIN { use version; my $ops = { '<' => sub { $_[0] < 0 }, '<=' => sub { $_[0] <= 0 }, '==' => sub { $_[0] == 0 }, '>' => sub { $_[0] > 0 }, '>=' => sub { $_[0] >= 0 }, '!=' => sub { $_[0] != 0 }, }; sub affected_versions { my( $self, $available_versions, $range ) = @_; my @affected_versions; foreach my $version (@$available_versions) { if ( $self->in_range( $version, $range ) ) { push @affected_versions, $version; } } return @affected_versions; } =item * in_range( VERSION, RANGE ) Returns true if VERSION is contained in RANGE, and false otherwise. VERSION is any sort of Perl, such as C<1.23> or C<1.2.3>. The RANGE is a comma-separated list of range specifications using the comparators C<< < >>, C<< <= >>, C<< == >>, C<< > >>, C<< >= >>, or C<< != >>. For example, C<< >=1.23,<1.45 >>, C<< ==1.23 >>, or C<< >1.23 >>. my $version = 5.67; my $range = '>=5,<6'; # so, all the versions in 5.x if( $cav->in_range( $version, $range ) ) { say "$version is within $range"; } else { say "$version is not within $range"; } =cut sub in_range { my( $self, $version, $range ) = @_; my( @original ) = ($version, $range); return unless defined $version && defined $range; return unless defined( $version = eval { version->parse($version) } ); my @ands = split /\s*,\s*/, $range; my $result = 1; foreach my $and (@ands) { my( $op, $range_version ) = $and =~ m/^(<=|<|>=|>|==|!=)?\s*([^\s]+)$/; return unless defined( $range_version = eval { version->parse($range_version) } ); $op = '>=' unless defined $op; unless( exists $ops->{$op} ) { $result = 0; last; } no warnings qw(numeric); $result = $ops->{$op}->( version::vcmp($version, $range_version) ); last if $result == 0; } return $result; } } =back =head1 LICENSE Copyright (C) Viacheslav Tykhanovskyi. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 AUTHOR Viacheslav Tykhanovskyi Eviacheslav.t@gmail.comE =cut 1; CPAN-Audit-20250115.001/lib/CPAN/Audit/Discover.pm0000644000076500000240000000110214741775320017472 0ustar brianstaffpackage CPAN::Audit::Discover; use strict; use warnings; use CPAN::Audit::Discover::Cpanfile; use CPAN::Audit::Discover::CpanfileSnapshot; our $VERSION = "1.001"; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } sub discover { my $self = shift; my ($path) = @_; if ( -f "$path/cpanfile.snapshot" ) { return CPAN::Audit::Discover::CpanfileSnapshot->new->discover("$path/cpanfile.snapshot"); } elsif ( -f "$path/cpanfile" ) { return CPAN::Audit::Discover::Cpanfile->new->discover("$path/cpanfile"); } else { } return; } 1; CPAN-Audit-20250115.001/lib/CPAN/Audit/Filter.pm0000644000076500000240000000365214741775320017155 0ustar brianstaffuse v5.10; package CPAN::Audit::Filter; use strict; use warnings; our $VERSION = "1.001"; =encoding utf8 =head1 NAME CPAN::Audit::Filter - manage the reports / CVEs to ignore =head1 SYNOPSIS use CPAN::Audit::Filter; my $filter = CPAN::Audit::Filter->new( exclude => $array_ref ); my $query = CPAN::Audit::Query->new(...); my $advisories = $query->advisories_for( $distname, $version_range ); foreach my $advisory ( $advisories->@* ) { next if $filter->excludes($advisory); ... } =head1 DESCRIPTION =head2 Class methods =over 4 =item * new( exclude => ARRAYREF ) The values in the array ref for C are uppercased before they are stored. =cut sub new { my($class, %params) = @_; my $self = bless {}, $class; $params{exclude} //= []; my %excludes = map { uc($_) => 1 } @{ $params{exclude} }; $self->{excludes} = \%excludes; $self->{ignored} = {}; return $self; } =back =head2 Instance methods =over 4 =item * excludes( $advisory ) Returns true if this instance excludes either the ID or any of the CVEs for ADVISORY, a hash as returned by L. This hash has these keys: id - a string, such as Some-Module-001 cves - an array reference of CVE strings, such as CVE-2022-001 The values extracted from the hash are uppercased before use. =cut sub excludes { my($self, $advisory) = @_; return 0 unless keys %{$self->{excludes}}; my @ids = map { uc } grep { defined } ($advisory->{id}, @{$advisory->{cves}}); foreach my $id ( @ids ) { next unless $self->{excludes}{$id}; $self->{ignored}{$id}++; return 1; } return 0; } =item * ignored_count Return the count of the advisories that were ignored. Each ID or CVE value only counts once. =cut sub ignored_count { scalar keys %{$_[0]->{ignored}} } =back =head1 LICENSE Copyright (C) 2022 Graham TerMarsch This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; CPAN-Audit-20250115.001/lib/CPAN/Audit/Installed.pm0000755000076500000240000000322714741775320017650 0ustar brianstaffpackage CPAN::Audit::Installed; use strict; use warnings; use File::Find (); use Cwd (); our $VERSION = "1.001"; sub new { my( $class, %params ) = @_; bless \%params, $class; } sub find { my $self = shift; my (@inc) = @_; @inc = @INC unless @inc; @inc = grep { defined && -d $_ } map { Cwd::realpath($_) } @inc; my %seen; my @deps; push @deps, { dist => 'perl', version => $] } if $self->{include_perl}; File::Find::find( { wanted => sub { my $path = $File::Find::name; if ( $path && -f $path && m/\.pm$/ ) { return unless my $module = module_from_file($path); return unless my $distname = $self->{db}->{module2dist}->{$module}; my $dist = $self->{db}->{dists}->{$distname}; if ( $dist->{main_module} eq $module ) { return if $seen{$module}++; return unless my $version = module_version($path); push @deps, { dist => $distname, version => $version }; if ( $self->{cb} ) { $self->{cb}->( { path => $path, distname => $distname, version => $version } ); } } } }, follow => 1, follow_skip => 2, }, @inc ); return @deps; } sub module_version { require Module::Extract::VERSION; my( $file ) = @_; my $version = Module::Extract::VERSION->parse_version_safely( $file ); if( eval { $version->can('numify') } ) { $version = $version->numify; } return "$version"; } sub module_from_file { my ($path) = @_; my $module; open my $fh, '<', $path or return; while ( my $line = <$fh> ) { if ( $line =~ m/package\s+(.*?)\s*;/ms ) { $module = $1; last; } } close $fh; return unless $module; } 1; CPAN-Audit-20250115.001/lib/CPAN/Audit/FreshnessCheck.pm0000644000076500000240000000342114741775320020620 0ustar brianstaffpackage CPAN::Audit::FreshnessCheck; use v5.10; our $VERSION = '1.001'; =head1 NAME CPAN::Audit::Freshness - check freshness of CPAN::Audit::DB =head1 SYNOPSIS use CPAN::Audit::Freshness; # from the command-line, with default threshold % perl -MCPAN::Audit::Freshness cpan-audit # from the command-line, with specified threshold of 5 days % perl -MCPAN::Audit::Freshness=5 cpan-audit % env CPAN_AUDIT_FRESH_DAYS=30 perl -MCPAN::Audit::Freshness cpan-audit =head1 DESCRIPTION When loaded, this module outputs a warning if it thinks the version of L is too old. It does this by comparing the version of that module, which is date-based, with the current time. The default threshold is 30 days, although you can set the value of C. There is no other functionality for this module. =head1 LICENSE This library is under the Artistic License 2.0. =head1 AUTHOR Copyright (C) 2022-2024 brian d foy, I<< >> =cut sub import { my( $class, $days ) = @_; require Time::Piece; require CPAN::Audit::DB; my $template = '%Y%m%d'; my( $year, $month, $day ) = (localtime)[5,4,3]; $year += 1900; $month += 1; my $now_string = sprintf "%4d%02d%02d", $year, $month, $day; my $now_moment = Time::Piece->strptime( $now_string, $template ); my $db_time = CPAN::Audit::DB->VERSION =~ s/\..*//r; my $db_moment = Time::Piece->strptime( $db_time, $template ); my $duration = int( ($now_moment - $db_moment) / 86_400 ); warn "Database is $duration days old. Check for updates with `cpan -D CPAN::Audit::DB`\n" if $duration >= threshold($days); } sub default_threshold { 30 } sub threshold { $_[0] // $ENV{CPAN_AUDIT_FRESH_DAYS} // default_threshold() } __PACKAGE__; CPAN-Audit-20250115.001/Makefile.PL0000644000076500000240000000735114741775320015007 0ustar brianstaffpackage CPAN::Audit; use strict; use warnings; =encoding utf8 =head1 The build file for CPAN::Audit This build file is a modulino; it works as both a build script and a module. To build the distribution, run this file normally: % perl Makefile.PL But, it's more interesting than that. You can load it with C and call C to get the data structure it passes to C: my $package = require '/path/to/Makefile.PL'; my $arguments = $package->arguments; Note that C-ing a file makes an entry in C<%INC> for exactly that name. If you try to C another file with the same name, even from a different path, C thinks it has already loaded the file. As such, I recommend you always require the full path to the file. The return value of the C is a package name (in this case, the name of the main module. Use that to call the C method. Even if this distribution needs a higher version of Perl, this bit only needs v5.8. You can play with the data structure with a primitive Perl. =cut use File::Spec::Functions qw(catfile); my $module = __PACKAGE__; ( my $dist = $module ) =~ s/::/-/g; my $github = 'https://github.com/briandfoy/cpan-audit'; my $main_file = catfile( 'lib', split /::/, "$module.pm" ); my $advisory_repo = 'git@github.com:briandfoy/cpan-security-advisory.git'; sub MY::postamble { my $file = __FILE__; return <<"POSTAMBLE"; ###################################################################### # Postamble from $file cpan-security-advisory: \t- git submodule add $advisory_repo cpan-security-advisory/LICENSE: submodules .PHONY: submodules submodules: cpan-security-advisory \t- git submodule init \tgit submodule update --remote # End postamble ###################################################################### POSTAMBLE } my %WriteMakefile = ( 'MIN_PERL_VERSION' => '5.020', 'NAME' => $module, 'AUTHOR' => 'Viacheslav Tykhanovskyi ', 'ABSTRACT_FROM' => $main_file, 'VERSION_FROM' => $main_file, 'LICENSE' => 'perl_5', 'DIR' => [], 'CONFIGURE_REQUIRES' => { 'ExtUtils::MakeMaker' => '6.64', 'File::Spec::Functions' => '0', }, 'BUILD_REQUIRES' => { }, 'EXE_FILES' => [ 'script/cpan-audit', ], 'TEST_REQUIRES' => { 'Capture::Tiny' => '0', 'File::Temp' => '0', 'HTTP::Tiny' => '0', 'Test::More' => '0.98', }, 'PREREQ_PM' => { 'CPAN::DistnameInfo' => '0', 'Module::CoreList' => '5.20181020', 'Module::CPANfile' => '0', 'Module::Extract::VERSION' => '0', 'IO::Interactive' => '0', 'JSON' => '0', 'PerlIO::gzip' => '0', 'CPANSA::DB' => '20241121.001', }, 'META_MERGE' => { 'meta-spec' => { version => 2 }, resources => { repository => { type => 'git', url => $github, web => $github, }, bugtracker => { web => "$github/issues", }, homepage => $github, }, }, clean => { FILES => "$dist-*" }, ); sub arguments { \%WriteMakefile } sub all_modules { my @keys = qw(CONFIGURE_REQUIRES BUILD_REQUIRES PREREQ_PM TEST_REQUIRES); my @modules = map { keys %$_ } @WriteMakefile{@keys}; } do_it() unless caller; sub do_it { require File::Spec; my $MM ='ExtUtils::MakeMaker'; my $MM_version = eval{ "$MM " . $WriteMakefile{'CONFIGURE_REQUIRES'}{'ExtUtils::MakeMaker'} } || "$MM 6.64"; eval "use $MM_version; 1" or die "Could not load $MM_version: $@"; eval "use Test::Manifest 1.21" if -e File::Spec->catfile( qw(t test_manifest) ); my $arguments = arguments(); my $minimum_perl = $arguments->{MIN_PERL_VERSION} || '5.008'; eval "require $minimum_perl;" or die $@; WriteMakefile( %$arguments ); } no warnings; __PACKAGE__; CPAN-Audit-20250115.001/SECURITY.md0000644000076500000240000000230414741775320014617 0ustar brianstaff# Security Policy for CPAN::Audit ## Reporting security issues **Do not report security problems on public forums or in repository issues.** Privately report vulnerabilities to the maintainers listed at the end of this document. Include as many details as possible to reproduce the issue, including code samples or test cases. Check that your report does not expose any of your sensitive data, such as passwords, tokens, or other secrets. You do not need to have a solution or fix. Depending on the issue, CPANSec may be notified. Depending on the issue, CPANSec may be notified. You can also privately report issues to the CPAN Security Group (CPANSec) . This is especially important if you think a vulnerability is being actively exploited. CPANSec may report the issue to the relevant authorities. See [Report a Security Issue](https://security.metacpan.org/docs/report.html). ## Response to reports The maintainers aim to respond to all reports within one day, but this may be affected by life and other things that happen to people who maintain open source code. A new release will be provided as soon as possible. ## Maintainers * brian d foy, CPAN-Audit-20250115.001/META.json0000664000076500000240000000345114741775322014457 0ustar brianstaff{ "abstract" : "Audit CPAN distributions for known vulnerabilities", "author" : [ "Viacheslav Tykhanovskyi " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "CPAN-Audit", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : {} }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.64", "File::Spec::Functions" : "0" } }, "runtime" : { "requires" : { "CPAN::DistnameInfo" : "0", "CPANSA::DB" : "20241121.001", "IO::Interactive" : "0", "JSON" : "0", "Module::CPANfile" : "0", "Module::CoreList" : "5.20181020", "Module::Extract::VERSION" : "0", "PerlIO::gzip" : "0", "perl" : "5.020" } }, "test" : { "requires" : { "Capture::Tiny" : "0", "File::Temp" : "0", "HTTP::Tiny" : "0", "Test::More" : "0.98" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/briandfoy/cpan-audit/issues" }, "homepage" : "https://github.com/briandfoy/cpan-audit", "repository" : { "type" : "git", "url" : "https://github.com/briandfoy/cpan-audit", "web" : "https://github.com/briandfoy/cpan-audit" } }, "version" : "20250115.001", "x_serialization_backend" : "JSON::PP version 4.16" }