diff --git a/.github/workflows/perltest.yml b/.github/workflows/perltest.yml new file mode 100644 index 0000000..54129d6 --- /dev/null +++ b/.github/workflows/perltest.yml @@ -0,0 +1,47 @@ +# This is a basic workflow to help you get started with Actions + +name: CI + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + push: + branches: [ 'master', 'develop' ] + pull_request: + branches: [ 'master' ] + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: ['ubuntu-latest', 'macos-latest'] + perl: [ 'latest' ] + name: Perl ${{ matrix.perl }} on ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - name: Set up perl + uses: shogo82148/actions-setup-perl@v1 + with: + perl-version: ${{ matrix.perl }} + - run: perl -V + - run: cpanm Module::Install + - run: cpanm -n --installdeps . + - run: prove -lv t + + coverage: + runs-on: ubuntu-latest + container: davorg/perl-coveralls:latest + name: Test coverage + steps: + - uses: actions/checkout@v3 + - name: Install modules Module::Install + run: cpanm Module::Install + - name: Install dependencies + run: cpanm -n --installdeps . + - name: Coverage + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: cover -test -report Coveralls diff --git a/Changes b/Changes new file mode 100644 index 0000000..1f6b628 --- /dev/null +++ b/Changes @@ -0,0 +1,4 @@ +Revision history for Perl extension CSAF. + +1.00 2024-xx-xx + - First release of CSAF diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..0bbcf1e --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,74 @@ +# CSAF Perl Toolkit + +The INSTALL is used to introduce the module and provide instructions on +how to install the module, any machine dependencies it may have (for +example C compilers and installed libraries) and any other information +that should be provided before the module is installed. + +## INSTALLATION + +Using Makefile.PL: + +To install this module, run the following commands. + + perl Makefile.PL + make + make test + make install + +Using App::cpanminus: + + cpanm CSAF + +## SUPPORT AND DOCUMENTATION + +After installing, you can find documentation for this module with the +perldoc command. + + perldoc CSAF + + +You can also look for information at: + + * GitHub issues (report bugs here) https://github.com/giterlizzi/perl-CSAF/issues + + +## LICENSE AND COPYRIGHT + +Copyright (C) 2023-2024 Giuseppe Di Terlizzi + +This program is free software; you can redistribute it and/or modify it +under the terms of the the Artistic License (2.0). You may obtain a +copy of the full license at: + +http://www.perlfoundation.org/artistic_license_2_0 + +Any use, modification, and distribution of the Standard or Modified +Versions is governed by this Artistic License. By using, modifying or +distributing the Package, you accept this license. Do not use, modify, +or distribute the Package, if you do not accept this license. + +If your Modified Version has been derived from a Modified Version made +by someone other than you, you are nevertheless required to ensure that +your Modified Version complies with the requirements of this license. + +This license does not grant you the right to use any trademark, service +mark, tradename, or logo of the Copyright Holder. + +This license includes the non-exclusive, worldwide, free-of-charge +patent license to make, have made, use, offer to sell, sell, import and +otherwise transfer the Package with respect to any patent claims +licensable by the Copyright Holder that are necessarily infringed by the +Package. If you institute patent litigation (including a cross-claim or +counterclaim) against any party alleging that the Package constitutes +direct or contributory patent infringement, then this Artistic License +to you shall terminate on the date that such litigation is filed. + +Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER +AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. +THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY +YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR +CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR +CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a582bc5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + The Artistic License 2.0 + + Copyright (c) 2000-2006, The Perl Foundation. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +Preamble + +This license establishes the terms under which a given free software +Package may be copied, modified, distributed, and/or redistributed. +The intent is that the Copyright Holder maintains some artistic +control over the development of that Package while still keeping the +Package available as open source and free software. + +You are always permitted to make arrangements wholly outside of this +license directly with the Copyright Holder of a given Package. If the +terms of this license do not permit the full use that you propose to +make of the Package, you should contact the Copyright Holder and seek +a different licensing arrangement. + +Definitions + + "Copyright Holder" means the individual(s) or organization(s) + named in the copyright notice for the entire Package. + + "Contributor" means any party that has contributed code or other + material to the Package, in accordance with the Copyright Holder's + procedures. + + "You" and "your" means any person who would like to copy, + distribute, or modify the Package. + + "Package" means the collection of files distributed by the + Copyright Holder, and derivatives of that collection and/or of + those files. A given Package may consist of either the Standard + Version, or a Modified Version. + + "Distribute" means providing a copy of the Package or making it + accessible to anyone else, or in the case of a company or + organization, to others outside of your company or organization. + + "Distributor Fee" means any fee that you charge for Distributing + this Package or providing support for this Package to another + party. It does not mean licensing fees. + + "Standard Version" refers to the Package if it has not been + modified, or has been modified only in ways explicitly requested + by the Copyright Holder. + + "Modified Version" means the Package, if it has been changed, and + such changes were not explicitly requested by the Copyright + Holder. + + "Original License" means this Artistic License as Distributed with + the Standard Version of the Package, in its current version or as + it may be modified by The Perl Foundation in the future. + + "Source" form means the source code, documentation source, and + configuration files for the Package. + + "Compiled" form means the compiled bytecode, object code, binary, + or any other form resulting from mechanical transformation or + translation of the Source form. + + +Permission for Use and Modification Without Distribution + +(1) You are permitted to use the Standard Version and create and use +Modified Versions for any purpose without restriction, provided that +you do not Distribute the Modified Version. + + +Permissions for Redistribution of the Standard Version + +(2) You may Distribute verbatim copies of the Source form of the +Standard Version of this Package in any medium without restriction, +either gratis or for a Distributor Fee, provided that you duplicate +all of the original copyright notices and associated disclaimers. At +your discretion, such verbatim copies may or may not include a +Compiled form of the Package. + +(3) You may apply any bug fixes, portability changes, and other +modifications made available from the Copyright Holder. The resulting +Package will still be considered the Standard Version, and as such +will be subject to the Original License. + + +Distribution of Modified Versions of the Package as Source + +(4) You may Distribute your Modified Version as Source (either gratis +or for a Distributor Fee, and with or without a Compiled form of the +Modified Version) provided that you clearly document how it differs +from the Standard Version, including, but not limited to, documenting +any non-standard features, executables, or modules, and provided that +you do at least ONE of the following: + + (a) make the Modified Version available to the Copyright Holder + of the Standard Version, under the Original License, so that the + Copyright Holder may include your modifications in the Standard + Version. + + (b) ensure that installation of your Modified Version does not + prevent the user installing or running the Standard Version. In + addition, the Modified Version must bear a name that is different + from the name of the Standard Version. + + (c) allow anyone who receives a copy of the Modified Version to + make the Source form of the Modified Version available to others + under + + (i) the Original License or + + (ii) a license that permits the licensee to freely copy, + modify and redistribute the Modified Version using the same + licensing terms that apply to the copy that the licensee + received, and requires that the Source form of the Modified + Version, and of any works derived from it, be made freely + available in that license fees are prohibited but Distributor + Fees are allowed. + + +Distribution of Compiled Forms of the Standard Version +or Modified Versions without the Source + +(5) You may Distribute Compiled forms of the Standard Version without +the Source, provided that you include complete instructions on how to +get the Source of the Standard Version. Such instructions must be +valid at the time of your distribution. If these instructions, at any +time while you are carrying out such distribution, become invalid, you +must provide new instructions on demand or cease further distribution. +If you provide valid instructions or cease distribution within thirty +days after you become aware that the instructions are invalid, then +you do not forfeit any of your rights under this license. + +(6) You may Distribute a Modified Version in Compiled form without +the Source, provided that you comply with Section 4 with respect to +the Source of the Modified Version. + + +Aggregating or Linking the Package + +(7) You may aggregate the Package (either the Standard Version or +Modified Version) with other packages and Distribute the resulting +aggregation provided that you do not charge a licensing fee for the +Package. Distributor Fees are permitted, and licensing fees for other +components in the aggregation are permitted. The terms of this license +apply to the use and Distribution of the Standard or Modified Versions +as included in the aggregation. + +(8) You are permitted to link Modified and Standard Versions with +other works, to embed the Package in a larger work of your own, or to +build stand-alone binary or bytecode versions of applications that +include the Package, and Distribute the result without restriction, +provided the result does not expose a direct interface to the Package. + + +Items That are Not Considered Part of a Modified Version + +(9) Works (including, but not limited to, modules and scripts) that +merely extend or make use of the Package, do not, by themselves, cause +the Package to be a Modified Version. In addition, such works are not +considered parts of the Package itself, and are not subject to the +terms of this license. + + +General Provisions + +(10) Any use, modification, and distribution of the Standard or +Modified Versions is governed by this Artistic License. By using, +modifying or distributing the Package, you accept this license. Do not +use, modify, or distribute the Package, if you do not accept this +license. + +(11) If your Modified Version has been derived from a Modified +Version made by someone other than you, you are nevertheless required +to ensure that your Modified Version complies with the requirements of +this license. + +(12) This license does not grant you the right to use any trademark, +service mark, tradename, or logo of the Copyright Holder. + +(13) This license includes the non-exclusive, worldwide, +free-of-charge patent license to make, have made, use, offer to sell, +sell, import and otherwise transfer the Package with respect to any +patent claims licensable by the Copyright Holder that are necessarily +infringed by the Package. If you institute patent litigation +(including a cross-claim or counterclaim) against any party alleging +that the Package constitutes direct or contributory patent +infringement, then this Artistic License to you shall terminate on the +date that such litigation is filed. + +(14) Disclaimer of Warranty: +THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS +IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL +LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..94462d7 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,116 @@ +Changes +INSTALL.md +lib/CSAF.pm +lib/CSAF/Base.pm +lib/CSAF/Builder.pm +lib/CSAF/Document.pm +lib/CSAF/List.pm +lib/CSAF/Parser.pm +lib/CSAF/Renderer.pm +lib/CSAF/Renderer/Base.pm +lib/CSAF/Renderer/HTML.pm +lib/CSAF/Renderer/JSON.pm +lib/CSAF/resources/cache/6616ab5b335e47cd27e701999bd8eb3a +lib/CSAF/resources/cache/803157887dabfefc7425634a877ed27a +lib/CSAF/resources/cache/ac1b12ae2d190a0c468b534b8a1b9bd2 +lib/CSAF/resources/cache/efea8b3a7512905f5aa6affa56ea3a68 +lib/CSAF/resources/template/default.tt2 +lib/CSAF/Type.pm +lib/CSAF/Type/Acknowledgment.pm +lib/CSAF/Type/Acknowledgments.pm +lib/CSAF/Type/AggregateSeverity.pm +lib/CSAF/Type/Base.pm +lib/CSAF/Type/Branch.pm +lib/CSAF/Type/Branches.pm +lib/CSAF/Type/CVSS2.pm +lib/CSAF/Type/CVSS3.pm +lib/CSAF/Type/CWE.pm +lib/CSAF/Type/Distribution.pm +lib/CSAF/Type/Document.pm +lib/CSAF/Type/Engine.pm +lib/CSAF/Type/FileHash.pm +lib/CSAF/Type/FileHashes.pm +lib/CSAF/Type/FullProductName.pm +lib/CSAF/Type/FullProductNames.pm +lib/CSAF/Type/Generator.pm +lib/CSAF/Type/GenericURI.pm +lib/CSAF/Type/GenericURIs.pm +lib/CSAF/Type/Hash.pm +lib/CSAF/Type/Hashes.pm +lib/CSAF/Type/ID.pm +lib/CSAF/Type/IDs.pm +lib/CSAF/Type/Involvement.pm +lib/CSAF/Type/Involvements.pm +lib/CSAF/Type/List.pm +lib/CSAF/Type/Note.pm +lib/CSAF/Type/Notes.pm +lib/CSAF/Type/Product.pm +lib/CSAF/Type/ProductGroup.pm +lib/CSAF/Type/ProductGroups.pm +lib/CSAF/Type/ProductStatus.pm +lib/CSAF/Type/ProductTree.pm +lib/CSAF/Type/Publisher.pm +lib/CSAF/Type/Reference.pm +lib/CSAF/Type/References.pm +lib/CSAF/Type/Relationship.pm +lib/CSAF/Type/Relationships.pm +lib/CSAF/Type/Remediation.pm +lib/CSAF/Type/Remediations.pm +lib/CSAF/Type/RestartRequired.pm +lib/CSAF/Type/Revision.pm +lib/CSAF/Type/RevisionHistory.pm +lib/CSAF/Type/Score.pm +lib/CSAF/Type/Scores.pm +lib/CSAF/Type/Threat.pm +lib/CSAF/Type/Threats.pm +lib/CSAF/Type/TLP.pm +lib/CSAF/Type/Tracking.pm +lib/CSAF/Type/Vulnerabilities.pm +lib/CSAF/Type/Vulnerability.pm +lib/CSAF/Util.pm +lib/CSAF/Validator.pm +lib/CSAF/Validator/Base.pm +lib/CSAF/Validator/MandatoryTests.pm +lib/CSAF/Validator/Message.pm +lib/CSAF/Validator/OptionalTests.pm +lib/CSAF/Validator/Schema.pm +lib/CSAF/Writer.pm +LICENSE +Makefile.PL +MANIFEST +README.md +t/00-load.t +t/10-mandatory-6.1.11.t +t/10-mandatory-6.1.13.t +t/10-mandatory-6.1.15.t +t/10-mandatory-6.1.16.t +t/10-mandatory-6.1.17.t +t/10-mandatory-6.1.18.t +t/10-mandatory-6.1.19.t +t/10-mandatory-6.1.20.t +t/10-mandatory-6.1.22.t +t/10-mandatory-6.1.23.t +t/10-mandatory-6.1.26.t +t/10-mandatory-6.1.27.1.t +t/10-mandatory-6.1.27.11.t +t/10-mandatory-6.1.27.2.t +t/10-mandatory-6.1.27.3.t +t/10-mandatory-6.1.27.4.t +t/10-mandatory-6.1.27.5.t +t/10-mandatory-6.1.27.6.t +t/10-mandatory-6.1.27.7.t +t/10-mandatory-6.1.27.8.t +t/10-mandatory-6.1.28.t +t/10-mandatory-6.1.31.t +t/10-mandatory-6.1.6.t +t/10-mandatory-6.1.7.t +t/10-mandatory-6.1.8.t +t/10-mandatory-6.1.9.t +t/10-optional-6.2.14.t +t/10-optional-6.2.2.t +t/10-optional-6.2.3.t +t/10-optional-6.2.4.t +t/lib/Test/CSAF.pm +t/manifest.t +t/pod-coverage.t +t/pod.t diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..61eff7b --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,47 @@ +#!perl + +use strict; +use warnings; +use ExtUtils::MakeMaker; + +WriteMakefile( + NAME => 'CSAF', + AUTHOR => q{Giuseppe Di Terlizzi }, + VERSION_FROM => 'lib/CSAF.pm', + ABSTRACT_FROM => 'lib/CSAF.pm', + LICENSE => 'artistic_2', + EXE_FILES => [], + MIN_PERL_VERSION => 5.010, + PL_FILES => {}, + CONFIGURE_REQUIRES => {'ExtUtils::MakeMaker' => '0'}, + TEST_REQUIRES => {'Test::More' => '0'}, + PREREQ_PM => { + 'Cpanel::JSON::XS' => '0', + 'Digest::SHA' => '0', # CORE + 'File::Basename' => '0', # CORE + 'File::Path' => '0', # CORE + 'File::Spec::Functions' => '0', # CORE + 'JSON::Validator' => '0', + 'List::MoreUtils' => '0', + 'List::Util' => '0', # CORE + 'Moo' => '0', + 'Tie::File' => '0', # CORE + 'Time::Piece' => '0', # CORE + 'URI::PackageURL' => '0', + 'Template' => '0', + }, + META_MERGE => { + 'meta-spec' => {version => 2}, + 'resources' => { + bugtracker => {web => 'https://github.com/giterlizzi/perl-CSAF/issues'}, + repository => { + type => 'git', + url => 'git://github.com/giterlizzi/perl-CSAF', + web => 'https://github.com/giterlizzi/perl-CSAF' + } + }, + x_purl => 'pkg:cpan/GDT/CSAF' + }, + dist => {COMPRESS => 'gzip -9f', SUFFIX => 'gz'}, + clean => {FILES => 'CSAF-*'} +); diff --git a/README.md b/README.md new file mode 100644 index 0000000..41eed00 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +[![Release](https://img.shields.io/github/release/giterlizzi/perl-CSAF.svg)](https://github.com/giterlizzi/perl-CSAF/releases) [![Actions Status](https://github.com/giterlizzi/perl-CSAF/workflows/linux/badge.svg)](https://github.com/giterlizzi/perl-CSAF/actions) [![License](https://img.shields.io/github/license/giterlizzi/perl-CSAF.svg)](https://github.com/giterlizzi/perl-CSAF) [![Starts](https://img.shields.io/github/stars/giterlizzi/perl-CSAF.svg)](https://github.com/giterlizzi/perl-CSAF) [![Forks](https://img.shields.io/github/forks/giterlizzi/perl-CSAF.svg)](https://github.com/giterlizzi/perl-CSAF) [![Issues](https://img.shields.io/github/issues/giterlizzi/perl-CSAF.svg)](https://github.com/giterlizzi/perl-CSAF/issues) [![Coverage Status](https://coveralls.io/repos/github/giterlizzi/perl-CSAF/badge.svg)](https://coveralls.io/github/giterlizzi/perl-CSAF) + +# CSAF Perl Toolkit + +## Synopsis + +```.pl +use CSAF; + +``` + +## Install + +Using Makefile.PL: + +To install `CSAF` distribution, run the following commands. + + perl Makefile.PL + make + make test + make install + +Using App::cpanminus: + + cpanm CSAF + + +## Documentation + + - `perldoc CSAF` + - https://metacpan.org/release/CSAF + + +## Copyright + + - Copyright 2023-2024 © Giuseppe Di Terlizzi diff --git a/lib/CSAF.pm b/lib/CSAF.pm new file mode 100644 index 0000000..21e3c8a --- /dev/null +++ b/lib/CSAF.pm @@ -0,0 +1,53 @@ +package CSAF; + +use 5.010001; +use strict; +use warnings; + +use CSAF::Builder; +use CSAF::Writer; +use CSAF::Validator; +use CSAF::Renderer; + +use CSAF::Document; + +use overload '""' => 'to_string'; + +our $VERSION = '0.10'; + +our $CACHE = {}; + +sub new { + + my $class = shift; + + $CACHE = {}; # Reset Cache + + my $self = {_ => CSAF::Document->new}; + + return bless $self, $class; + +} + +# CSAF document core properties + +sub document { shift->{_}->document } +sub product_tree { shift->{_}->product_tree } +sub vulnerabilities { shift->{_}->vulnerabilities } + +# Helper classes + +sub builder { CSAF::Builder->new(csaf => shift) } +sub renderer { CSAF::Renderer->new(csaf => shift) } +sub validator { CSAF::Validator->new(csaf => shift) } +sub writer { CSAF::Writer->new(csaf => shift, @_) } + +# Helpers + +sub validate { shift->validator->validate } +sub render { shift->renderer->render(@_) } + +sub to_string { shift->renderer->render } +sub TO_JSON { shift->builder->TO_JSON } + +1; diff --git a/lib/CSAF/Base.pm b/lib/CSAF/Base.pm new file mode 100644 index 0000000..47ce0ad --- /dev/null +++ b/lib/CSAF/Base.pm @@ -0,0 +1,27 @@ +package CSAF::Base; + +use 5.010001; +use strict; +use warnings; + +use Carp; +use Moo; + +around BUILDARGS => sub { + + my ($orig, $class, @args) = @_; + + return {csaf => $args[0]} if @args == 1; + return $class->$orig(@args); + +}; + +has csaf => ( + is => 'ro', + isa => sub { + Carp::croak 'Must be an instance of "CSAF"' unless ref($_[0]) eq 'CSAF'; + }, + required => 1 +); + +1; diff --git a/lib/CSAF/Builder.pm b/lib/CSAF/Builder.pm new file mode 100644 index 0000000..a3855e8 --- /dev/null +++ b/lib/CSAF/Builder.pm @@ -0,0 +1,55 @@ +package CSAF::Builder; + +use 5.010001; +use strict; +use warnings; + +use Carp; + +use CSAF::Validator; + +use Moo; +extends 'CSAF::Base'; + +sub build { + + my ($self, $skip_validation) = @_; + + my $document = $self->csaf->document->TO_BUILD; + my $vulnerabilities = $self->csaf->vulnerabilities->TO_BUILD; + my $product_tree = $self->csaf->product_tree->TO_BUILD; + + my $csaf = {document => $document}; + + if (@{$vulnerabilities}) { + $csaf->{vulnerabilities} = $vulnerabilities; + } + + if ($product_tree) { + $csaf->{product_tree} = $product_tree; + } + + my @errors = (); + + unless ($skip_validation) { + + my $v = $self->csaf->validator; + my @messages = $v->validate; + + if (@messages && $v->has_error) { + Carp::croak 'CSAF Document validation error(s)'; + } + + if (@messages && $v->has_warning) { + Carp::carp 'CSAF Document validation warning(s)'; + } + + } + + return $csaf; + +} + +sub TO_JSON { shift->build } + +1; diff --git a/lib/CSAF/Document.pm b/lib/CSAF/Document.pm new file mode 100644 index 0000000..5d2f6d5 --- /dev/null +++ b/lib/CSAF/Document.pm @@ -0,0 +1,29 @@ +package CSAF::Document; + +use 5.010001; +use strict; +use warnings; + +use CSAF::Type::Document; +use CSAF::Type::ProductTree; +use CSAF::Type::Vulnerabilities; + +use Moo; +extends 'CSAF::Type::Base'; + +sub document { + my ($self, %params) = @_; + $self->{document} ||= CSAF::Type::Document->new(%params); +} + +sub product_tree { + my ($self, %params) = @_; + $self->{product_tree} ||= CSAF::Type::ProductTree->new(%params); +} + +sub vulnerabilities { + my ($self, %params) = @_; + $self->{vulnerabilities} ||= CSAF::Type::Vulnerabilities->new(%params); +} + +1; diff --git a/lib/CSAF/List.pm b/lib/CSAF/List.pm new file mode 100644 index 0000000..f32bafa --- /dev/null +++ b/lib/CSAF/List.pm @@ -0,0 +1,45 @@ +package CSAF::List; + +use 5.010001; +use strict; +use warnings; + +use Moo; + +has items => (is => 'rw', default => sub { [] }); + +around BUILDARGS => sub { + my ($orig, $class, @args) = @_; + + return {items => \@args} if @args > 0; # TODO + return $class->$orig(@args); +}; + +sub size { scalar @{shift->items} } + +sub each { + + my ($self, $callback) = @_; + + return @{$self->items} unless $callback; + + my $idx = 0; + $_->$callback($idx++) for @{$self->items}; + + return $self; + +} + +sub to_array { [@{shift->items}] } + +sub item { push @{shift->items}, shift } +sub append { shift->item(@_) } +sub add { shift->item(@_) } + +sub first { shift->items->[0] } +sub last { shift->items->[-1] } +sub join { join($_[1], $_[0]->items) } + +sub TO_JSON { [@{shift->items}] } + +1; diff --git a/lib/CSAF/Lite.pm b/lib/CSAF/Lite.pm new file mode 100644 index 0000000..b6b00e9 --- /dev/null +++ b/lib/CSAF/Lite.pm @@ -0,0 +1,224 @@ +package CSAF::Lite; + +use 5.010001; +use strict; +use warnings; + +use Moo; + +use CSAF; +use CSAF::Builder; + +has id => (is => 'rw', required => 1); +has category => (is => 'rw', default => sub {'csaf_base'}); +has lang => (is => 'rw', default => 'en'); +has title => (is => 'rw'); +has severity => (is => 'rw'); +has publisher => (is => 'rw'); +has notes => (is => 'rw'); + +has initial_release_date => (is => 'rw'); +has current_release_date => (is => 'rw'); + +has tlp_label => (is => 'rw', default => 'WHITE', coerce => sub { uc $_[0] }); + +my $PRODUCTS = {}; +my @VULNERABILITIES = (); +my @NOTES = (); + +has publisher_name => (is => 'rw'); +has publisher_namespace => (is => 'rw'); +has publisher_category => (is => 'rw'); + +has engine_name => (is => 'rw', default => 'CSAF Perl Toolkit (Lite)'); +has engine_version => (is => 'rw', default => $CSAF::VERSION); + +sub note { + + my ($self, %params) = @_; + + my $category = delete $params{category} || Carp::croak 'Category is required'; + my $title = delete $params{title}; + my $text = delete $params{text}; + + push @NOTES, {category => $category, title => $title, text => $text}; + +} + +sub description_note { shift->note(category => 'description', @_) } +sub details_note { shift->note(category => 'details', @_) } +sub faq_note { shift->note(category => 'faq', @_) } +sub general_note { shift->note(category => 'general', @_) } +sub legal_disclaimer_note { shift->note(category => 'legal_disclaimer', @_) } +sub other_note { shift->note(category => 'other', @_) } +sub summary_note { shift->note(category => 'summary', @_) } + +sub vulnerability { + + my ($self, %params) = @_; + + my $cve = delete $params{cve} || Carp::croak 'CVE is required for a vulnerability'; + my $title = delete $params{title} || Carp::croak 'Title is required for a vulnerability'; + my $cwe_id = delete $params{cwe_id} || $params{cwe} || $params{weakness}; + + my $first_affected = delete $params{first_affected} || []; + my $first_fixed = delete $params{first_fixed} || []; + my $fixed = delete $params{fixed} || []; + my $known_affected = delete $params{known_affected} || []; + my $known_not_affected = delete $params{known_not_affected} || []; + my $last_affected = delete $params{last_affected} || []; + my $recommended = delete $params{recommended} || []; + my $under_investigation = delete $params{under_investigation} || []; + + my $note = delete $params{note} || []; + my $remediation = delete $params{remediation} || []; + + my $vuln = { + cve => $cve, + title => $title, + cwe_id => $cwe_id, + + first_affected => $first_affected, + first_fixed => $first_fixed, + fixed => $fixed, + known_affected => $known_affected, + known_not_affected => $known_not_affected, + last_affected => $last_affected, + recommended => $recommended, + under_investigation => $under_investigation, + + note => $note, + remediation => $remediation, + }; + + push @VULNERABILITIES, $vuln; + +} + +sub product { + + my ($self, %params) = @_; + + my $vendor = $params{vendor}; + my $product = $params{product}; + my $vendor_product_id = $params{id} || _build_product_id($vendor, $product); + + $PRODUCTS->{$vendor_product_id} = \%params; + +} + +sub generate { + + my ($self) = @_; + + my $csaf = CSAF->new; + + $csaf->document->title($self->title); + $csaf->document->category($self->category); + $csaf->document->lang($self->lang); + $csaf->document->aggregate_severity($self->severity); + + $csaf->document->distribution->tlp_label($self->tlp_label); + + $csaf->document->publisher(%{$self->publisher}); + + $self->current_release_date($self->initial_release_date) unless $self->current_release_date; + $self->initial_release_date($self->current_release_date) unless $self->initial_release_date; + + my $tracking = $csaf->document->tracking( + id => $self->id, + status => 'final', + version => '1', + current_release_date => $self->current_release_date, + initial_release_date => $self->initial_release_date, + ); + + $tracking->revision_history->item(date => $self->initial_release_date, summary => 'First release', number => '1'); + + $tracking->generator->engine_name($self->engine_name); + $tracking->generator->engine_version($self->engine_version); + + foreach my $note (@NOTES) { + $csaf->document->notes->item(%{$note}); + } + + foreach my $product_id (keys %{$PRODUCTS}) { + + my $vendor = $PRODUCTS->{$product_id}->{vendor}; + my $product = $PRODUCTS->{$product_id}->{product}; + my $versions = $PRODUCTS->{$product_id}->{versions}; + + my $branches = $csaf->product_tree->branches; + my $vendor_item = $branches->item(category => 'vendor', name => $vendor); + + my $vendor_branches = $vendor_item->branches; + my $product_item = $vendor_branches->item(category => 'product_name', name => $product); + + my $product_branches = $product_item->branches; + + foreach my $item (@{$versions}) { + + my $product_id = $item->{id} || _build_product_id($vendor, $product, $item->{version}); + my $product_version_item = {product_id => $product_id, name => $item->{name}}; + + if (!defined $item->{name}) { + $product_version_item->{name} = join(' ', $vendor, $product, $item->{version}); + } + + if ($item->{cpe}) { + $product_version_item->{product_identification_helper}->{cpe} = $item->{cpe}; + } + + if ($item->{purl}) { + $product_version_item->{product_identification_helper}->{purl} = $item->{purl}; + } + + $product_branches->item( + category => 'product_version', + name => $item->{version}, + product => $product_version_item, + ); + } + + } + + my $vulns = $csaf->vulnerabilities; + + foreach my $vuln (@VULNERABILITIES) { + + my $vuln_item = $vulns->item(cve => $vuln->{cve}, title => $vuln->{title}); + my $product_status = $vuln_item->product_status; + + $vuln_item->cwe_id($vuln->{cwe_id}) if $vuln->{cwe_id}; + + $product_status->first_affected($vuln->{first_affected}) if (@{$vuln->{first_affected}}); + $product_status->first_fixed($vuln->{first_fixed}) if (@{$vuln->{first_fixed}}); + $product_status->fixed($vuln->{fixed}) if (@{$vuln->{fixed}}); + $product_status->known_affected($vuln->{known_affected}) if (@{$vuln->{known_affected}}); + $product_status->known_not_affected($vuln->{known_not_affected}) if (@{$vuln->{known_not_affected}}); + $product_status->last_affected($vuln->{last_affected}) if (@{$vuln->{last_affected}}); + $product_status->recommended($vuln->{recommended}) if (@{$vuln->{recommended}}); + $product_status->under_investigation($vuln->{under_investigation}) if (@{$vuln->{under_investigation}}); + + foreach my $note (@{$vuln->{note}}) { + $vuln_item->notes->item(%{$note}); + } + + } + + return CSAF::Builder->new($csaf); + +} + +sub TO_JSON { shift->generate } + +sub _build_product_id { + + my $product_id = join ':', map { lc $_ } @_; + $product_id =~ s/\s/_/g; + + return $product_id; + +} + +1; diff --git a/lib/CSAF/Parser.pm b/lib/CSAF/Parser.pm new file mode 100644 index 0000000..a416070 --- /dev/null +++ b/lib/CSAF/Parser.pm @@ -0,0 +1,151 @@ +package CSAF::Parser; + +use 5.010001; +use strict; +use warnings; + +use CSAF; +use CSAF::Util qw(JSON file_read); + +use Moo; + +has file => (is => 'ro', required => 1); + +sub parse { + + my $self = shift; + + Carp::croak qq{File $self->file not found} unless (-e $self->file); + + my $content = file_read($self->file); + my $json = eval { JSON->decode($content) }; + + Carp::croak qq{Failed to parse $self->file: $@} if ($@); + + Carp::croak qq{Invalid CSAF document} unless (defined $json->{document}); + + my $csaf = CSAF->new; + + if (my $document = $json->{document}) { + + $csaf->document->title($document->{title}); + $csaf->document->category($document->{category}); + $csaf->document->csaf_version($document->{csaf_version}); + $csaf->document->lang($document->{lang}) if ($document->{lang}); + + if (my $aggregate_severity = $document->{aggregate_severity}) { + $csaf->document->aggregate_severity(%{$aggregate_severity}); + } + + if (my $distribution = $document->{distribution}) { + + $csaf->document->distribution(%{$distribution}); + + if (my $tlp = $distribution->{tlp}) { + $csaf->document->distribution->tlp(%{$tlp}); + } + + } + + $csaf->document->publisher(%{$document->{publisher}}); + + if (my $notes = $document->{notes}) { + $csaf->document->notes->item(%{$_}) for (@{$notes}); + } + + if (my $references = $document->{references}) { + $csaf->document->references->item(%{$_}) for (@{$references}); + } + + if (my $tracking = $document->{tracking}) { + $csaf->document->tracking(%{$tracking}); + $csaf->document->tracking->generator(%{$tracking->{generator}}) if ($tracking->{generator}); + $csaf->document->tracking->generator->engine(%{$tracking->{generator}->{engine}}) + if ($tracking->{generator}->{engine}); + $csaf->document->tracking->revision_history->item(%{$_}) for (@{$tracking->{revision_history}}); + } + + if (my $acknowledgments = $document->{acknowledgments}) { + $csaf->document->acknowledgments->item(%{$_}) for (@{$acknowledgments}); + } + + } + + if (my $vulnerabilities = $json->{vulnerabilities}) { + foreach my $vulnerability (@{$vulnerabilities}) { + + my $vuln = $csaf->vulnerabilities->item(cve => $vulnerability->{cve}); + + if (my $cwe = $vulnerability->{cwe}) { + $vuln->cwe(%{$cwe}); + } + + if (my $notes = $vulnerability->{notes}) { + $vuln->notes->item(%{$_}) for (@{$notes}); + } + + if (my $references = $vulnerability->{references}) { + $vuln->references->item(%{$_}) for (@{$references}); + } + + if (my $product_status = $vulnerability->{product_status}) { + $vuln->product_status(%{$product_status}); + } + + if (my $scores = $vulnerability->{scores}) { + $vuln->scores->item(%{$_}) for (@{$scores}); + } + + if (my $acknowledgments = $vulnerability->{acknowledgments}) { + $vuln->acknowledgments->item(%{$_}) for (@{$acknowledgments}); + } + + if (my $remediations = $vulnerability->{remediations}) { + $vuln->remediations->item(%{$_}) for (@{$remediations}); + } + + if (my $threats = $vulnerability->{threats}) { + $vuln->threats->item(%{$_}) for (@{$threats}); + } + + if (my $involvements = $vulnerability->{involvements}) { + $vuln->involvements->item(%{$_}) for (@{$involvements}); + } + + } + } + + + if (my $product_tree = $json->{product_tree}) { + + my $csaf_product_tree = $csaf->product_tree; + + if (my $branches = $product_tree->{branches}) { + branches_walk($branches, $csaf_product_tree); + } + + if (my $relationships = $product_tree->{relationships}) { + $csaf_product_tree->relationships->item(%{$_}) for (@{$relationships}); + } + } + + return $csaf; + +} + +sub branches_walk { + + my ($branches, $csaf) = @_; + + foreach my $branch (@{$branches}) { + if (defined $branch->{branches}) { + branches_walk($branch->{branches}, $csaf->branches->item(%{$branch})); + } + else { + $csaf->branches->item(%{$branch}); + } + } + +} + +1; diff --git a/lib/CSAF/Renderer.pm b/lib/CSAF/Renderer.pm new file mode 100644 index 0000000..464d70f --- /dev/null +++ b/lib/CSAF/Renderer.pm @@ -0,0 +1,34 @@ +package CSAF::Renderer; + +use 5.010001; +use strict; +use warnings; + +use Carp; + +use CSAF::Renderer::JSON; +use CSAF::Renderer::HTML; + +use Moo; +extends 'CSAF::Renderer::Base'; + +sub render { + + my ($self, %options) = @_; + + my $format = delete $options{'format'} || 'json'; + + my $renderer = { + json => sub { CSAF::Renderer::JSON->new($self->csaf) }, + html => sub { CSAF::Renderer::HTML->new($self->csaf) }, + }; + + if (defined $renderer->{lc $format}) { + return $renderer->{lc $format}->()->render(%options); + } + + Carp::croak 'Unknown render format'; + +} + +1; diff --git a/lib/CSAF/Renderer/Base.pm b/lib/CSAF/Renderer/Base.pm new file mode 100644 index 0000000..19457d7 --- /dev/null +++ b/lib/CSAF/Renderer/Base.pm @@ -0,0 +1,16 @@ +package CSAF::Renderer::Base; + +use 5.010001; +use strict; +use warnings; + +use Carp; + +use Moo; +extends 'CSAF::Base'; + +use overload '""' => \&render, fallback => 1; + +sub render { Carp::croak 'Method "render" not implemented by subclass' } + +1; diff --git a/lib/CSAF/Renderer/HTML.pm b/lib/CSAF/Renderer/HTML.pm new file mode 100644 index 0000000..43c2ca9 --- /dev/null +++ b/lib/CSAF/Renderer/HTML.pm @@ -0,0 +1,59 @@ +package CSAF::Renderer::HTML; + +use 5.010001; +use strict; +use warnings; + +use CSAF::Util qw(tt_templates_path); +use Template; + +use Moo; +extends 'CSAF::Renderer::Base'; + +sub render { + + my ($self, %options) = @_; + + my $products = $CSAF::CACHE->{products} || {}; + my $max_base_score = 0; + + $self->csaf->builder->build; + + foreach my $vuln ($self->csaf->vulnerabilities->each) { + foreach my $score ($vuln->scores->each) { + if ($score->cvss_v3 && $score->cvss_v3->baseScore && $max_base_score < $score->cvss_v3->baseScore) { + $max_base_score = $score->cvss_v3->baseScore; + } + } + } + + my $tt = Template->new( + INCLUDE_PATH => tt_templates_path, + PRE_CHOMP => 1, + TRIM => 1, + ENCODING => 'UTF-8', + VARIABLES => { + document => $self->csaf->document, + product_tree => $self->csaf->product_tree, + vulnerabilities => $self->csaf->vulnerabilities, + max_base_score => $max_base_score, + }, + FILTERS => { + product_name => sub { + my ($product_id) = @_; + return $products->{$product_id} || $product_id; + } + } + ) or Carp::croak $Template::ERROR; + + my $template = $options{template} || 'default'; + my $vars = $options{vars} || {}; + my $output = undef; + + $tt->process("$template.tt2", $vars, \$output) or Carp::croak $tt->error; + + return $output; + +} + +1; diff --git a/lib/CSAF/Renderer/JSON.pm b/lib/CSAF/Renderer/JSON.pm new file mode 100644 index 0000000..fbac7d5 --- /dev/null +++ b/lib/CSAF/Renderer/JSON.pm @@ -0,0 +1,21 @@ +package CSAF::Renderer::JSON; + +use 5.010001; +use strict; +use warnings; + +use CSAF::Util qw(JSON); + +use Moo; +extends 'CSAF::Renderer::Base'; + +sub render { + + my $csaf = shift->csaf->builder->build; + my $json = JSON->encode($csaf); + + return $json; + +} + +1; diff --git a/lib/CSAF/Type.pm b/lib/CSAF/Type.pm new file mode 100644 index 0000000..3aa1b12 --- /dev/null +++ b/lib/CSAF/Type.pm @@ -0,0 +1,90 @@ +package CSAF::Type; + +use 5.010001; +use strict; +use warnings; + +use Carp; + +use constant TYPE_CLASSES => { + acknowledgment => 'CSAF::Type::Acknowledgment', + acknowledgments => 'CSAF::Type::Acknowledgments', + aggregate_severity => 'CSAF::Type::AggregateSeverity', + branch => 'CSAF::Type::Branch', + branches => 'CSAF::Type::Branches', + cvss_v2 => 'CSAF::Type::CVSS2', + cvss_v3 => 'CSAF::Type::CVSS3', + cwe => 'CSAF::Type::CWE', + distribution => 'CSAF::Type::Distribution', + document => 'CSAF::Type::Document', + engine => 'CSAF::Type::Engine', + file_hash => 'CSAF::Type::FileHash', + file_hashes => 'CSAF::Type::FileHashes', + full_product_name => 'CSAF::Type::FullProductName', + full_product_names => 'CSAF::Type::FullProductNames', + generator => 'CSAF::Type::Generator', + generic_uri => 'CSAF::Type::GenericURI', + generic_uris => 'CSAF::Type::GenericURIs', + hash => 'CSAF::Type::Hash', + hashes => 'CSAF::Type::Hashes', + id => 'CSAF::Type::ID', + ids => 'CSAF::Type::IDs', + note => 'CSAF::Type::Note', + notes => 'CSAF::Type::Notes', + product => 'CSAF::Type::Product', + product_group => 'CSAF::Type::ProductGroup', + product_groups => 'CSAF::Type::ProductGroups', + product_identification_helper => 'CSAF::Type::ProductIdentificationHelper', + product_status => 'CSAF::Type::ProductStatus', + product_tree => 'CSAF::Type::ProductTree', + publisher => 'CSAF::Type::Publisher', + reference => 'CSAF::Type::Reference', + references => 'CSAF::Type::References', + relationship => 'CSAF::Type::Relationship', + relationships => 'CSAF::Type::Relationships', + remediation => 'CSAF::Type::Remediation', + remediations => 'CSAF::Type::Remediations', + restart_required => 'CSAF::Type::RestartRequired', + revision => 'CSAF::Type::Revision', + revision_history => 'CSAF::Type::RevisionHistory', + score => 'CSAF::Type::Score', + scores => 'CSAF::Type::Scores', + threat => 'CSAF::Type::Threat', + threats => 'CSAF::Type::Threats', + tlp => 'CSAF::Type::TLP', + tracking => 'CSAF::Type::Tracking', + vulnerabilities => 'CSAF::Type::Vulnerabilities', + vulnerability => 'CSAF::Type::Vulnerability', +}; + + +sub new { + + my ($self, %params) = @_; + + my $name = delete $params{name}; + my $value = delete $params{value}; + + return _build(lc $name, $value); + +} + +sub name { + my ($self, $name, $value) = @_; + return _build(lc $name, $value); +} + +sub _build { + + my ($name, $value) = @_; + + my $class = TYPE_CLASSES->{$name} or Carp::croak 'Unknown CSAF type'; + + if ($class->can('new') or eval "require $class; 1") { + local $Carp::Internal{caller()} = 1; + return $class->new($value); + } + +} + +1; diff --git a/lib/CSAF/Type/Acknowledgment.pm b/lib/CSAF/Type/Acknowledgment.pm new file mode 100644 index 0000000..f60ede1 --- /dev/null +++ b/lib/CSAF/Type/Acknowledgment.pm @@ -0,0 +1,35 @@ +package CSAF::Type::Acknowledgment; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::Base'; + +has names => (is => 'rw', isa => \&_check_isa_array, default => sub { [] }); +has urls => (is => 'rw', isa => \&_check_isa_array, default => sub { [] }); +has summary => (is => 'rw'); +has organization => (is => 'rw'); + +sub _check_isa_array { + Carp::croak 'must be an array' if (ref $_[0] ne 'ARRAY'); +} + +sub TO_BUILD { + + my $self = shift; + + my $output = {}; + + $output->{summary} = $self->summary if ($self->summary); + $output->{organization} = $self->organization if ($self->organization); + + $output->{names} = $self->names if (@{$self->names}); + $output->{urls} = $self->urls if (@{$self->urls}); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Acknowledgments.pm b/lib/CSAF/Type/Acknowledgments.pm new file mode 100644 index 0000000..a934e42 --- /dev/null +++ b/lib/CSAF/Type/Acknowledgments.pm @@ -0,0 +1,12 @@ +package CSAF::Type::Acknowledgments; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::Acknowledgment'); + +1; diff --git a/lib/CSAF/Type/AggregateSeverity.pm b/lib/CSAF/Type/AggregateSeverity.pm new file mode 100644 index 0000000..6615f72 --- /dev/null +++ b/lib/CSAF/Type/AggregateSeverity.pm @@ -0,0 +1,28 @@ +package CSAF::Type::AggregateSeverity; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::Base'; + +has text => (is => 'rw'); +has namespace => (is => 'rw'); + +sub TO_BUILD { + + my $self = shift; + + my $output = {}; + + $output->{text} = $self->text if ($self->text); + $output->{namespace} = $self->namespace if ($self->namespace); + + return if (!keys %{$output}); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Base.pm b/lib/CSAF/Type/Base.pm new file mode 100644 index 0000000..029ebb0 --- /dev/null +++ b/lib/CSAF/Type/Base.pm @@ -0,0 +1,13 @@ +package CSAF::Type::Base; + +use 5.010001; +use strict; +use warnings; + +use Moo; +use Carp; + +sub TO_BUILD { Carp::croak 'Method "TO_BUILD" not implemented by subclass' } +sub TO_JSON { shift->TO_BUILD } + +1; diff --git a/lib/CSAF/Type/Branch.pm b/lib/CSAF/Type/Branch.pm new file mode 100644 index 0000000..39abfc9 --- /dev/null +++ b/lib/CSAF/Type/Branch.pm @@ -0,0 +1,37 @@ +package CSAF::Type::Branch; + +use 5.010001; +use strict; +use warnings; + +use Moo; +use CSAF::Type::Branches; +use CSAF::Type::Product; + +extends 'CSAF::Type::Base'; + +has [qw(category name)] => (is => 'rw', required => 1); +has product => (is => 'rw', predicate => 1, coerce => sub { CSAF::Type::Product->new(shift) }); + +sub branches { + my $self = shift; + $self->{branches} ||= CSAF::Type::Branches->new(@_); +} + +sub TO_BUILD { + + my $self = shift; + + my $output = {category => $self->category, name => $self->name}; + + if (@{$self->branches->items}) { + $output->{branches} = $self->branches; + } + + $output->{product} = $self->product if ($self->product); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Branches.pm b/lib/CSAF/Type/Branches.pm new file mode 100644 index 0000000..997d63e --- /dev/null +++ b/lib/CSAF/Type/Branches.pm @@ -0,0 +1,12 @@ +package CSAF::Type::Branches; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::Branch'); + +1; diff --git a/lib/CSAF/Type/CVSS2.pm b/lib/CSAF/Type/CVSS2.pm new file mode 100644 index 0000000..5fab89a --- /dev/null +++ b/lib/CSAF/Type/CVSS2.pm @@ -0,0 +1,70 @@ +package CSAF::Type::CVSS2; + +use 5.010001; +use strict; +use warnings; + +use Moo; + +extends 'CSAF::Type::Base'; + +has version => (is => 'ro', default => '2.0'); +has vectorString => (is => 'ro', required => 1); +has baseScore => (is => 'ro', required => 1, coerce => sub { ($_[0] + 0) }); + +has [qw( + accessVector + accessComplexity + authentication + confidentialityImpact + integrityImpact + availabilityImpact + exploitability + remediationLevel + reportConfidence + collateralDamagePotential + targetDistribution + confidentialityRequirement + integrityRequirement + availabilityRequirement +)] => (is => 'rw', coerce => sub { uc $_[0] }); + +has ['temporalScore', 'environmentalScore'] => (is => 'rw', coerce => sub { ($_[0] + 0) }); + + +sub TO_BUILD { + + my $self = shift; + + my $output = {version => $self->version, vectorString => $self->vectorString, baseScore => $self->baseScore}; + + my @attributes = qw( + accessVector + accessComplexity + authentication + confidentialityImpact + integrityImpact + availabilityImpact + exploitability + remediationLevel + reportConfidence + temporalScore + collateralDamagePotential + targetDistribution + confidentialityRequirement + integrityRequirement + availabilityRequirement + environmentalScore + ); + + for my $attribute (@attributes) { + $output->{$attribute} = $self->$attribute if ($self->$attribute); + } + + return $output; + +} + +sub TO_JSON { shift->TO_BUILD } + +1; diff --git a/lib/CSAF/Type/CVSS3.pm b/lib/CSAF/Type/CVSS3.pm new file mode 100644 index 0000000..66b690e --- /dev/null +++ b/lib/CSAF/Type/CVSS3.pm @@ -0,0 +1,105 @@ +package CSAF::Type::CVSS3; + +use 5.010001; +use strict; +use warnings; + +use Moo; +use Carp; + +extends 'CSAF::Type::Base'; + + +# TODO Parse vector string and set single metrics + +has version => ( + is => 'ro', + default => '3.1', + isa => sub { Carp::croak "CVSS3 version must be 3.0 or 3.1" unless ($_[0] eq '3.0' || $_[0] eq '3.1') } +); + +has vectorString => (is => 'rw', required => 1, coerce => sub { uc $_[0] }); +has baseScore => (is => 'rw', required => 1, coerce => sub { ($_[0] + 0) }); +has baseSeverity => (is => 'rw', required => 1, coerce => sub { uc $_[0] }); + +has [qw( + attackVector + attackComplexity + privilegesRequired + userInteraction + scope + confidentialityImpact + integrityImpact + availabilityImpact + exploitCodeMaturity + remediationLevel + reportConfidence + temporalScore + temporalSeverity + confidentialityRequirement + integrityRequirement + availabilityRequirement + modifiedAttackVector + modifiedAttackComplexity + modifiedPrivilegesRequired + modifiedUserInteraction + modifiedScope + modifiedConfidentialityImpact + modifiedIntegrityImpact + modifiedAvailabilityImpact + environmentalScore + environmentalSeverity +)] => (is => 'rw', coerce => sub { uc $_[0] }); + +sub TO_BUILD { + + my $self = shift; + + my $output = { + version => $self->version, + vectorString => $self->vectorString, + baseScore => $self->baseScore, + baseSeverity => $self->baseSeverity + }; + + + my @attributes = qw( + attackVector + attackComplexity + privilegesRequired + userInteraction + scope + confidentialityImpact + integrityImpact + availabilityImpact + exploitCodeMaturity + remediationLevel + reportConfidence + temporalScore + temporalSeverity + confidentialityRequirement + integrityRequirement + availabilityRequirement + modifiedAttackVector + modifiedAttackComplexity + modifiedPrivilegesRequired + modifiedUserInteraction + modifiedScope + modifiedConfidentialityImpact + modifiedIntegrityImpact + modifiedAvailabilityImpact + environmentalScore + environmentalSeverity + ); + + for my $attribute (@attributes) { + $output->{$attribute} = $self->$attribute if ($self->$attribute); + } + + return $output; + +} + +sub TO_JSON { shift->TO_BUILD } + +1; diff --git a/lib/CSAF/Type/CWE.pm b/lib/CSAF/Type/CWE.pm new file mode 100644 index 0000000..57234fa --- /dev/null +++ b/lib/CSAF/Type/CWE.pm @@ -0,0 +1,34 @@ +package CSAF::Type::CWE; + +use 5.010001; +use strict; +use warnings; + +use Moo; + +use CSAF::Util qw(get_weakness_name); + +extends 'CSAF::Type::Base'; + +has id => (is => 'rw', isa => sub { Carp::croak 'Malformed CWE ID' if ($_[0] !~ /^CWE-\d{0,5}$/) }); +has name => (is => 'rw'); + +sub TO_BUILD { + + my $self = shift; + + my $output = {id => $self->id}; + + if (my $name = $self->name) { + $output->{name} = $name; + } + + if (!$self->name) { + $output->{name} = get_weakness_name($self->id); + } + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Distribution.pm b/lib/CSAF/Type/Distribution.pm new file mode 100644 index 0000000..3034504 --- /dev/null +++ b/lib/CSAF/Type/Distribution.pm @@ -0,0 +1,31 @@ +package CSAF::Type::Distribution; + +use 5.010001; +use strict; +use warnings; + +use CSAF::Type::TLP; + +use Moo; +extends 'CSAF::Type::Base'; + +has text => (is => 'rw'); + +sub tlp { + my ($self, %params) = @_; + $self->{tlp} ||= CSAF::Type::TLP->new(%params); +} + +sub TO_BUILD { + + my $self = shift; + + my $output = {tlp => $self->tlp}; + + $output->{text} = $self->text if ($self->text); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Document.pm b/lib/CSAF/Type/Document.pm new file mode 100644 index 0000000..6963953 --- /dev/null +++ b/lib/CSAF/Type/Document.pm @@ -0,0 +1,100 @@ +package CSAF::Type::Document; + +use 5.010001; +use strict; +use warnings; + +use Moo; +use Carp; + +use CSAF::Type::AggregateSeverity; +use CSAF::Type::Distribution; +use CSAF::Type::Publisher; +use CSAF::Type::Tracking; +use CSAF::Type::Acknowledgments; +use CSAF::Type::Notes; +use CSAF::Type::References; + +extends 'CSAF::Type::Base'; + +has category => (is => 'rw', default => 'csaf_base', required => 1); +has csaf_version => (is => 'rw', default => '2.0'); +has lang => (is => 'rw', default => 'en', coerce => sub { (my $lang = $_[0]) =~ tr /_/-/; $lang }); +has title => (is => 'rw'); +has source_lang => (is => 'rw', coerce => sub { (my $lang = $_[0]) =~ tr /_/-/; $lang }); + +sub aggregate_severity { + my ($self, %params) = @_; + $self->{aggregate_severity} ||= CSAF::Type::AggregateSeverity->new(%params); +} + +sub distribution { + my ($self, %params) = @_; + $self->{distribution} ||= CSAF::Type::Distribution->new(%params); +} + +sub tracking { + my ($self, %params) = @_; + $self->{tracking} ||= CSAF::Type::Tracking->new(%params); +} + +sub publisher { + my ($self, %params) = @_; + $self->{publisher} ||= CSAF::Type::Publisher->new(%params); +} + +sub acknowledgments { + my $self = shift; + $self->{acknowledgments} ||= CSAF::Type::Acknowledgments->new(@_); +} + +sub notes { + my $self = shift; + $self->{notes} ||= CSAF::Type::Notes->new(@_); +} + +sub references { + my $self = shift; + $self->{references} ||= CSAF::Type::References->new(@_); +} + +sub TO_BUILD { + + my $self = shift; + + # TODO + Carp::croak 'Missing document title' unless $self->title; + + my $output = { + category => $self->category, + csaf_version => $self->csaf_version, + distribution => $self->distribution, + publisher => $self->publisher, + title => $self->title, + tracking => $self->tracking, + lang => $self->lang, + }; + + if (@{$self->acknowledgments->items}) { + $output->{acknowledgments} = $self->acknowledgments; + } + + if (@{$self->notes->items}) { + $output->{notes} = $self->notes; + } + + if ($self->aggregate_severity->text || $self->aggregate_severity->namespace) { + $output->{aggregate_severity} = $self->aggregate_severity; + } + + if (@{$self->references->items}) { + $output->{references} = $self->references; + } + + $output->{source_lang} = $self->source_lang if ($self->source_lang); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Engine.pm b/lib/CSAF/Type/Engine.pm new file mode 100644 index 0000000..ae514b8 --- /dev/null +++ b/lib/CSAF/Type/Engine.pm @@ -0,0 +1,24 @@ +package CSAF::Type::Engine; + +use 5.010001; +use strict; +use warnings; + +use Moo; + +extends 'CSAF::Type::Base'; + +has name => (is => 'rw', default => 'CSAF Perl Toolkit'); +has version => (is => 'rw', default => sub {$CSAF::VERSION}); + +sub TO_BUILD { + + my $self = shift; + + my $output = {name => $self->name, version => $self->version}; + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/FileHash.pm b/lib/CSAF/Type/FileHash.pm new file mode 100644 index 0000000..00ad244 --- /dev/null +++ b/lib/CSAF/Type/FileHash.pm @@ -0,0 +1,23 @@ +package CSAF::Type::FileHash; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::Base'; + +has algorithm => (is => 'rw', required => 1); +has value => (is => 'rw', required => 1); + +sub TO_BUILD { + + my $self = shift; + + my $output = {algorithm => $self->algorithm, value => $self->value}; + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/FileHashes.pm b/lib/CSAF/Type/FileHashes.pm new file mode 100644 index 0000000..fecd189 --- /dev/null +++ b/lib/CSAF/Type/FileHashes.pm @@ -0,0 +1,14 @@ +package CSAF::Type::FileHashes; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +our $ITEM_CLASS_NAME = 'CSAF::Type::FileHash'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::FileHash'); + +1; diff --git a/lib/CSAF/Type/FullProductName.pm b/lib/CSAF/Type/FullProductName.pm new file mode 100644 index 0000000..8e3f77f --- /dev/null +++ b/lib/CSAF/Type/FullProductName.pm @@ -0,0 +1,41 @@ +package CSAF::Type::FullProductName; + +use 5.010001; +use strict; +use warnings; + +use CSAF::Type::ProductIdentificationHelper; + +use Moo; +extends 'CSAF::Type::Base'; + +has name => (is => 'rw', required => 1); +has product_id => (is => 'rw', required => 1, trigger => 1); + +sub _trigger_product_id { + my ($self) = @_; + $CSAF::CACHE->{products}->{$self->product_id} = $self->name; +} + +has product_identification_helper => ( + is => 'rw', + predicate => 1, + coerce => sub { + (ref($_[0]) !~ /ProductIdentificationHelper/) ? CSAF::Type::ProductIdentificationHelper->new(shift) : $_[0]; + } +); + +sub TO_BUILD { + + my $self = shift; + + my $output = {name => $self->name, product_id => $self->product_id}; + + $output->{product_identification_helper} = $self->product_identification_helper->TO_BUILD + if ($self->has_product_identification_helper); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/FullProductNames.pm b/lib/CSAF/Type/FullProductNames.pm new file mode 100644 index 0000000..aa19552 --- /dev/null +++ b/lib/CSAF/Type/FullProductNames.pm @@ -0,0 +1,12 @@ +package CSAF::Type::FullProductNames; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::FullProductName'); + +1; diff --git a/lib/CSAF/Type/Generator.pm b/lib/CSAF/Type/Generator.pm new file mode 100644 index 0000000..3e1e481 --- /dev/null +++ b/lib/CSAF/Type/Generator.pm @@ -0,0 +1,35 @@ +package CSAF::Type::Generator; + +use 5.010001; +use strict; +use warnings; + +use Moo; +use CSAF::Util qw(check_datetime); +use CSAF::Type::Engine; + +extends 'CSAF::Type::Base'; + +has date => (is => 'rw', predicate => 1, coerce => \&check_datetime); + +sub engine { + my ($self, %params) = @_; + $self->{engine} ||= CSAF::Type::Engine->new(%params); +} + + +sub TO_BUILD { + + my $self = shift; + + my $output = {engine => $self->engine}; + + if ($self->has_date) { + $output->{date} = $self->date; + } + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/GenericURI.pm b/lib/CSAF/Type/GenericURI.pm new file mode 100644 index 0000000..a35bdec --- /dev/null +++ b/lib/CSAF/Type/GenericURI.pm @@ -0,0 +1,19 @@ +package CSAF::Type::GenericURI; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::Base'; + +has uri => (is => 'rw', required => 1); +has namespace => (is => 'rw', required => 1); + + +sub TO_BUILD { + my $self = shift; + return {uri => $self->uri, namespace => $self->namespace}; +} + +1; diff --git a/lib/CSAF/Type/GenericURIs.pm b/lib/CSAF/Type/GenericURIs.pm new file mode 100644 index 0000000..092ec7b --- /dev/null +++ b/lib/CSAF/Type/GenericURIs.pm @@ -0,0 +1,12 @@ +package CSAF::Type::GenericURIs; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::GenericURI'); + +1; diff --git a/lib/CSAF/Type/Hash.pm b/lib/CSAF/Type/Hash.pm new file mode 100644 index 0000000..6239d21 --- /dev/null +++ b/lib/CSAF/Type/Hash.pm @@ -0,0 +1,32 @@ +package CSAF::Type::Hash; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::Base'; + +use CSAF::Type::FileHashes; + +has filename => (is => 'rw', required => 1); + +has file_hashes => ( + is => 'rw', + required => 1, + coerce => sub { + (ref($_[0]) !~ /FileHashes/) ? CSAF::Type::FileHashes->new(shift) : $_[0]; + } +); + +sub TO_BUILD { + + my $self = shift; + + my $output = {filename => $self->filename, file_hashes => $self->file_hashes->TO_BUILD}; + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Hashes.pm b/lib/CSAF/Type/Hashes.pm new file mode 100644 index 0000000..a12eeca --- /dev/null +++ b/lib/CSAF/Type/Hashes.pm @@ -0,0 +1,12 @@ +package CSAF::Type::Hashes; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::Hash'); + +1; diff --git a/lib/CSAF/Type/ID.pm b/lib/CSAF/Type/ID.pm new file mode 100644 index 0000000..dfe0033 --- /dev/null +++ b/lib/CSAF/Type/ID.pm @@ -0,0 +1,23 @@ +package CSAF::Type::ID; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::Base'; + +has system_name => (is => 'rw', required => 1); +has text => (is => 'rw', required => 1); + +sub TO_BUILD { + + my $self = shift; + + my $output = {system_name => $self->system_name, text => $self->text}; + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/IDs.pm b/lib/CSAF/Type/IDs.pm new file mode 100644 index 0000000..9e18cb2 --- /dev/null +++ b/lib/CSAF/Type/IDs.pm @@ -0,0 +1,12 @@ +package CSAF::Type::IDs; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::ID'); + +1; diff --git a/lib/CSAF/Type/Involvement.pm b/lib/CSAF/Type/Involvement.pm new file mode 100644 index 0000000..1620274 --- /dev/null +++ b/lib/CSAF/Type/Involvement.pm @@ -0,0 +1,31 @@ +package CSAF::Type::Involvement; + +use 5.010001; +use strict; +use warnings; + +use Moo; +use CSAF::Util qw(check_datetime); + +extends 'CSAF::Type::Base'; + + +has date => (is => 'rw', coerce => \&check_datetime); +has party => (is => 'rw', required => 1,); +has status => (is => 'rw', required => 1); +has summary => (is => 'rw'); + +sub TO_BUILD { + + my $self = shift; + + my $output = {status => $self->status, party => $self->number}; + + $output->{date} = $self->date if ($self->date); + $output->{summary} = $self->summary if ($self->summary); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Involvements.pm b/lib/CSAF/Type/Involvements.pm new file mode 100644 index 0000000..02f86c1 --- /dev/null +++ b/lib/CSAF/Type/Involvements.pm @@ -0,0 +1,12 @@ +package CSAF::Type::Involvements; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::Involvement'); + +1; diff --git a/lib/CSAF/Type/List.pm b/lib/CSAF/Type/List.pm new file mode 100644 index 0000000..3330cea --- /dev/null +++ b/lib/CSAF/Type/List.pm @@ -0,0 +1,84 @@ +package CSAF::Type::List; + +use 5.010001; +use strict; +use warnings; + +use Moo; +use Carp; + +extends 'CSAF::Type::Base'; + +has item_class => (is => 'ro', builder => '_build_item_class', lazy => 1); +has item_class_name => (is => 'ro', required => 1); +has items => (is => 'rw', default => sub { [] }); + +around BUILDARGS => sub { + my ($orig, $class, @args) = @_; + + return {items => \@args} if @args > 0; # TODO + return $class->$orig(@args); +}; + +sub _build_item_class { + + my $class = shift->item_class_name; + + return $class if ($class->can('new') or eval "require $class; 1"); + + Carp::croak "Failed to load item class $class: $@"; + +} + +sub size { scalar @{shift->items} } + +sub each { + + my ($self, $callback) = @_; + + return @{$self->items} unless $callback; + + my $idx = 0; + $_->$callback($idx++) for @{$self->items}; + + return $self; + +} + +sub to_array { [@{shift->items}] } + +sub item { + + my ($self, %params) = @_; + + my $item = $self->item_class->new(%params); + push @{$self->items}, $item; + + return $item; + +} + +sub append { shift->item(@_) } +sub add { shift->item(@_) } + +sub TO_BUILD { + + my $self = shift; + my $output = []; + + foreach my $item (@{$self->items}) { + if (ref($item) =~ /^CSAF::Type/) { + push @{$output}, $item->TO_BUILD; + } + else { + push @{$output}, $item; + } + } + + return $output; + +} + +sub TO_JSON { shift->TO_BUILD } + +1; diff --git a/lib/CSAF/Type/Note.pm b/lib/CSAF/Type/Note.pm new file mode 100644 index 0000000..4e9479d --- /dev/null +++ b/lib/CSAF/Type/Note.pm @@ -0,0 +1,36 @@ +package CSAF::Type::Note; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::Base'; + +my @CATEGORIES = ('description', 'details', 'faq', 'general', 'legal_disclaimer', 'other', 'summary'); + +has category => ( + is => 'rw', + required => 1, + isa => sub { Carp::croak 'Unknown note "category"' unless grep(/$_[0]/, @CATEGORIES) } +); + +has text => (is => 'rw', required => 1); +has audience => (is => 'rw'); +has title => (is => 'rw'); + + +sub TO_BUILD { + + my $self = shift; + + my $output = {category => $self->category, text => $self->text}; + + $output->{audience} = $self->audience if ($self->audience); + $output->{title} = $self->title if ($self->title); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Notes.pm b/lib/CSAF/Type/Notes.pm new file mode 100644 index 0000000..39a209b --- /dev/null +++ b/lib/CSAF/Type/Notes.pm @@ -0,0 +1,26 @@ +package CSAF::Type::Notes; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::Note'); + +sub get_category { + + my ($self, $category) = @_; + + my @items = (); + + foreach ($self->each) { + push @items, $_ if ($_->category eq $category); + } + + return \@items; + +} + +1; diff --git a/lib/CSAF/Type/Product.pm b/lib/CSAF/Type/Product.pm new file mode 100644 index 0000000..9d3140f --- /dev/null +++ b/lib/CSAF/Type/Product.pm @@ -0,0 +1,43 @@ +package CSAF::Type::Product; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::Base'; + +use CSAF::Type::ProductIdentificationHelper; + +has name => (is => 'rw', required => 1); +has product_id => (is => 'rw', required => 1, trigger => 1); + +sub _trigger_product_id { + my ($self) = @_; + + $CSAF::CACHE->{products}->{$self->product_id} = $self->name; +} + +has product_identification_helper => ( + is => 'rw', + predicate => 1, + coerce => sub { + (ref($_[0]) !~ /ProductIdentificationHelper/) ? CSAF::Type::ProductIdentificationHelper->new(shift) : $_[0]; + } +); + +sub TO_BUILD { + + my $self = shift; + + my $output = {name => $self->name, product_id => $self->product_id}; + + $output->{product_identification_helper} = $self->product_identification_helper + if $self->has_product_identification_helper; + + return $output; + +} + + +1; diff --git a/lib/CSAF/Type/ProductGroup.pm b/lib/CSAF/Type/ProductGroup.pm new file mode 100644 index 0000000..6f6d718 --- /dev/null +++ b/lib/CSAF/Type/ProductGroup.pm @@ -0,0 +1,27 @@ +package CSAF::Type::ProductGroup; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::Base'; + + +has group_id => (is => 'rw', required => 1, default => sub { [] }); +has product_ids => (is => 'rw', required => 1, default => sub { [] }); +has summary => (is => 'rw'); + +sub TO_BUILD { + + my $self = shift; + + my $output = {group_id => $self->group_id, product_ids => $self->product_ids}; + + $output->{summary} = $self->summary if ($self->summary); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/ProductGroups.pm b/lib/CSAF/Type/ProductGroups.pm new file mode 100644 index 0000000..40e2508 --- /dev/null +++ b/lib/CSAF/Type/ProductGroups.pm @@ -0,0 +1,12 @@ +package CSAF::Type::ProductGroups; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::ProductGroup'); + +1; diff --git a/lib/CSAF/Type/ProductIdentificationHelper.pm b/lib/CSAF/Type/ProductIdentificationHelper.pm new file mode 100644 index 0000000..ad91cc7 --- /dev/null +++ b/lib/CSAF/Type/ProductIdentificationHelper.pm @@ -0,0 +1,66 @@ +package CSAF::Type::ProductIdentificationHelper; + +use 5.010001; +use strict; +use warnings; + +use CSAF::Type::Hashes; +use CSAF::Type::GenericURIs; + +use URI::PackageURL; + +use Moo; +extends 'CSAF::Type::Base'; + +my $PURL_REGEX = qr{^pkg:[A-Za-z\\.\\-\\+][A-Za-z0-9\\.\\-\\+]*/.+}; +my $CPE_REGEX + = qr{^(cpe:2\.3:[aho\*\-](:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#\$%&'\(\)\+,/:;<=>@\[\]\^`\{\|\}~]))+(\?*|\*?))|[\*\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\*\-]))(:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#\$%&'\(\)\+,/:;<=>@\[\]\^`\{\|\}~]))+(\?*|\*?))|[\*\-])){4})|([c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9\._\-~%]*){0,6})$}; + +has cpe => (is => 'rw', predicate => 1, isa => sub { Carp::croak 'Invalid CPE' if $_[0] !~ /$CPE_REGEX/ },); + +has purl => ( + is => 'rw', + predicate => 1, + coerce => sub { ref($_[0]) eq 'URI::PackageURL' ? $_[0]->to_string : $_[0] }, + isa => sub { Carp::croak 'Invalid purl' if $_[0] !~ /$PURL_REGEX/ } +); + +has [qw(sbom_urls serial_numbers skus model_numbers)] => (is => 'rw', predicate => 1, default => sub { [] }); + +sub hashes { + my $self = shift; + $self->{hashes} ||= CSAF::Type::Hashes->new(@_); +} + +sub x_generic_uris { + my $self = shift; + $self->{x_generic_uris} ||= CSAF::Type::GenericURIs->new(@_); +} + +sub TO_BUILD { + + my $self = shift; + + my $output = {}; + + $output->{cpe} = $self->cpe if $self->has_cpe; + $output->{purl} = $self->purl if $self->has_purl; + + $output->{skus} = $self->skus if @{$self->skus}; + $output->{sbom_urls} = $self->sbom_urls if @{$self->sbom_urls}; + $output->{serial_numbers} = $self->serial_numbers if @{$self->serial_numbers}; + $output->{model_numbers} = $self->model_numbers if @{$self->model_numbers}; + + if (@{$self->x_generic_uris->items}) { + $output->{x_generic_uris} = $self->x_generic_uris; + } + + if (@{$self->hashes->items}) { + $output->{hashes} = $self->hashes->TO_BUILD; + } + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/ProductStatus.pm b/lib/CSAF/Type/ProductStatus.pm new file mode 100644 index 0000000..de2d4bf --- /dev/null +++ b/lib/CSAF/Type/ProductStatus.pm @@ -0,0 +1,41 @@ +package CSAF::Type::ProductStatus; + +use 5.010001; +use strict; +use warnings; + +use Moo; +use Carp; + +extends 'CSAF::Type::Base'; + +my @ATTRIBUTES = qw( + first_affected first_fixed fixed known_affected known_not_affected + last_affected recommended under_investigation +); + +has [@ATTRIBUTES] => ( + is => 'rw', + isa => sub { + Carp::croak 'must be an array of products' if (ref $_[0] ne 'ARRAY'); + }, + default => sub { [] } +); + +sub TO_BUILD { + + my $self = shift; + + my $output = {}; + + for my $attribute (@ATTRIBUTES) { + $output->{$attribute} = $self->$attribute if (@{$self->$attribute}); + } + + return if (!keys %{$output}); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/ProductTree.pm b/lib/CSAF/Type/ProductTree.pm new file mode 100644 index 0000000..8fd5a9a --- /dev/null +++ b/lib/CSAF/Type/ProductTree.pm @@ -0,0 +1,60 @@ +package CSAF::Type::ProductTree; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::Base'; + +use CSAF::Type::Branches; +use CSAF::Type::FullProductNames; +use CSAF::Type::ProductGroups; +use CSAF::Type::Relationships; + + +sub branches { + my ($self, %params) = @_; + $self->{branches} ||= CSAF::Type::Branches->new(%params); +} + +sub full_product_names { + my ($self, %params) = @_; + $self->{full_product_names} ||= CSAF::Type::FullProductNames->new(%params); +} + +sub product_groups { + my ($self, %params) = @_; + $self->{product_groups} ||= CSAF::Type::ProductGroups->new(%params); +} + +sub relationships { + my ($self, %params) = @_; + $self->{relationships} ||= CSAF::Type::Relationships->new(%params); +} + +sub TO_BUILD { + + my $self = shift; + + my $output = {}; + + if (@{$self->branches->items}) { + $output->{branches} = $self->branches->TO_BUILD; + } + + if (@{$self->relationships->items}) { + $output->{relationships} = $self->relationships->TO_BUILD; + } + + if (@{$self->full_product_names->items}) { + $output->{full_product_names} = $self->full_product_names->TO_BUILD; + } + + return if not keys %{$output}; + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Publisher.pm b/lib/CSAF/Type/Publisher.pm new file mode 100644 index 0000000..d7ef6a8 --- /dev/null +++ b/lib/CSAF/Type/Publisher.pm @@ -0,0 +1,35 @@ +package CSAF::Type::Publisher; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::Base'; + +my @CATEGORIES = ('coordinator', 'discoverer', 'other', 'translator', 'user', 'vendor'); + +has ['name', 'namespace'] => (is => 'rw', required => 1); + +has category => ( + is => 'rw', + required => 1, + isa => sub { Carp::croak 'Unknown document "category"' unless grep(/$_[0]/, @CATEGORIES) } +); + +has ['contact_details', 'issuing_authority'] => (is => 'rw'); + +sub TO_BUILD { + + my $self = shift; + + my $output = {category => $self->category, name => $self->name, namespace => $self->namespace}; + + $output->{contact_details} = $self->contact_details if ($self->contact_details); + $output->{issuing_authority} = $self->issuing_authority if ($self->issuing_authority); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Reference.pm b/lib/CSAF/Type/Reference.pm new file mode 100644 index 0000000..0f63e53 --- /dev/null +++ b/lib/CSAF/Type/Reference.pm @@ -0,0 +1,31 @@ +package CSAF::Type::Reference; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::Base'; + +my @CATEGORIES = ('self', 'external'); + +has summary => (is => 'rw', required => 1); +has url => (is => 'rw', required => 1); + +has category => + (is => 'rw', isa => sub { Carp::croak 'Unknown reference "category"' unless grep(/$_[0]/, @CATEGORIES) }); + +sub TO_BUILD { + + my $self = shift; + + my $output = {summary => $self->summary, url => $self->url}; + + $output->{category} = $self->category if ($self->category); + + return $output; + +} + + +1; diff --git a/lib/CSAF/Type/References.pm b/lib/CSAF/Type/References.pm new file mode 100644 index 0000000..8ed1986 --- /dev/null +++ b/lib/CSAF/Type/References.pm @@ -0,0 +1,12 @@ +package CSAF::Type::References; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::Reference'); + +1; diff --git a/lib/CSAF/Type/Relationship.pm b/lib/CSAF/Type/Relationship.pm new file mode 100644 index 0000000..63c51d7 --- /dev/null +++ b/lib/CSAF/Type/Relationship.pm @@ -0,0 +1,40 @@ +package CSAF::Type::Relationship; + +use 5.010001; +use strict; +use warnings; + +use CSAF::Type::FullProductName; + +use Moo; +extends 'CSAF::Type::Base'; + +has category => (is => 'rw', required => 1); +has product_reference => (is => 'rw', required => 1); +has relates_to_product_reference => (is => 'rw', required => 1); + +has full_product_name => ( + is => 'rw', + predicate => 1, + coerce => sub { + (ref($_[0]) !~ /FullProductName/) ? CSAF::Type::FullProductName->new(shift) : $_[0]; + } +); + +sub TO_BUILD { + + my $self = shift; + + my $output = { + category => $self->category, + full_product_name => $self->full_product_name, + product_reference => $self->product_reference, + relates_to_product_reference => $self->relates_to_product_reference, + }; + + return $output; + +} + + +1; diff --git a/lib/CSAF/Type/Relationships.pm b/lib/CSAF/Type/Relationships.pm new file mode 100644 index 0000000..a573255 --- /dev/null +++ b/lib/CSAF/Type/Relationships.pm @@ -0,0 +1,12 @@ +package CSAF::Type::Relationships; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::Relationship'); + +1; diff --git a/lib/CSAF/Type/Remediation.pm b/lib/CSAF/Type/Remediation.pm new file mode 100644 index 0000000..0b48e94 --- /dev/null +++ b/lib/CSAF/Type/Remediation.pm @@ -0,0 +1,40 @@ +package CSAF::Type::Remediation; + +use 5.010001; +use strict; +use warnings; + +use CSAF::Util qw(check_datetime); +use CSAF::Type::RestartRequired; + +use Moo; +extends 'CSAF::Type::Base'; + + +has category => (is => 'rw', required => 1); +has date => (is => 'rw', coerce => \&check_datetime); +has details => (is => 'rw', required => 1); +has entitlements => (is => 'rw', default => sub { [] }); +has group_ids => (is => 'rw', default => sub { [] }); +has product_ids => (is => 'rw', default => sub { [] }); +has restart_required => (is => 'ro', coerce => sub { CSAF::Type::RestartRequired->new(shift) }); +has url => (is => 'rw'); + +sub TO_BUILD { + + my $self = shift; + + my $output = {category => $self->category, details => $self->details}; + + $output->{date} = $self->date if ($self->date); + $output->{entitlements} = $self->product_ids if (@{$self->entitlements}); + $output->{group_ids} = $self->group_ids if (@{$self->group_ids}); + $output->{product_ids} = $self->product_ids if (@{$self->product_ids}); + $output->{restart_required} = $self->restart_required if (defined $self->{restart_required}); + $output->{url} = $self->url if ($self->url); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Remediations.pm b/lib/CSAF/Type/Remediations.pm new file mode 100644 index 0000000..5e85c21 --- /dev/null +++ b/lib/CSAF/Type/Remediations.pm @@ -0,0 +1,26 @@ +package CSAF::Type::Remediations; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::Remediation'); + +sub get_category { + + my ($self, $category) = @_; + + my @items = (); + + foreach ($self->each) { + push @items, $_ if ($_->category eq $category); + } + + return \@items; + +} + +1; diff --git a/lib/CSAF/Type/RestartRequired.pm b/lib/CSAF/Type/RestartRequired.pm new file mode 100644 index 0000000..d7b2358 --- /dev/null +++ b/lib/CSAF/Type/RestartRequired.pm @@ -0,0 +1,26 @@ +package CSAF::Type::RestartRequired; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::Base'; + + +has category => (is => 'rw', required => 1); +has details => (is => 'rw'); + +sub TO_BUILD { + + my $self = shift; + + my $output = {category => $self->category}; + + $output->{details} = $self->details if ($self->details); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Revision.pm b/lib/CSAF/Type/Revision.pm new file mode 100644 index 0000000..e1060eb --- /dev/null +++ b/lib/CSAF/Type/Revision.pm @@ -0,0 +1,29 @@ +package CSAF::Type::Revision; + +use 5.010001; +use strict; +use warnings; + +use Moo; +use CSAF::Util qw(check_datetime); + +extends 'CSAF::Type::Base'; + +has date => (is => 'rw', required => 1, coerce => \&check_datetime); +has legacy_version => (is => 'rw'); +has number => (is => 'rw', required => 1); +has summary => (is => 'rw', required => 1); + +sub TO_BUILD { + + my $self = shift; + + my $output = {date => $self->date, number => $self->number, summary => $self->summary}; + + $output->{legacy_version} = $self->legacy_version if ($self->legacy_version); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/RevisionHistory.pm b/lib/CSAF/Type/RevisionHistory.pm new file mode 100644 index 0000000..e3254ab --- /dev/null +++ b/lib/CSAF/Type/RevisionHistory.pm @@ -0,0 +1,12 @@ +package CSAF::Type::RevisionHistory; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::Revision'); + +1; diff --git a/lib/CSAF/Type/Score.pm b/lib/CSAF/Type/Score.pm new file mode 100644 index 0000000..3aefcd7 --- /dev/null +++ b/lib/CSAF/Type/Score.pm @@ -0,0 +1,32 @@ +package CSAF::Type::Score; + +use 5.010001; +use strict; +use warnings; + +use Moo; +use CSAF::Type::CVSS3; +use CSAF::Type::CVSS2; + +extends 'CSAF::Type::Base'; + +has products => (is => 'rw', default => sub { [] }); +has cvss_v2 => (is => 'ro', coerce => sub { CSAF::Type::CVSS2->new(shift) }); +has cvss_v3 => (is => 'ro', coerce => sub { CSAF::Type::CVSS3->new(shift) }); + +sub TO_BUILD { + + my $self = shift; + + my $output = {}; + + $output->{products} = $self->products if (@{$self->products}); + + $output->{cvss_v3} = $self->cvss_v3 if (defined $self->{cvss_v3}); + $output->{cvss_v2} = $self->cvss_v2 if (defined $self->{cvss_v2}); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Scores.pm b/lib/CSAF/Type/Scores.pm new file mode 100644 index 0000000..0b96db8 --- /dev/null +++ b/lib/CSAF/Type/Scores.pm @@ -0,0 +1,12 @@ +package CSAF::Type::Scores; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::Score'); + +1; diff --git a/lib/CSAF/Type/TLP.pm b/lib/CSAF/Type/TLP.pm new file mode 100644 index 0000000..b19940f --- /dev/null +++ b/lib/CSAF/Type/TLP.pm @@ -0,0 +1,37 @@ +package CSAF::Type::TLP; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::Base'; + +my @LABELS = (qw[AMBER GREEN RED WHITE]); + +has label => ( + is => 'rw', + default => 'WHITE', + isa => sub { + my $test = shift; + Carp::croak 'Unknown TLP label' unless grep { $test eq $_ } @LABELS; + }, + coerce => sub { uc $_[0] } +); + +has url => (is => 'rw', default => 'https://www.first.org/tlp/'); + +sub TO_BUILD { + + my $self = shift; + + my $output = {}; + + $output->{label} = $self->label; + $output->{url} = $self->url if ($self->url); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Threat.pm b/lib/CSAF/Type/Threat.pm new file mode 100644 index 0000000..2f284ba --- /dev/null +++ b/lib/CSAF/Type/Threat.pm @@ -0,0 +1,34 @@ +package CSAF::Type::Threat; + +use 5.010001; +use strict; +use warnings; + +use CSAF::Util qw(check_datetime); + +use Moo; +extends 'CSAF::Type::Base'; + + +has category => (is => 'rw', required => 1); +has date => (is => 'rw', coerce => \&check_datetime); +has details => (is => 'rw', required => 1); +has group_ids => (is => 'rw', default => sub { [] }); +has product_ids => (is => 'rw', default => sub { [] }); + + +sub TO_BUILD { + + my $self = shift; + + my $output = {category => $self->category, details => $self->details}; + + $output->{date} = $self->date if ($self->date); + $output->{group_ids} = $self->group_ids if (@{$self->group_ids}); + $output->{product_ids} = $self->product_ids if (@{$self->product_ids}); + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Threats.pm b/lib/CSAF/Type/Threats.pm new file mode 100644 index 0000000..2fd30e8 --- /dev/null +++ b/lib/CSAF/Type/Threats.pm @@ -0,0 +1,26 @@ +package CSAF::Type::Threats; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::Threat'); + +sub get_category { + + my ($self, $category) = @_; + + my @items = (); + + foreach ($self->each) { + push @items, $_ if ($_->category eq $category); + } + + return \@items; + +} + +1; diff --git a/lib/CSAF/Type/Tracking.pm b/lib/CSAF/Type/Tracking.pm new file mode 100644 index 0000000..2dcdeec --- /dev/null +++ b/lib/CSAF/Type/Tracking.pm @@ -0,0 +1,53 @@ +package CSAF::Type::Tracking; + +use 5.010001; +use strict; +use warnings; +use version; + +use Moo; +use CSAF::Type::Generator; +use CSAF::Type::RevisionHistory; +use CSAF::Util qw(check_datetime); + +extends 'CSAF::Type::Base'; + +has ['current_release_date', 'initial_release_date'] => (is => 'rw', required => 1, coerce => \&check_datetime); + +has ['id', 'status'] => (is => 'rw', required => 1); + +has version => (is => 'rw', required => 1, coerce => sub {"$_[0]"}); + +has aliases => (is => 'rw', default => sub { [] }); + +sub generator { + my ($self, %params) = @_; + $self->{generator} ||= CSAF::Type::Generator->new(%params); +} + +sub revision_history { + my $self = shift; + $self->{revision_history} ||= CSAF::Type::RevisionHistory->new(@_); +} + +sub TO_BUILD { + + my $self = shift; + + my $output = { + id => $self->id, + current_release_date => $self->current_release_date, + initial_release_date => $self->initial_release_date, + revision_history => $self->revision_history->TO_BUILD, + status => $self->status, + version => $self->version, + }; + + $output->{aliases} = $self->aliases if (@{$self->aliases}); + $output->{generator} = $self->generator; + + return $output; + +} + +1; diff --git a/lib/CSAF/Type/Vulnerabilities.pm b/lib/CSAF/Type/Vulnerabilities.pm new file mode 100644 index 0000000..48559d4 --- /dev/null +++ b/lib/CSAF/Type/Vulnerabilities.pm @@ -0,0 +1,12 @@ +package CSAF::Type::Vulnerabilities; + +use 5.010001; +use strict; +use warnings; + +use Moo; +extends 'CSAF::Type::List'; + +has item_class_name => (is => 'ro', default => 'CSAF::Type::Vulnerability'); + +1; diff --git a/lib/CSAF/Type/Vulnerability.pm b/lib/CSAF/Type/Vulnerability.pm new file mode 100644 index 0000000..265123c --- /dev/null +++ b/lib/CSAF/Type/Vulnerability.pm @@ -0,0 +1,138 @@ +package CSAF::Type::Vulnerability; + +use 5.010001; +use strict; +use warnings; + +use Carp; + +use CSAF::Type::Acknowledgments; +use CSAF::Type::CWE; +use CSAF::Type::IDs; +use CSAF::Type::Involvements; +use CSAF::Type::Notes; +use CSAF::Type::ProductStatus; +use CSAF::Type::References; +use CSAF::Type::Remediations; +use CSAF::Type::Scores; +use CSAF::Type::Threats; + +use CSAF::Util qw(check_datetime get_weakness_name); + +use Moo; +extends 'CSAF::Type::Base'; + + +has [qw( + flags + title +)] => (is => 'rw'); + +has cve => (is => 'rw', isa => sub { Carp::croak 'Malformed CVE ID' if ($_[0] !~ /^CVE-\d{4}-\d{4,7}$/) }); +has discovery_date => (is => 'rw', coerce => \&check_datetime); +has release_date => (is => 'rw', coerce => \&check_datetime); + +sub cwe { + my ($self, %params) = @_; + $self->{cwe} ||= CSAF::Type::CWE->new(%params); +} + +sub product_status { + my ($self, %params) = @_; + $self->{product_status} ||= CSAF::Type::ProductStatus->new(%params); +} + +sub notes { + my $self = shift; + $self->{notes} ||= CSAF::Type::Notes->new(@_); +} + +sub references { + my $self = shift; + $self->{references} ||= CSAF::Type::References->new(@_); +} + +sub scores { + my ($self, %params) = @_; + $self->{scores} ||= CSAF::Type::Scores->new(%params); +} + +sub acknowledgments { + my $self = shift; + $self->{acknowledgments} ||= CSAF::Type::Acknowledgments->new(@_); +} + +sub remediations { + my $self = shift; + $self->{remediations} ||= CSAF::Type::Remediations->new(@_); +} + +sub threats { + my $self = shift; + $self->{threats} ||= CSAF::Type::Threats->new(@_); +} + +sub ids { + my $self = shift; + $self->{ids} ||= CSAF::Type::IDs->new(@_); +} + +sub involvements { + my $self = shift; + $self->{involvements} ||= CSAF::Type::Involvements->new(@_); +} + + +sub TO_BUILD { + + my $self = shift; + + my $output = {}; + + $output->{cve} = $self->cve if ($self->cve); + $output->{title} = $self->title if ($self->title); + + $output->{discovery_date} = check_datetime($self->discovery_date) if ($self->discovery_date); + $output->{release_date} = check_datetime($self->release_date) if ($self->release_date); + + if ($self->cwe->id) { + $output->{cwe} = $self->cwe; + } + + if (@{$self->acknowledgments->items}) { + $output->{acknowledgments} = $self->acknowledgments; + } + + if (@{$self->notes->items}) { + $output->{notes} = $self->notes; + } + + if (@{$self->scores->items}) { + $output->{scores} = $self->scores; + } + + if (@{$self->remediations->items}) { + $output->{remediations} = $self->remediations; + } + + if (@{$self->references->items}) { + $output->{references} = $self->references; + } + + if (@{$self->involvements->items}) { + $output->{involvements} = $self->involvements; + } + + if (my $product_status = $self->product_status->TO_BUILD) { + $output->{product_status} = $product_status; + } + + if (my $ids = $self->ids->TO_BUILD) { + $output->{ids} = $ids if (@$ids); # TODO + } + + return $output; + +} + +1; diff --git a/lib/CSAF/Util.pm b/lib/CSAF/Util.pm new file mode 100644 index 0000000..b48d4cf --- /dev/null +++ b/lib/CSAF/Util.pm @@ -0,0 +1,1536 @@ +package CSAF::Util; + +use 5.010001; +use strict; +use warnings; + +use Cpanel::JSON::XS; +use Time::Piece; +use File::Basename qw(dirname); +use File::Spec::Functions qw(catfile); + +use Exporter 'import'; + +our @EXPORT_OK = (qw( + schema_cache_path resources_path tt_templates_path + check_datetime tracking_id_to_well_filename + get_weakness_name collect_product_ids check_purl + file_read JSON +)); + +my $PURL_REGEXP = qr{^pkg:[A-Za-z\\.\\-\\+][A-Za-z0-9\\.\\-\\+]*/.+}; + +sub JSON { + Cpanel::JSON::XS->new->utf8->canonical->allow_nonref->allow_unknown->allow_blessed->convert_blessed + ->stringify_infnan->escape_slash->allow_dupkeys->pretty; +} + +sub check_purl { + return (shift =~ /$PURL_REGEXP/); +} + +sub get_weakness_name { + + my $weakness = shift; + return unless $weakness; + + state $weaknesses = {}; + + unless (keys %{$weaknesses}) { + while (my $line = ) { + chomp($line); + my ($cwe_id, $name, $type) = split '\|', $line; + $weaknesses->{$cwe_id} = $name; + } + } + + $weaknesses->{$weakness}; + +} + +sub Time::Piece::TO_JSON { + shift->datetime; +} + + +sub schema_cache_path { + return catfile(resources_path(), 'cache'); +} + +sub tt_templates_path { + return catfile(resources_path(), 'template'); +} + +sub resources_path { + return catfile(dirname(__FILE__), 'resources'); +} + +sub check_datetime { + + my $datetime = shift; + return unless $datetime; + + return $datetime if ($datetime->isa('Time::Piece')); + + return Time::Piece->new($datetime) if ($datetime =~ /^([0-9]+)$/); + return Time::Piece->new if ($datetime eq 'now'); + + return Time::Piece->strptime($1, '%Y-%m-%dT%H:%M:%S') if ($datetime =~ /(\d{4}-\d{2}-\d{2}[T]\d{2}:\d{2}:\d{2})/); + return Time::Piece->strptime($1, '%Y-%m-%d %H:%M:%S') if ($datetime =~ /(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})/); + return Time::Piece->strptime($1, '%Y-%m-%d') if ($datetime =~ /(\d{4}-\d{2}-\d{2})/); + +} + +sub tracking_id_to_well_filename { + + my $id = shift; + + $id = lc $id; + $id =~ s/[^+\-a-z0-9]+/_/; # Rif. 5.1 (Additional Conventions - Filename) + + return "$id.json"; + +} + +sub collect_product_ids { + + my $item = shift; + my @product_ids = (); + + my $ref_item = ref($item); + + if ($ref_item =~ /Branch$/) { + + if ($item->has_product) { + push @product_ids, $item->product->product_id; + } + + foreach (@{$item->branches->items}) { + push @product_ids, collect_product_ids($_); + } + + } + + if ($ref_item =~ /FullProductName$/) { + push @product_ids, $item->product_id; + } + + return @product_ids; + +} + + +sub file_read { + + my ($file) = @_; + + my $content = do { + open(my $fh, '<', $file) or Carp::croak qq{Failed to read file: $!}; + local $/ = undef; + <$fh>; + }; + + return $content; + +} + + +1; + +__DATA__ +CWE-1|DEPRECATED: Location|category +CWE-2|7PK - Environment|category +CWE-3|DEPRECATED: Technology-specific Environment Issues|category +CWE-4|DEPRECATED: J2EE Environment Issues|category +CWE-5|J2EE Misconfiguration: Data Transmission Without Encryption|weakness +CWE-6|J2EE Misconfiguration: Insufficient Session-ID Length|weakness +CWE-7|J2EE Misconfiguration: Missing Custom Error Page|weakness +CWE-8|J2EE Misconfiguration: Entity Bean Declared Remote|weakness +CWE-9|J2EE Misconfiguration: Weak Access Permissions for EJB Methods|weakness +CWE-10|DEPRECATED: ASP.NET Environment Issues|category +CWE-11|ASP.NET Misconfiguration: Creating Debug Binary|weakness +CWE-12|ASP.NET Misconfiguration: Missing Custom Error Page|weakness +CWE-13|ASP.NET Misconfiguration: Password in Configuration File|weakness +CWE-14|Compiler Removal of Code to Clear Buffers|weakness +CWE-15|External Control of System or Configuration Setting|weakness +CWE-16|Configuration|category +CWE-17|DEPRECATED: Code|category +CWE-18|DEPRECATED: Source Code|category +CWE-19|Data Processing Errors|category +CWE-20|Improper Input Validation|weakness +CWE-21|DEPRECATED: Pathname Traversal and Equivalence Errors|category +CWE-22|Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')|weakness +CWE-23|Relative Path Traversal|weakness +CWE-24|Path Traversal: '../filedir'|weakness +CWE-25|Path Traversal: '/../filedir'|weakness +CWE-26|Path Traversal: '/dir/../filename'|weakness +CWE-27|Path Traversal: 'dir/../../filename'|weakness +CWE-28|Path Traversal: '..\filedir'|weakness +CWE-29|Path Traversal: '\..\filename'|weakness +CWE-30|Path Traversal: '\dir\..\filename'|weakness +CWE-31|Path Traversal: 'dir\..\..\filename'|weakness +CWE-32|Path Traversal: '...' (Triple Dot)|weakness +CWE-33|Path Traversal: '....' (Multiple Dot)|weakness +CWE-34|Path Traversal: '....//'|weakness +CWE-35|Path Traversal: '.../...//'|weakness +CWE-36|Absolute Path Traversal|weakness +CWE-37|Path Traversal: '/absolute/pathname/here'|weakness +CWE-38|Path Traversal: '\absolute\pathname\here'|weakness +CWE-39|Path Traversal: 'C:dirname'|weakness +CWE-40|Path Traversal: '\\UNC\share\name\' (Windows UNC Share)|weakness +CWE-41|Improper Resolution of Path Equivalence|weakness +CWE-42|Path Equivalence: 'filename.' (Trailing Dot)|weakness +CWE-43|Path Equivalence: 'filename....' (Multiple Trailing Dot)|weakness +CWE-44|Path Equivalence: 'file.name' (Internal Dot)|weakness +CWE-45|Path Equivalence: 'file...name' (Multiple Internal Dot)|weakness +CWE-46|Path Equivalence: 'filename ' (Trailing Space)|weakness +CWE-47|Path Equivalence: ' filename' (Leading Space)|weakness +CWE-48|Path Equivalence: 'file name' (Internal Whitespace)|weakness +CWE-49|Path Equivalence: 'filename/' (Trailing Slash)|weakness +CWE-50|Path Equivalence: '//multiple/leading/slash'|weakness +CWE-51|Path Equivalence: '/multiple//internal/slash'|weakness +CWE-52|Path Equivalence: '/multiple/trailing/slash//'|weakness +CWE-53|Path Equivalence: '\multiple\\internal\backslash'|weakness +CWE-54|Path Equivalence: 'filedir\' (Trailing Backslash)|weakness +CWE-55|Path Equivalence: '/./' (Single Dot Directory)|weakness +CWE-56|Path Equivalence: 'filedir*' (Wildcard)|weakness +CWE-57|Path Equivalence: 'fakedir/../realdir/filename'|weakness +CWE-58|Path Equivalence: Windows 8.3 Filename|weakness +CWE-59|Improper Link Resolution Before File Access ('Link Following')|weakness +CWE-60|DEPRECATED: UNIX Path Link Problems|category +CWE-61|UNIX Symbolic Link (Symlink) Following|weakness +CWE-62|UNIX Hard Link|weakness +CWE-63|DEPRECATED: Windows Path Link Problems|category +CWE-64|Windows Shortcut Following (.LNK)|weakness +CWE-65|Windows Hard Link|weakness +CWE-66|Improper Handling of File Names that Identify Virtual Resources|weakness +CWE-67|Improper Handling of Windows Device Names|weakness +CWE-68|DEPRECATED: Windows Virtual File Problems|category +CWE-69|Improper Handling of Windows ::DATA Alternate Data Stream|weakness +CWE-70|DEPRECATED: Mac Virtual File Problems|category +CWE-71|DEPRECATED: Apple '.DS_Store'|weakness +CWE-72|Improper Handling of Apple HFS+ Alternate Data Stream Path|weakness +CWE-73|External Control of File Name or Path|weakness +CWE-74|Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')|weakness +CWE-75|Failure to Sanitize Special Elements into a Different Plane (Special Element Injection)|weakness +CWE-76|Improper Neutralization of Equivalent Special Elements|weakness +CWE-77|Improper Neutralization of Special Elements used in a Command ('Command Injection')|weakness +CWE-78|Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')|weakness +CWE-79|Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')|weakness +CWE-80|Improper Neutralization of Script-Related HTML Tags in a Web Page (Basic XSS)|weakness +CWE-81|Improper Neutralization of Script in an Error Message Web Page|weakness +CWE-82|Improper Neutralization of Script in Attributes of IMG Tags in a Web Page|weakness +CWE-83|Improper Neutralization of Script in Attributes in a Web Page|weakness +CWE-84|Improper Neutralization of Encoded URI Schemes in a Web Page|weakness +CWE-85|Doubled Character XSS Manipulations|weakness +CWE-86|Improper Neutralization of Invalid Characters in Identifiers in Web Pages|weakness +CWE-87|Improper Neutralization of Alternate XSS Syntax|weakness +CWE-88|Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')|weakness +CWE-89|Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')|weakness +CWE-90|Improper Neutralization of Special Elements used in an LDAP Query ('LDAP Injection')|weakness +CWE-91|XML Injection (aka Blind XPath Injection)|weakness +CWE-92|DEPRECATED: Improper Sanitization of Custom Special Characters|weakness +CWE-93|Improper Neutralization of CRLF Sequences ('CRLF Injection')|weakness +CWE-94|Improper Control of Generation of Code ('Code Injection')|weakness +CWE-95|Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')|weakness +CWE-96|Improper Neutralization of Directives in Statically Saved Code ('Static Code Injection')|weakness +CWE-97|Improper Neutralization of Server-Side Includes (SSI) Within a Web Page|weakness +CWE-98|Improper Control of Filename for Include/Require Statement in PHP Program ('PHP Remote File Inclusion')|weakness +CWE-99|Improper Control of Resource Identifiers ('Resource Injection')|weakness +CWE-100|DEPRECATED: Technology-Specific Input Validation Problems|category +CWE-101|DEPRECATED: Struts Validation Problems|category +CWE-102|Struts: Duplicate Validation Forms|weakness +CWE-103|Struts: Incomplete validate() Method Definition|weakness +CWE-104|Struts: Form Bean Does Not Extend Validation Class|weakness +CWE-105|Struts: Form Field Without Validator|weakness +CWE-106|Struts: Plug-in Framework not in Use|weakness +CWE-107|Struts: Unused Validation Form|weakness +CWE-108|Struts: Unvalidated Action Form|weakness +CWE-109|Struts: Validator Turned Off|weakness +CWE-110|Struts: Validator Without Form Field|weakness +CWE-111|Direct Use of Unsafe JNI|weakness +CWE-112|Missing XML Validation|weakness +CWE-113|Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Request/Response Splitting')|weakness +CWE-114|Process Control|weakness +CWE-115|Misinterpretation of Input|weakness +CWE-116|Improper Encoding or Escaping of Output|weakness +CWE-117|Improper Output Neutralization for Logs|weakness +CWE-118|Incorrect Access of Indexable Resource ('Range Error')|weakness +CWE-119|Improper Restriction of Operations within the Bounds of a Memory Buffer|weakness +CWE-120|Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')|weakness +CWE-121|Stack-based Buffer Overflow|weakness +CWE-122|Heap-based Buffer Overflow|weakness +CWE-123|Write-what-where Condition|weakness +CWE-124|Buffer Underwrite ('Buffer Underflow')|weakness +CWE-125|Out-of-bounds Read|weakness +CWE-126|Buffer Over-read|weakness +CWE-127|Buffer Under-read|weakness +CWE-128|Wrap-around Error|weakness +CWE-129|Improper Validation of Array Index|weakness +CWE-130|Improper Handling of Length Parameter Inconsistency|weakness +CWE-131|Incorrect Calculation of Buffer Size|weakness +CWE-132|DEPRECATED: Miscalculated Null Termination|weakness +CWE-133|String Errors|category +CWE-134|Use of Externally-Controlled Format String|weakness +CWE-135|Incorrect Calculation of Multi-Byte String Length|weakness +CWE-136|Type Errors|category +CWE-137|Data Neutralization Issues|category +CWE-138|Improper Neutralization of Special Elements|weakness +CWE-139|DEPRECATED: General Special Element Problems|category +CWE-140|Improper Neutralization of Delimiters|weakness +CWE-141|Improper Neutralization of Parameter/Argument Delimiters|weakness +CWE-142|Improper Neutralization of Value Delimiters|weakness +CWE-143|Improper Neutralization of Record Delimiters|weakness +CWE-144|Improper Neutralization of Line Delimiters|weakness +CWE-145|Improper Neutralization of Section Delimiters|weakness +CWE-146|Improper Neutralization of Expression/Command Delimiters|weakness +CWE-147|Improper Neutralization of Input Terminators|weakness +CWE-148|Improper Neutralization of Input Leaders|weakness +CWE-149|Improper Neutralization of Quoting Syntax|weakness +CWE-150|Improper Neutralization of Escape, Meta, or Control Sequences|weakness +CWE-151|Improper Neutralization of Comment Delimiters|weakness +CWE-152|Improper Neutralization of Macro Symbols|weakness +CWE-153|Improper Neutralization of Substitution Characters|weakness +CWE-154|Improper Neutralization of Variable Name Delimiters|weakness +CWE-155|Improper Neutralization of Wildcards or Matching Symbols|weakness +CWE-156|Improper Neutralization of Whitespace|weakness +CWE-157|Failure to Sanitize Paired Delimiters|weakness +CWE-158|Improper Neutralization of Null Byte or NUL Character|weakness +CWE-159|Improper Handling of Invalid Use of Special Elements|weakness +CWE-160|Improper Neutralization of Leading Special Elements|weakness +CWE-161|Improper Neutralization of Multiple Leading Special Elements|weakness +CWE-162|Improper Neutralization of Trailing Special Elements|weakness +CWE-163|Improper Neutralization of Multiple Trailing Special Elements|weakness +CWE-164|Improper Neutralization of Internal Special Elements|weakness +CWE-165|Improper Neutralization of Multiple Internal Special Elements|weakness +CWE-166|Improper Handling of Missing Special Element|weakness +CWE-167|Improper Handling of Additional Special Element|weakness +CWE-168|Improper Handling of Inconsistent Special Elements|weakness +CWE-169|DEPRECATED: Technology-Specific Special Elements|category +CWE-170|Improper Null Termination|weakness +CWE-171|DEPRECATED: Cleansing, Canonicalization, and Comparison Errors|category +CWE-172|Encoding Error|weakness +CWE-173|Improper Handling of Alternate Encoding|weakness +CWE-174|Double Decoding of the Same Data|weakness +CWE-175|Improper Handling of Mixed Encoding|weakness +CWE-176|Improper Handling of Unicode Encoding|weakness +CWE-177|Improper Handling of URL Encoding (Hex Encoding)|weakness +CWE-178|Improper Handling of Case Sensitivity|weakness +CWE-179|Incorrect Behavior Order: Early Validation|weakness +CWE-180|Incorrect Behavior Order: Validate Before Canonicalize|weakness +CWE-181|Incorrect Behavior Order: Validate Before Filter|weakness +CWE-182|Collapse of Data into Unsafe Value|weakness +CWE-183|Permissive List of Allowed Inputs|weakness +CWE-184|Incomplete List of Disallowed Inputs|weakness +CWE-185|Incorrect Regular Expression|weakness +CWE-186|Overly Restrictive Regular Expression|weakness +CWE-187|Partial String Comparison|weakness +CWE-188|Reliance on Data/Memory Layout|weakness +CWE-189|Numeric Errors|category +CWE-190|Integer Overflow or Wraparound|weakness +CWE-191|Integer Underflow (Wrap or Wraparound)|weakness +CWE-192|Integer Coercion Error|weakness +CWE-193|Off-by-one Error|weakness +CWE-194|Unexpected Sign Extension|weakness +CWE-195|Signed to Unsigned Conversion Error|weakness +CWE-196|Unsigned to Signed Conversion Error|weakness +CWE-197|Numeric Truncation Error|weakness +CWE-198|Use of Incorrect Byte Ordering|weakness +CWE-199|Information Management Errors|category +CWE-200|Exposure of Sensitive Information to an Unauthorized Actor|weakness +CWE-201|Insertion of Sensitive Information Into Sent Data|weakness +CWE-202|Exposure of Sensitive Information Through Data Queries|weakness +CWE-203|Observable Discrepancy|weakness +CWE-204|Observable Response Discrepancy|weakness +CWE-205|Observable Behavioral Discrepancy|weakness +CWE-206|Observable Internal Behavioral Discrepancy|weakness +CWE-207|Observable Behavioral Discrepancy With Equivalent Products|weakness +CWE-208|Observable Timing Discrepancy|weakness +CWE-209|Generation of Error Message Containing Sensitive Information|weakness +CWE-210|Self-generated Error Message Containing Sensitive Information|weakness +CWE-211|Externally-Generated Error Message Containing Sensitive Information|weakness +CWE-212|Improper Removal of Sensitive Information Before Storage or Transfer|weakness +CWE-213|Exposure of Sensitive Information Due to Incompatible Policies|weakness +CWE-214|Invocation of Process Using Visible Sensitive Information|weakness +CWE-215|Insertion of Sensitive Information Into Debugging Code|weakness +CWE-216|DEPRECATED: Containment Errors (Container Errors)|weakness +CWE-217|DEPRECATED: Failure to Protect Stored Data from Modification|weakness +CWE-218|DEPRECATED: Failure to provide confidentiality for stored data|weakness +CWE-219|Storage of File with Sensitive Data Under Web Root|weakness +CWE-220|Storage of File With Sensitive Data Under FTP Root|weakness +CWE-221|Information Loss or Omission|weakness +CWE-222|Truncation of Security-relevant Information|weakness +CWE-223|Omission of Security-relevant Information|weakness +CWE-224|Obscured Security-relevant Information by Alternate Name|weakness +CWE-225|DEPRECATED: General Information Management Problems|weakness +CWE-226|Sensitive Information in Resource Not Removed Before Reuse|weakness +CWE-227|7PK - API Abuse|category +CWE-228|Improper Handling of Syntactically Invalid Structure|weakness +CWE-229|Improper Handling of Values|weakness +CWE-230|Improper Handling of Missing Values|weakness +CWE-231|Improper Handling of Extra Values|weakness +CWE-232|Improper Handling of Undefined Values|weakness +CWE-233|Improper Handling of Parameters|weakness +CWE-234|Failure to Handle Missing Parameter|weakness +CWE-235|Improper Handling of Extra Parameters|weakness +CWE-236|Improper Handling of Undefined Parameters|weakness +CWE-237|Improper Handling of Structural Elements|weakness +CWE-238|Improper Handling of Incomplete Structural Elements|weakness +CWE-239|Failure to Handle Incomplete Element|weakness +CWE-240|Improper Handling of Inconsistent Structural Elements|weakness +CWE-241|Improper Handling of Unexpected Data Type|weakness +CWE-242|Use of Inherently Dangerous Function|weakness +CWE-243|Creation of chroot Jail Without Changing Working Directory|weakness +CWE-244|Improper Clearing of Heap Memory Before Release ('Heap Inspection')|weakness +CWE-245|J2EE Bad Practices: Direct Management of Connections|weakness +CWE-246|J2EE Bad Practices: Direct Use of Sockets|weakness +CWE-247|DEPRECATED: Reliance on DNS Lookups in a Security Decision|weakness +CWE-248|Uncaught Exception|weakness +CWE-249|DEPRECATED: Often Misused: Path Manipulation|weakness +CWE-250|Execution with Unnecessary Privileges|weakness +CWE-251|Often Misused: String Management|category +CWE-252|Unchecked Return Value|weakness +CWE-253|Incorrect Check of Function Return Value|weakness +CWE-254|7PK - Security Features|category +CWE-255|Credentials Management Errors|category +CWE-256|Plaintext Storage of a Password|weakness +CWE-257|Storing Passwords in a Recoverable Format|weakness +CWE-258|Empty Password in Configuration File|weakness +CWE-259|Use of Hard-coded Password|weakness +CWE-260|Password in Configuration File|weakness +CWE-261|Weak Encoding for Password|weakness +CWE-262|Not Using Password Aging|weakness +CWE-263|Password Aging with Long Expiration|weakness +CWE-264|Permissions, Privileges, and Access Controls|category +CWE-265|Privilege Issues|category +CWE-266|Incorrect Privilege Assignment|weakness +CWE-267|Privilege Defined With Unsafe Actions|weakness +CWE-268|Privilege Chaining|weakness +CWE-269|Improper Privilege Management|weakness +CWE-270|Privilege Context Switching Error|weakness +CWE-271|Privilege Dropping / Lowering Errors|weakness +CWE-272|Least Privilege Violation|weakness +CWE-273|Improper Check for Dropped Privileges|weakness +CWE-274|Improper Handling of Insufficient Privileges|weakness +CWE-275|Permission Issues|category +CWE-276|Incorrect Default Permissions|weakness +CWE-277|Insecure Inherited Permissions|weakness +CWE-278|Insecure Preserved Inherited Permissions|weakness +CWE-279|Incorrect Execution-Assigned Permissions|weakness +CWE-280|Improper Handling of Insufficient Permissions or Privileges |weakness +CWE-281|Improper Preservation of Permissions|weakness +CWE-282|Improper Ownership Management|weakness +CWE-283|Unverified Ownership|weakness +CWE-284|Improper Access Control|weakness +CWE-285|Improper Authorization|weakness +CWE-286|Incorrect User Management|weakness +CWE-287|Improper Authentication|weakness +CWE-288|Authentication Bypass Using an Alternate Path or Channel|weakness +CWE-289|Authentication Bypass by Alternate Name|weakness +CWE-290|Authentication Bypass by Spoofing|weakness +CWE-291|Reliance on IP Address for Authentication|weakness +CWE-292|DEPRECATED: Trusting Self-reported DNS Name|weakness +CWE-293|Using Referer Field for Authentication|weakness +CWE-294|Authentication Bypass by Capture-replay|weakness +CWE-295|Improper Certificate Validation|weakness +CWE-296|Improper Following of a Certificate's Chain of Trust|weakness +CWE-297|Improper Validation of Certificate with Host Mismatch|weakness +CWE-298|Improper Validation of Certificate Expiration|weakness +CWE-299|Improper Check for Certificate Revocation|weakness +CWE-300|Channel Accessible by Non-Endpoint|weakness +CWE-301|Reflection Attack in an Authentication Protocol|weakness +CWE-302|Authentication Bypass by Assumed-Immutable Data|weakness +CWE-303|Incorrect Implementation of Authentication Algorithm|weakness +CWE-304|Missing Critical Step in Authentication|weakness +CWE-305|Authentication Bypass by Primary Weakness|weakness +CWE-306|Missing Authentication for Critical Function|weakness +CWE-307|Improper Restriction of Excessive Authentication Attempts|weakness +CWE-308|Use of Single-factor Authentication|weakness +CWE-309|Use of Password System for Primary Authentication|weakness +CWE-310|Cryptographic Issues|category +CWE-311|Missing Encryption of Sensitive Data|weakness +CWE-312|Cleartext Storage of Sensitive Information|weakness +CWE-313|Cleartext Storage in a File or on Disk|weakness +CWE-314|Cleartext Storage in the Registry|weakness +CWE-315|Cleartext Storage of Sensitive Information in a Cookie|weakness +CWE-316|Cleartext Storage of Sensitive Information in Memory|weakness +CWE-317|Cleartext Storage of Sensitive Information in GUI|weakness +CWE-318|Cleartext Storage of Sensitive Information in Executable|weakness +CWE-319|Cleartext Transmission of Sensitive Information|weakness +CWE-320|Key Management Errors|category +CWE-321|Use of Hard-coded Cryptographic Key|weakness +CWE-322|Key Exchange without Entity Authentication|weakness +CWE-323|Reusing a Nonce, Key Pair in Encryption|weakness +CWE-324|Use of a Key Past its Expiration Date|weakness +CWE-325|Missing Cryptographic Step|weakness +CWE-326|Inadequate Encryption Strength|weakness +CWE-327|Use of a Broken or Risky Cryptographic Algorithm|weakness +CWE-328|Use of Weak Hash|weakness +CWE-329|Generation of Predictable IV with CBC Mode|weakness +CWE-330|Use of Insufficiently Random Values|weakness +CWE-331|Insufficient Entropy|weakness +CWE-332|Insufficient Entropy in PRNG|weakness +CWE-333|Improper Handling of Insufficient Entropy in TRNG|weakness +CWE-334|Small Space of Random Values|weakness +CWE-335|Incorrect Usage of Seeds in Pseudo-Random Number Generator (PRNG)|weakness +CWE-336|Same Seed in Pseudo-Random Number Generator (PRNG)|weakness +CWE-337|Predictable Seed in Pseudo-Random Number Generator (PRNG)|weakness +CWE-338|Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)|weakness +CWE-339|Small Seed Space in PRNG|weakness +CWE-340|Generation of Predictable Numbers or Identifiers|weakness +CWE-341|Predictable from Observable State|weakness +CWE-342|Predictable Exact Value from Previous Values|weakness +CWE-343|Predictable Value Range from Previous Values|weakness +CWE-344|Use of Invariant Value in Dynamically Changing Context|weakness +CWE-345|Insufficient Verification of Data Authenticity|weakness +CWE-346|Origin Validation Error|weakness +CWE-347|Improper Verification of Cryptographic Signature|weakness +CWE-348|Use of Less Trusted Source|weakness +CWE-349|Acceptance of Extraneous Untrusted Data With Trusted Data|weakness +CWE-350|Reliance on Reverse DNS Resolution for a Security-Critical Action|weakness +CWE-351|Insufficient Type Distinction|weakness +CWE-352|Cross-Site Request Forgery (CSRF)|weakness +CWE-353|Missing Support for Integrity Check|weakness +CWE-354|Improper Validation of Integrity Check Value|weakness +CWE-355|User Interface Security Issues|category +CWE-356|Product UI does not Warn User of Unsafe Actions|weakness +CWE-357|Insufficient UI Warning of Dangerous Operations|weakness +CWE-358|Improperly Implemented Security Check for Standard|weakness +CWE-359|Exposure of Private Personal Information to an Unauthorized Actor|weakness +CWE-360|Trust of System Event Data|weakness +CWE-361|7PK - Time and State|category +CWE-362|Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')|weakness +CWE-363|Race Condition Enabling Link Following|weakness +CWE-364|Signal Handler Race Condition|weakness +CWE-365|DEPRECATED: Race Condition in Switch|weakness +CWE-366|Race Condition within a Thread|weakness +CWE-367|Time-of-check Time-of-use (TOCTOU) Race Condition|weakness +CWE-368|Context Switching Race Condition|weakness +CWE-369|Divide By Zero|weakness +CWE-370|Missing Check for Certificate Revocation after Initial Check|weakness +CWE-371|State Issues|category +CWE-372|Incomplete Internal State Distinction|weakness +CWE-373|DEPRECATED: State Synchronization Error|weakness +CWE-374|Passing Mutable Objects to an Untrusted Method|weakness +CWE-375|Returning a Mutable Object to an Untrusted Caller|weakness +CWE-376|DEPRECATED: Temporary File Issues|category +CWE-377|Insecure Temporary File|weakness +CWE-378|Creation of Temporary File With Insecure Permissions|weakness +CWE-379|Creation of Temporary File in Directory with Insecure Permissions|weakness +CWE-380|DEPRECATED: Technology-Specific Time and State Issues|category +CWE-381|DEPRECATED: J2EE Time and State Issues|category +CWE-382|J2EE Bad Practices: Use of System.exit()|weakness +CWE-383|J2EE Bad Practices: Direct Use of Threads|weakness +CWE-384|Session Fixation|weakness +CWE-385|Covert Timing Channel|weakness +CWE-386|Symbolic Name not Mapping to Correct Object|weakness +CWE-387|Signal Errors|category +CWE-388|7PK - Errors|category +CWE-389|Error Conditions, Return Values, Status Codes|category +CWE-390|Detection of Error Condition Without Action|weakness +CWE-391|Unchecked Error Condition|weakness +CWE-392|Missing Report of Error Condition|weakness +CWE-393|Return of Wrong Status Code|weakness +CWE-394|Unexpected Status Code or Return Value|weakness +CWE-395|Use of NullPointerException Catch to Detect NULL Pointer Dereference|weakness +CWE-396|Declaration of Catch for Generic Exception|weakness +CWE-397|Declaration of Throws for Generic Exception|weakness +CWE-398|7PK - Code Quality|category +CWE-399|Resource Management Errors|category +CWE-400|Uncontrolled Resource Consumption|weakness +CWE-401|Missing Release of Memory after Effective Lifetime|weakness +CWE-402|Transmission of Private Resources into a New Sphere ('Resource Leak')|weakness +CWE-403|Exposure of File Descriptor to Unintended Control Sphere ('File Descriptor Leak')|weakness +CWE-404|Improper Resource Shutdown or Release|weakness +CWE-405|Asymmetric Resource Consumption (Amplification)|weakness +CWE-406|Insufficient Control of Network Message Volume (Network Amplification)|weakness +CWE-407|Inefficient Algorithmic Complexity|weakness +CWE-408|Incorrect Behavior Order: Early Amplification|weakness +CWE-409|Improper Handling of Highly Compressed Data (Data Amplification)|weakness +CWE-410|Insufficient Resource Pool|weakness +CWE-411|Resource Locking Problems|category +CWE-412|Unrestricted Externally Accessible Lock|weakness +CWE-413|Improper Resource Locking|weakness +CWE-414|Missing Lock Check|weakness +CWE-415|Double Free|weakness +CWE-416|Use After Free|weakness +CWE-417|Communication Channel Errors|category +CWE-418|DEPRECATED: Channel Errors|category +CWE-419|Unprotected Primary Channel|weakness +CWE-420|Unprotected Alternate Channel|weakness +CWE-421|Race Condition During Access to Alternate Channel|weakness +CWE-422|Unprotected Windows Messaging Channel ('Shatter')|weakness +CWE-423|DEPRECATED: Proxied Trusted Channel|weakness +CWE-424|Improper Protection of Alternate Path|weakness +CWE-425|Direct Request ('Forced Browsing')|weakness +CWE-426|Untrusted Search Path|weakness +CWE-427|Uncontrolled Search Path Element|weakness +CWE-428|Unquoted Search Path or Element|weakness +CWE-429|Handler Errors|category +CWE-430|Deployment of Wrong Handler|weakness +CWE-431|Missing Handler|weakness +CWE-432|Dangerous Signal Handler not Disabled During Sensitive Operations|weakness +CWE-433|Unparsed Raw Web Content Delivery|weakness +CWE-434|Unrestricted Upload of File with Dangerous Type|weakness +CWE-435|Improper Interaction Between Multiple Correctly-Behaving Entities|weakness +CWE-436|Interpretation Conflict|weakness +CWE-437|Incomplete Model of Endpoint Features|weakness +CWE-438|Behavioral Problems|category +CWE-439|Behavioral Change in New Version or Environment|weakness +CWE-440|Expected Behavior Violation|weakness +CWE-441|Unintended Proxy or Intermediary ('Confused Deputy')|weakness +CWE-442|DEPRECATED: Web Problems|category +CWE-443|DEPRECATED: HTTP response splitting|weakness +CWE-444|Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling')|weakness +CWE-445|DEPRECATED: User Interface Errors|category +CWE-446|UI Discrepancy for Security Feature|weakness +CWE-447|Unimplemented or Unsupported Feature in UI|weakness +CWE-448|Obsolete Feature in UI|weakness +CWE-449|The UI Performs the Wrong Action|weakness +CWE-450|Multiple Interpretations of UI Input|weakness +CWE-451|User Interface (UI) Misrepresentation of Critical Information|weakness +CWE-452|Initialization and Cleanup Errors|category +CWE-453|Insecure Default Variable Initialization|weakness +CWE-454|External Initialization of Trusted Variables or Data Stores|weakness +CWE-455|Non-exit on Failed Initialization|weakness +CWE-456|Missing Initialization of a Variable|weakness +CWE-457|Use of Uninitialized Variable|weakness +CWE-458|DEPRECATED: Incorrect Initialization|weakness +CWE-459|Incomplete Cleanup|weakness +CWE-460|Improper Cleanup on Thrown Exception|weakness +CWE-461|DEPRECATED: Data Structure Issues|category +CWE-462|Duplicate Key in Associative List (Alist)|weakness +CWE-463|Deletion of Data Structure Sentinel|weakness +CWE-464|Addition of Data Structure Sentinel|weakness +CWE-465|Pointer Issues|category +CWE-466|Return of Pointer Value Outside of Expected Range|weakness +CWE-467|Use of sizeof() on a Pointer Type|weakness +CWE-468|Incorrect Pointer Scaling|weakness +CWE-469|Use of Pointer Subtraction to Determine Size|weakness +CWE-470|Use of Externally-Controlled Input to Select Classes or Code ('Unsafe Reflection')|weakness +CWE-471|Modification of Assumed-Immutable Data (MAID)|weakness +CWE-472|External Control of Assumed-Immutable Web Parameter|weakness +CWE-473|PHP External Variable Modification|weakness +CWE-474|Use of Function with Inconsistent Implementations|weakness +CWE-475|Undefined Behavior for Input to API|weakness +CWE-476|NULL Pointer Dereference|weakness +CWE-477|Use of Obsolete Function|weakness +CWE-478|Missing Default Case in Multiple Condition Expression|weakness +CWE-479|Signal Handler Use of a Non-reentrant Function|weakness +CWE-480|Use of Incorrect Operator|weakness +CWE-481|Assigning instead of Comparing|weakness +CWE-482|Comparing instead of Assigning|weakness +CWE-483|Incorrect Block Delimitation|weakness +CWE-484|Omitted Break Statement in Switch|weakness +CWE-485|7PK - Encapsulation|category +CWE-486|Comparison of Classes by Name|weakness +CWE-487|Reliance on Package-level Scope|weakness +CWE-488|Exposure of Data Element to Wrong Session|weakness +CWE-489|Active Debug Code|weakness +CWE-490|DEPRECATED: Mobile Code Issues|category +CWE-491|Public cloneable() Method Without Final ('Object Hijack')|weakness +CWE-492|Use of Inner Class Containing Sensitive Data|weakness +CWE-493|Critical Public Variable Without Final Modifier|weakness +CWE-494|Download of Code Without Integrity Check|weakness +CWE-495|Private Data Structure Returned From A Public Method|weakness +CWE-496|Public Data Assigned to Private Array-Typed Field|weakness +CWE-497|Exposure of Sensitive System Information to an Unauthorized Control Sphere|weakness +CWE-498|Cloneable Class Containing Sensitive Information|weakness +CWE-499|Serializable Class Containing Sensitive Data|weakness +CWE-500|Public Static Field Not Marked Final|weakness +CWE-501|Trust Boundary Violation|weakness +CWE-502|Deserialization of Untrusted Data|weakness +CWE-503|DEPRECATED: Byte/Object Code|category +CWE-504|DEPRECATED: Motivation/Intent|category +CWE-505|DEPRECATED: Intentionally Introduced Weakness|category +CWE-506|Embedded Malicious Code|weakness +CWE-507|Trojan Horse|weakness +CWE-508|Non-Replicating Malicious Code|weakness +CWE-509|Replicating Malicious Code (Virus or Worm)|weakness +CWE-510|Trapdoor|weakness +CWE-511|Logic/Time Bomb|weakness +CWE-512|Spyware|weakness +CWE-513|DEPRECATED: Intentionally Introduced Nonmalicious Weakness|category +CWE-514|Covert Channel|weakness +CWE-515|Covert Storage Channel|weakness +CWE-516|DEPRECATED: Covert Timing Channel|weakness +CWE-517|DEPRECATED: Other Intentional, Nonmalicious Weakness|category +CWE-518|DEPRECATED: Inadvertently Introduced Weakness|category +CWE-519|DEPRECATED: .NET Environment Issues|category +CWE-520|.NET Misconfiguration: Use of Impersonation|weakness +CWE-521|Weak Password Requirements|weakness +CWE-522|Insufficiently Protected Credentials|weakness +CWE-523|Unprotected Transport of Credentials|weakness +CWE-524|Use of Cache Containing Sensitive Information|weakness +CWE-525|Use of Web Browser Cache Containing Sensitive Information|weakness +CWE-526|Cleartext Storage of Sensitive Information in an Environment Variable|weakness +CWE-527|Exposure of Version-Control Repository to an Unauthorized Control Sphere|weakness +CWE-528|Exposure of Core Dump File to an Unauthorized Control Sphere|weakness +CWE-529|Exposure of Access Control List Files to an Unauthorized Control Sphere|weakness +CWE-530|Exposure of Backup File to an Unauthorized Control Sphere|weakness +CWE-531|Inclusion of Sensitive Information in Test Code|weakness +CWE-532|Insertion of Sensitive Information into Log File|weakness +CWE-533|DEPRECATED: Information Exposure Through Server Log Files|weakness +CWE-534|DEPRECATED: Information Exposure Through Debug Log Files|weakness +CWE-535|Exposure of Information Through Shell Error Message|weakness +CWE-536|Servlet Runtime Error Message Containing Sensitive Information|weakness +CWE-537|Java Runtime Error Message Containing Sensitive Information|weakness +CWE-538|Insertion of Sensitive Information into Externally-Accessible File or Directory|weakness +CWE-539|Use of Persistent Cookies Containing Sensitive Information|weakness +CWE-540|Inclusion of Sensitive Information in Source Code|weakness +CWE-541|Inclusion of Sensitive Information in an Include File|weakness +CWE-542|DEPRECATED: Information Exposure Through Cleanup Log Files|weakness +CWE-543|Use of Singleton Pattern Without Synchronization in a Multithreaded Context|weakness +CWE-544|Missing Standardized Error Handling Mechanism|weakness +CWE-545|DEPRECATED: Use of Dynamic Class Loading|weakness +CWE-546|Suspicious Comment|weakness +CWE-547|Use of Hard-coded, Security-relevant Constants|weakness +CWE-548|Exposure of Information Through Directory Listing|weakness +CWE-549|Missing Password Field Masking|weakness +CWE-550|Server-generated Error Message Containing Sensitive Information|weakness +CWE-551|Incorrect Behavior Order: Authorization Before Parsing and Canonicalization|weakness +CWE-552|Files or Directories Accessible to External Parties|weakness +CWE-553|Command Shell in Externally Accessible Directory|weakness +CWE-554|ASP.NET Misconfiguration: Not Using Input Validation Framework|weakness +CWE-555|J2EE Misconfiguration: Plaintext Password in Configuration File|weakness +CWE-556|ASP.NET Misconfiguration: Use of Identity Impersonation|weakness +CWE-557|Concurrency Issues|category +CWE-558|Use of getlogin() in Multithreaded Application|weakness +CWE-559|DEPRECATED: Often Misused: Arguments and Parameters|category +CWE-560|Use of umask() with chmod-style Argument|weakness +CWE-561|Dead Code|weakness +CWE-562|Return of Stack Variable Address|weakness +CWE-563|Assignment to Variable without Use|weakness +CWE-564|SQL Injection: Hibernate|weakness +CWE-565|Reliance on Cookies without Validation and Integrity Checking|weakness +CWE-566|Authorization Bypass Through User-Controlled SQL Primary Key|weakness +CWE-567|Unsynchronized Access to Shared Data in a Multithreaded Context|weakness +CWE-568|finalize() Method Without super.finalize()|weakness +CWE-569|Expression Issues|category +CWE-570|Expression is Always False|weakness +CWE-571|Expression is Always True|weakness +CWE-572|Call to Thread run() instead of start()|weakness +CWE-573|Improper Following of Specification by Caller|weakness +CWE-574|EJB Bad Practices: Use of Synchronization Primitives|weakness +CWE-575|EJB Bad Practices: Use of AWT Swing|weakness +CWE-576|EJB Bad Practices: Use of Java I/O|weakness +CWE-577|EJB Bad Practices: Use of Sockets|weakness +CWE-578|EJB Bad Practices: Use of Class Loader|weakness +CWE-579|J2EE Bad Practices: Non-serializable Object Stored in Session|weakness +CWE-580|clone() Method Without super.clone()|weakness +CWE-581|Object Model Violation: Just One of Equals and Hashcode Defined|weakness +CWE-582|Array Declared Public, Final, and Static|weakness +CWE-583|finalize() Method Declared Public|weakness +CWE-584|Return Inside Finally Block|weakness +CWE-585|Empty Synchronized Block|weakness +CWE-586|Explicit Call to Finalize()|weakness +CWE-587|Assignment of a Fixed Address to a Pointer|weakness +CWE-588|Attempt to Access Child of a Non-structure Pointer|weakness +CWE-589|Call to Non-ubiquitous API|weakness +CWE-590|Free of Memory not on the Heap|weakness +CWE-591|Sensitive Data Storage in Improperly Locked Memory|weakness +CWE-592|DEPRECATED: Authentication Bypass Issues|weakness +CWE-593|Authentication Bypass: OpenSSL CTX Object Modified after SSL Objects are Created|weakness +CWE-594|J2EE Framework: Saving Unserializable Objects to Disk|weakness +CWE-595|Comparison of Object References Instead of Object Contents|weakness +CWE-596|DEPRECATED: Incorrect Semantic Object Comparison|weakness +CWE-597|Use of Wrong Operator in String Comparison|weakness +CWE-598|Use of GET Request Method With Sensitive Query Strings|weakness +CWE-599|Missing Validation of OpenSSL Certificate|weakness +CWE-600|Uncaught Exception in Servlet |weakness +CWE-601|URL Redirection to Untrusted Site ('Open Redirect')|weakness +CWE-602|Client-Side Enforcement of Server-Side Security|weakness +CWE-603|Use of Client-Side Authentication|weakness +CWE-604|Deprecated Entries|view +CWE-605|Multiple Binds to the Same Port|weakness +CWE-606|Unchecked Input for Loop Condition|weakness +CWE-607|Public Static Final Field References Mutable Object|weakness +CWE-608|Struts: Non-private Field in ActionForm Class|weakness +CWE-609|Double-Checked Locking|weakness +CWE-610|Externally Controlled Reference to a Resource in Another Sphere|weakness +CWE-611|Improper Restriction of XML External Entity Reference|weakness +CWE-612|Improper Authorization of Index Containing Sensitive Information|weakness +CWE-613|Insufficient Session Expiration|weakness +CWE-614|Sensitive Cookie in HTTPS Session Without 'Secure' Attribute|weakness +CWE-615|Inclusion of Sensitive Information in Source Code Comments|weakness +CWE-616|Incomplete Identification of Uploaded File Variables (PHP)|weakness +CWE-617|Reachable Assertion|weakness +CWE-618|Exposed Unsafe ActiveX Method|weakness +CWE-619|Dangling Database Cursor ('Cursor Injection')|weakness +CWE-620|Unverified Password Change|weakness +CWE-621|Variable Extraction Error|weakness +CWE-622|Improper Validation of Function Hook Arguments|weakness +CWE-623|Unsafe ActiveX Control Marked Safe For Scripting|weakness +CWE-624|Executable Regular Expression Error|weakness +CWE-625|Permissive Regular Expression|weakness +CWE-626|Null Byte Interaction Error (Poison Null Byte)|weakness +CWE-627|Dynamic Variable Evaluation|weakness +CWE-628|Function Call with Incorrectly Specified Arguments|weakness +CWE-629|Weaknesses in OWASP Top Ten (2007)|view +CWE-630|DEPRECATED: Weaknesses Examined by SAMATE|view +CWE-631|DEPRECATED: Resource-specific Weaknesses|view +CWE-632|DEPRECATED: Weaknesses that Affect Files or Directories|category +CWE-633|DEPRECATED: Weaknesses that Affect Memory|category +CWE-634|DEPRECATED: Weaknesses that Affect System Processes|category +CWE-635|Weaknesses Originally Used by NVD from 2008 to 2016|view +CWE-636|Not Failing Securely ('Failing Open')|weakness +CWE-637|Unnecessary Complexity in Protection Mechanism (Not Using 'Economy of Mechanism')|weakness +CWE-638|Not Using Complete Mediation|weakness +CWE-639|Authorization Bypass Through User-Controlled Key|weakness +CWE-640|Weak Password Recovery Mechanism for Forgotten Password|weakness +CWE-641|Improper Restriction of Names for Files and Other Resources|weakness +CWE-642|External Control of Critical State Data|weakness +CWE-643|Improper Neutralization of Data within XPath Expressions ('XPath Injection')|weakness +CWE-644|Improper Neutralization of HTTP Headers for Scripting Syntax|weakness +CWE-645|Overly Restrictive Account Lockout Mechanism|weakness +CWE-646|Reliance on File Name or Extension of Externally-Supplied File|weakness +CWE-647|Use of Non-Canonical URL Paths for Authorization Decisions|weakness +CWE-648|Incorrect Use of Privileged APIs|weakness +CWE-649|Reliance on Obfuscation or Encryption of Security-Relevant Inputs without Integrity Checking|weakness +CWE-650|Trusting HTTP Permission Methods on the Server Side|weakness +CWE-651|Exposure of WSDL File Containing Sensitive Information|weakness +CWE-652|Improper Neutralization of Data within XQuery Expressions ('XQuery Injection')|weakness +CWE-653|Improper Isolation or Compartmentalization|weakness +CWE-654|Reliance on a Single Factor in a Security Decision|weakness +CWE-655|Insufficient Psychological Acceptability|weakness +CWE-656|Reliance on Security Through Obscurity|weakness +CWE-657|Violation of Secure Design Principles|weakness +CWE-658|Weaknesses in Software Written in C|view +CWE-659|Weaknesses in Software Written in C++|view +CWE-660|Weaknesses in Software Written in Java|view +CWE-661|Weaknesses in Software Written in PHP|view +CWE-662|Improper Synchronization|weakness +CWE-663|Use of a Non-reentrant Function in a Concurrent Context|weakness +CWE-664|Improper Control of a Resource Through its Lifetime|weakness +CWE-665|Improper Initialization|weakness +CWE-666|Operation on Resource in Wrong Phase of Lifetime|weakness +CWE-667|Improper Locking|weakness +CWE-668|Exposure of Resource to Wrong Sphere|weakness +CWE-669|Incorrect Resource Transfer Between Spheres|weakness +CWE-670|Always-Incorrect Control Flow Implementation|weakness +CWE-671|Lack of Administrator Control over Security|weakness +CWE-672|Operation on a Resource after Expiration or Release|weakness +CWE-673|External Influence of Sphere Definition|weakness +CWE-674|Uncontrolled Recursion|weakness +CWE-675|Multiple Operations on Resource in Single-Operation Context|weakness +CWE-676|Use of Potentially Dangerous Function|weakness +CWE-677|Weakness Base Elements|view +CWE-678|Composites|view +CWE-679|DEPRECATED: Chain Elements|view +CWE-680|Integer Overflow to Buffer Overflow|weakness +CWE-681|Incorrect Conversion between Numeric Types|weakness +CWE-682|Incorrect Calculation|weakness +CWE-683|Function Call With Incorrect Order of Arguments|weakness +CWE-684|Incorrect Provision of Specified Functionality|weakness +CWE-685|Function Call With Incorrect Number of Arguments|weakness +CWE-686|Function Call With Incorrect Argument Type|weakness +CWE-687|Function Call With Incorrectly Specified Argument Value|weakness +CWE-688|Function Call With Incorrect Variable or Reference as Argument|weakness +CWE-689|Permission Race Condition During Resource Copy|weakness +CWE-690|Unchecked Return Value to NULL Pointer Dereference|weakness +CWE-691|Insufficient Control Flow Management|weakness +CWE-692|Incomplete Denylist to Cross-Site Scripting|weakness +CWE-693|Protection Mechanism Failure|weakness +CWE-694|Use of Multiple Resources with Duplicate Identifier|weakness +CWE-695|Use of Low-Level Functionality|weakness +CWE-696|Incorrect Behavior Order|weakness +CWE-697|Incorrect Comparison|weakness +CWE-698|Execution After Redirect (EAR)|weakness +CWE-699|Software Development|view +CWE-700|Seven Pernicious Kingdoms|view +CWE-701|Weaknesses Introduced During Design|view +CWE-702|Weaknesses Introduced During Implementation|view +CWE-703|Improper Check or Handling of Exceptional Conditions|weakness +CWE-704|Incorrect Type Conversion or Cast|weakness +CWE-705|Incorrect Control Flow Scoping|weakness +CWE-706|Use of Incorrectly-Resolved Name or Reference|weakness +CWE-707|Improper Neutralization|weakness +CWE-708|Incorrect Ownership Assignment|weakness +CWE-709|Named Chains|view +CWE-710|Improper Adherence to Coding Standards|weakness +CWE-711|Weaknesses in OWASP Top Ten (2004)|view +CWE-712|OWASP Top Ten 2007 Category A1 - Cross Site Scripting (XSS)|category +CWE-713|OWASP Top Ten 2007 Category A2 - Injection Flaws|category +CWE-714|OWASP Top Ten 2007 Category A3 - Malicious File Execution|category +CWE-715|OWASP Top Ten 2007 Category A4 - Insecure Direct Object Reference|category +CWE-716|OWASP Top Ten 2007 Category A5 - Cross Site Request Forgery (CSRF)|category +CWE-717|OWASP Top Ten 2007 Category A6 - Information Leakage and Improper Error Handling|category +CWE-718|OWASP Top Ten 2007 Category A7 - Broken Authentication and Session Management|category +CWE-719|OWASP Top Ten 2007 Category A8 - Insecure Cryptographic Storage|category +CWE-720|OWASP Top Ten 2007 Category A9 - Insecure Communications|category +CWE-721|OWASP Top Ten 2007 Category A10 - Failure to Restrict URL Access|category +CWE-722|OWASP Top Ten 2004 Category A1 - Unvalidated Input|category +CWE-723|OWASP Top Ten 2004 Category A2 - Broken Access Control|category +CWE-724|OWASP Top Ten 2004 Category A3 - Broken Authentication and Session Management|category +CWE-725|OWASP Top Ten 2004 Category A4 - Cross-Site Scripting (XSS) Flaws|category +CWE-726|OWASP Top Ten 2004 Category A5 - Buffer Overflows|category +CWE-727|OWASP Top Ten 2004 Category A6 - Injection Flaws|category +CWE-728|OWASP Top Ten 2004 Category A7 - Improper Error Handling|category +CWE-729|OWASP Top Ten 2004 Category A8 - Insecure Storage|category +CWE-730|OWASP Top Ten 2004 Category A9 - Denial of Service|category +CWE-731|OWASP Top Ten 2004 Category A10 - Insecure Configuration Management|category +CWE-732|Incorrect Permission Assignment for Critical Resource|weakness +CWE-733|Compiler Optimization Removal or Modification of Security-critical Code|weakness +CWE-734|Weaknesses Addressed by the CERT C Secure Coding Standard (2008)|view +CWE-735|CERT C Secure Coding Standard (2008) Chapter 2 - Preprocessor (PRE)|category +CWE-736|CERT C Secure Coding Standard (2008) Chapter 3 - Declarations and Initialization (DCL)|category +CWE-737|CERT C Secure Coding Standard (2008) Chapter 4 - Expressions (EXP)|category +CWE-738|CERT C Secure Coding Standard (2008) Chapter 5 - Integers (INT)|category +CWE-739|CERT C Secure Coding Standard (2008) Chapter 6 - Floating Point (FLP)|category +CWE-740|CERT C Secure Coding Standard (2008) Chapter 7 - Arrays (ARR)|category +CWE-741|CERT C Secure Coding Standard (2008) Chapter 8 - Characters and Strings (STR)|category +CWE-742|CERT C Secure Coding Standard (2008) Chapter 9 - Memory Management (MEM)|category +CWE-743|CERT C Secure Coding Standard (2008) Chapter 10 - Input Output (FIO)|category +CWE-744|CERT C Secure Coding Standard (2008) Chapter 11 - Environment (ENV)|category +CWE-745|CERT C Secure Coding Standard (2008) Chapter 12 - Signals (SIG)|category +CWE-746|CERT C Secure Coding Standard (2008) Chapter 13 - Error Handling (ERR)|category +CWE-747|CERT C Secure Coding Standard (2008) Chapter 14 - Miscellaneous (MSC)|category +CWE-748|CERT C Secure Coding Standard (2008) Appendix - POSIX (POS)|category +CWE-749|Exposed Dangerous Method or Function|weakness +CWE-750|Weaknesses in the 2009 CWE/SANS Top 25 Most Dangerous Programming Errors|view +CWE-751|2009 Top 25 - Insecure Interaction Between Components|category +CWE-752|2009 Top 25 - Risky Resource Management|category +CWE-753|2009 Top 25 - Porous Defenses|category +CWE-754|Improper Check for Unusual or Exceptional Conditions|weakness +CWE-755|Improper Handling of Exceptional Conditions|weakness +CWE-756|Missing Custom Error Page|weakness +CWE-757|Selection of Less-Secure Algorithm During Negotiation ('Algorithm Downgrade')|weakness +CWE-758|Reliance on Undefined, Unspecified, or Implementation-Defined Behavior|weakness +CWE-759|Use of a One-Way Hash without a Salt|weakness +CWE-760|Use of a One-Way Hash with a Predictable Salt|weakness +CWE-761|Free of Pointer not at Start of Buffer|weakness +CWE-762|Mismatched Memory Management Routines|weakness +CWE-763|Release of Invalid Pointer or Reference|weakness +CWE-764|Multiple Locks of a Critical Resource|weakness +CWE-765|Multiple Unlocks of a Critical Resource|weakness +CWE-766|Critical Data Element Declared Public|weakness +CWE-767|Access to Critical Private Variable via Public Method|weakness +CWE-768|Incorrect Short Circuit Evaluation|weakness +CWE-769|DEPRECATED: Uncontrolled File Descriptor Consumption|weakness +CWE-770|Allocation of Resources Without Limits or Throttling|weakness +CWE-771|Missing Reference to Active Allocated Resource|weakness +CWE-772|Missing Release of Resource after Effective Lifetime|weakness +CWE-773|Missing Reference to Active File Descriptor or Handle|weakness +CWE-774|Allocation of File Descriptors or Handles Without Limits or Throttling|weakness +CWE-775|Missing Release of File Descriptor or Handle after Effective Lifetime|weakness +CWE-776|Improper Restriction of Recursive Entity References in DTDs ('XML Entity Expansion')|weakness +CWE-777|Regular Expression without Anchors|weakness +CWE-778|Insufficient Logging|weakness +CWE-779|Logging of Excessive Data|weakness +CWE-780|Use of RSA Algorithm without OAEP|weakness +CWE-781|Improper Address Validation in IOCTL with METHOD_NEITHER I/O Control Code|weakness +CWE-782|Exposed IOCTL with Insufficient Access Control|weakness +CWE-783|Operator Precedence Logic Error|weakness +CWE-784|Reliance on Cookies without Validation and Integrity Checking in a Security Decision|weakness +CWE-785|Use of Path Manipulation Function without Maximum-sized Buffer|weakness +CWE-786|Access of Memory Location Before Start of Buffer|weakness +CWE-787|Out-of-bounds Write|weakness +CWE-788|Access of Memory Location After End of Buffer|weakness +CWE-789|Memory Allocation with Excessive Size Value|weakness +CWE-790|Improper Filtering of Special Elements|weakness +CWE-791|Incomplete Filtering of Special Elements|weakness +CWE-792|Incomplete Filtering of One or More Instances of Special Elements|weakness +CWE-793|Only Filtering One Instance of a Special Element|weakness +CWE-794|Incomplete Filtering of Multiple Instances of Special Elements|weakness +CWE-795|Only Filtering Special Elements at a Specified Location|weakness +CWE-796|Only Filtering Special Elements Relative to a Marker|weakness +CWE-797|Only Filtering Special Elements at an Absolute Position|weakness +CWE-798|Use of Hard-coded Credentials|weakness +CWE-799|Improper Control of Interaction Frequency|weakness +CWE-800|Weaknesses in the 2010 CWE/SANS Top 25 Most Dangerous Programming Errors|view +CWE-801|2010 Top 25 - Insecure Interaction Between Components|category +CWE-802|2010 Top 25 - Risky Resource Management|category +CWE-803|2010 Top 25 - Porous Defenses|category +CWE-804|Guessable CAPTCHA|weakness +CWE-805|Buffer Access with Incorrect Length Value|weakness +CWE-806|Buffer Access Using Size of Source Buffer|weakness +CWE-807|Reliance on Untrusted Inputs in a Security Decision|weakness +CWE-808|2010 Top 25 - Weaknesses On the Cusp|category +CWE-809|Weaknesses in OWASP Top Ten (2010)|view +CWE-810|OWASP Top Ten 2010 Category A1 - Injection|category +CWE-811|OWASP Top Ten 2010 Category A2 - Cross-Site Scripting (XSS)|category +CWE-812|OWASP Top Ten 2010 Category A3 - Broken Authentication and Session Management|category +CWE-813|OWASP Top Ten 2010 Category A4 - Insecure Direct Object References|category +CWE-814|OWASP Top Ten 2010 Category A5 - Cross-Site Request Forgery(CSRF)|category +CWE-815|OWASP Top Ten 2010 Category A6 - Security Misconfiguration|category +CWE-816|OWASP Top Ten 2010 Category A7 - Insecure Cryptographic Storage|category +CWE-817|OWASP Top Ten 2010 Category A8 - Failure to Restrict URL Access|category +CWE-818|OWASP Top Ten 2010 Category A9 - Insufficient Transport Layer Protection|category +CWE-819|OWASP Top Ten 2010 Category A10 - Unvalidated Redirects and Forwards|category +CWE-820|Missing Synchronization|weakness +CWE-821|Incorrect Synchronization|weakness +CWE-822|Untrusted Pointer Dereference|weakness +CWE-823|Use of Out-of-range Pointer Offset|weakness +CWE-824|Access of Uninitialized Pointer|weakness +CWE-825|Expired Pointer Dereference|weakness +CWE-826|Premature Release of Resource During Expected Lifetime|weakness +CWE-827|Improper Control of Document Type Definition|weakness +CWE-828|Signal Handler with Functionality that is not Asynchronous-Safe|weakness +CWE-829|Inclusion of Functionality from Untrusted Control Sphere|weakness +CWE-830|Inclusion of Web Functionality from an Untrusted Source|weakness +CWE-831|Signal Handler Function Associated with Multiple Signals|weakness +CWE-832|Unlock of a Resource that is not Locked|weakness +CWE-833|Deadlock|weakness +CWE-834|Excessive Iteration|weakness +CWE-835|Loop with Unreachable Exit Condition ('Infinite Loop')|weakness +CWE-836|Use of Password Hash Instead of Password for Authentication|weakness +CWE-837|Improper Enforcement of a Single, Unique Action|weakness +CWE-838|Inappropriate Encoding for Output Context|weakness +CWE-839|Numeric Range Comparison Without Minimum Check|weakness +CWE-840|Business Logic Errors|category +CWE-841|Improper Enforcement of Behavioral Workflow|weakness +CWE-842|Placement of User into Incorrect Group|weakness +CWE-843|Access of Resource Using Incompatible Type ('Type Confusion')|weakness +CWE-844|Weaknesses Addressed by The CERT Oracle Secure Coding Standard for Java (2011)|view +CWE-845|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 2 - Input Validation and Data Sanitization (IDS)|category +CWE-846|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 3 - Declarations and Initialization (DCL)|category +CWE-847|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 4 - Expressions (EXP)|category +CWE-848|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 5 - Numeric Types and Operations (NUM)|category +CWE-849|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 6 - Object Orientation (OBJ)|category +CWE-850|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 7 - Methods (MET)|category +CWE-851|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 8 - Exceptional Behavior (ERR)|category +CWE-852|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 9 - Visibility and Atomicity (VNA)|category +CWE-853|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 10 - Locking (LCK)|category +CWE-854|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 11 - Thread APIs (THI)|category +CWE-855|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 12 - Thread Pools (TPS)|category +CWE-856|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 13 - Thread-Safety Miscellaneous (TSM)|category +CWE-857|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 14 - Input Output (FIO)|category +CWE-858|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 15 - Serialization (SER)|category +CWE-859|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 16 - Platform Security (SEC)|category +CWE-860|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 17 - Runtime Environment (ENV)|category +CWE-861|The CERT Oracle Secure Coding Standard for Java (2011) Chapter 18 - Miscellaneous (MSC)|category +CWE-862|Missing Authorization|weakness +CWE-863|Incorrect Authorization|weakness +CWE-864|2011 Top 25 - Insecure Interaction Between Components|category +CWE-865|2011 Top 25 - Risky Resource Management|category +CWE-866|2011 Top 25 - Porous Defenses|category +CWE-867|2011 Top 25 - Weaknesses On the Cusp|category +CWE-868|Weaknesses Addressed by the SEI CERT C++ Coding Standard (2016 Version)|view +CWE-869|CERT C++ Secure Coding Section 01 - Preprocessor (PRE)|category +CWE-870|CERT C++ Secure Coding Section 02 - Declarations and Initialization (DCL)|category +CWE-871|CERT C++ Secure Coding Section 03 - Expressions (EXP)|category +CWE-872|CERT C++ Secure Coding Section 04 - Integers (INT)|category +CWE-873|CERT C++ Secure Coding Section 05 - Floating Point Arithmetic (FLP)|category +CWE-874|CERT C++ Secure Coding Section 06 - Arrays and the STL (ARR)|category +CWE-875|CERT C++ Secure Coding Section 07 - Characters and Strings (STR)|category +CWE-876|CERT C++ Secure Coding Section 08 - Memory Management (MEM)|category +CWE-877|CERT C++ Secure Coding Section 09 - Input Output (FIO)|category +CWE-878|CERT C++ Secure Coding Section 10 - Environment (ENV)|category +CWE-879|CERT C++ Secure Coding Section 11 - Signals (SIG)|category +CWE-880|CERT C++ Secure Coding Section 12 - Exceptions and Error Handling (ERR)|category +CWE-881|CERT C++ Secure Coding Section 13 - Object Oriented Programming (OOP)|category +CWE-882|CERT C++ Secure Coding Section 14 - Concurrency (CON)|category +CWE-883|CERT C++ Secure Coding Section 49 - Miscellaneous (MSC)|category +CWE-884|CWE Cross-section|view +CWE-885|SFP Primary Cluster: Risky Values|category +CWE-886|SFP Primary Cluster: Unused entities|category +CWE-887|SFP Primary Cluster: API|category +CWE-888|Software Fault Pattern (SFP) Clusters|view +CWE-889|SFP Primary Cluster: Exception Management|category +CWE-890|SFP Primary Cluster: Memory Access|category +CWE-891|SFP Primary Cluster: Memory Management|category +CWE-892|SFP Primary Cluster: Resource Management|category +CWE-893|SFP Primary Cluster: Path Resolution|category +CWE-894|SFP Primary Cluster: Synchronization|category +CWE-895|SFP Primary Cluster: Information Leak|category +CWE-896|SFP Primary Cluster: Tainted Input|category +CWE-897|SFP Primary Cluster: Entry Points|category +CWE-898|SFP Primary Cluster: Authentication|category +CWE-899|SFP Primary Cluster: Access Control|category +CWE-900|Weaknesses in the 2011 CWE/SANS Top 25 Most Dangerous Software Errors|view +CWE-901|SFP Primary Cluster: Privilege|category +CWE-902|SFP Primary Cluster: Channel|category +CWE-903|SFP Primary Cluster: Cryptography|category +CWE-904|SFP Primary Cluster: Malware|category +CWE-905|SFP Primary Cluster: Predictability|category +CWE-906|SFP Primary Cluster: UI|category +CWE-907|SFP Primary Cluster: Other|category +CWE-908|Use of Uninitialized Resource|weakness +CWE-909|Missing Initialization of Resource|weakness +CWE-910|Use of Expired File Descriptor|weakness +CWE-911|Improper Update of Reference Count|weakness +CWE-912|Hidden Functionality|weakness +CWE-913|Improper Control of Dynamically-Managed Code Resources|weakness +CWE-914|Improper Control of Dynamically-Identified Variables|weakness +CWE-915|Improperly Controlled Modification of Dynamically-Determined Object Attributes|weakness +CWE-916|Use of Password Hash With Insufficient Computational Effort|weakness +CWE-917|Improper Neutralization of Special Elements used in an Expression Language Statement ('Expression Language Injection')|weakness +CWE-918|Server-Side Request Forgery (SSRF)|weakness +CWE-919|Weaknesses in Mobile Applications|view +CWE-920|Improper Restriction of Power Consumption|weakness +CWE-921|Storage of Sensitive Data in a Mechanism without Access Control|weakness +CWE-922|Insecure Storage of Sensitive Information|weakness +CWE-923|Improper Restriction of Communication Channel to Intended Endpoints|weakness +CWE-924|Improper Enforcement of Message Integrity During Transmission in a Communication Channel|weakness +CWE-925|Improper Verification of Intent by Broadcast Receiver|weakness +CWE-926|Improper Export of Android Application Components|weakness +CWE-927|Use of Implicit Intent for Sensitive Communication|weakness +CWE-928|Weaknesses in OWASP Top Ten (2013)|view +CWE-929|OWASP Top Ten 2013 Category A1 - Injection|category +CWE-930|OWASP Top Ten 2013 Category A2 - Broken Authentication and Session Management|category +CWE-931|OWASP Top Ten 2013 Category A3 - Cross-Site Scripting (XSS)|category +CWE-932|OWASP Top Ten 2013 Category A4 - Insecure Direct Object References|category +CWE-933|OWASP Top Ten 2013 Category A5 - Security Misconfiguration|category +CWE-934|OWASP Top Ten 2013 Category A6 - Sensitive Data Exposure|category +CWE-935|OWASP Top Ten 2013 Category A7 - Missing Function Level Access Control|category +CWE-936|OWASP Top Ten 2013 Category A8 - Cross-Site Request Forgery (CSRF)|category +CWE-937|OWASP Top Ten 2013 Category A9 - Using Components with Known Vulnerabilities|category +CWE-938|OWASP Top Ten 2013 Category A10 - Unvalidated Redirects and Forwards|category +CWE-939|Improper Authorization in Handler for Custom URL Scheme|weakness +CWE-940|Improper Verification of Source of a Communication Channel|weakness +CWE-941|Incorrectly Specified Destination in a Communication Channel|weakness +CWE-942|Permissive Cross-domain Policy with Untrusted Domains|weakness +CWE-943|Improper Neutralization of Special Elements in Data Query Logic|weakness +CWE-944|SFP Secondary Cluster: Access Management|category +CWE-945|SFP Secondary Cluster: Insecure Resource Access|category +CWE-946|SFP Secondary Cluster: Insecure Resource Permissions|category +CWE-947|SFP Secondary Cluster: Authentication Bypass|category +CWE-948|SFP Secondary Cluster: Digital Certificate|category +CWE-949|SFP Secondary Cluster: Faulty Endpoint Authentication|category +CWE-950|SFP Secondary Cluster: Hardcoded Sensitive Data|category +CWE-951|SFP Secondary Cluster: Insecure Authentication Policy|category +CWE-952|SFP Secondary Cluster: Missing Authentication|category +CWE-953|SFP Secondary Cluster: Missing Endpoint Authentication|category +CWE-954|SFP Secondary Cluster: Multiple Binds to the Same Port|category +CWE-955|SFP Secondary Cluster: Unrestricted Authentication|category +CWE-956|SFP Secondary Cluster: Channel Attack|category +CWE-957|SFP Secondary Cluster: Protocol Error|category +CWE-958|SFP Secondary Cluster: Broken Cryptography|category +CWE-959|SFP Secondary Cluster: Weak Cryptography|category +CWE-960|SFP Secondary Cluster: Ambiguous Exception Type|category +CWE-961|SFP Secondary Cluster: Incorrect Exception Behavior|category +CWE-962|SFP Secondary Cluster: Unchecked Status Condition|category +CWE-963|SFP Secondary Cluster: Exposed Data|category +CWE-964|SFP Secondary Cluster: Exposure Temporary File|category +CWE-965|SFP Secondary Cluster: Insecure Session Management|category +CWE-966|SFP Secondary Cluster: Other Exposures|category +CWE-967|SFP Secondary Cluster: State Disclosure|category +CWE-968|SFP Secondary Cluster: Covert Channel|category +CWE-969|SFP Secondary Cluster: Faulty Memory Release|category +CWE-970|SFP Secondary Cluster: Faulty Buffer Access|category +CWE-971|SFP Secondary Cluster: Faulty Pointer Use|category +CWE-972|SFP Secondary Cluster: Faulty String Expansion|category +CWE-973|SFP Secondary Cluster: Improper NULL Termination|category +CWE-974|SFP Secondary Cluster: Incorrect Buffer Length Computation|category +CWE-975|SFP Secondary Cluster: Architecture|category +CWE-976|SFP Secondary Cluster: Compiler|category +CWE-977|SFP Secondary Cluster: Design|category +CWE-978|SFP Secondary Cluster: Implementation|category +CWE-979|SFP Secondary Cluster: Failed Chroot Jail|category +CWE-980|SFP Secondary Cluster: Link in Resource Name Resolution|category +CWE-981|SFP Secondary Cluster: Path Traversal|category +CWE-982|SFP Secondary Cluster: Failure to Release Resource|category +CWE-983|SFP Secondary Cluster: Faulty Resource Use|category +CWE-984|SFP Secondary Cluster: Life Cycle|category +CWE-985|SFP Secondary Cluster: Unrestricted Consumption|category +CWE-986|SFP Secondary Cluster: Missing Lock|category +CWE-987|SFP Secondary Cluster: Multiple Locks/Unlocks|category +CWE-988|SFP Secondary Cluster: Race Condition Window|category +CWE-989|SFP Secondary Cluster: Unrestricted Lock|category +CWE-990|SFP Secondary Cluster: Tainted Input to Command|category +CWE-991|SFP Secondary Cluster: Tainted Input to Environment|category +CWE-992|SFP Secondary Cluster: Faulty Input Transformation|category +CWE-993|SFP Secondary Cluster: Incorrect Input Handling|category +CWE-994|SFP Secondary Cluster: Tainted Input to Variable|category +CWE-995|SFP Secondary Cluster: Feature|category +CWE-996|SFP Secondary Cluster: Security|category +CWE-997|SFP Secondary Cluster: Information Loss|category +CWE-998|SFP Secondary Cluster: Glitch in Computation|category +CWE-999|DEPRECATED: Weaknesses without Software Fault Patterns|view +CWE-1000|Research Concepts|view +CWE-1001|SFP Secondary Cluster: Use of an Improper API|category +CWE-1002|SFP Secondary Cluster: Unexpected Entry Points|category +CWE-1003|Weaknesses for Simplified Mapping of Published Vulnerabilities|view +CWE-1004|Sensitive Cookie Without 'HttpOnly' Flag|weakness +CWE-1005|7PK - Input Validation and Representation|category +CWE-1006|Bad Coding Practices|category +CWE-1007|Insufficient Visual Distinction of Homoglyphs Presented to User|weakness +CWE-1008|Architectural Concepts|view +CWE-1009|Audit|category +CWE-1010|Authenticate Actors|category +CWE-1011|Authorize Actors|category +CWE-1012|Cross Cutting|category +CWE-1013|Encrypt Data|category +CWE-1014|Identify Actors|category +CWE-1015|Limit Access|category +CWE-1016|Limit Exposure|category +CWE-1017|Lock Computer|category +CWE-1018|Manage User Sessions|category +CWE-1019|Validate Inputs|category +CWE-1020|Verify Message Integrity|category +CWE-1021|Improper Restriction of Rendered UI Layers or Frames|weakness +CWE-1022|Use of Web Link to Untrusted Target with window.opener Access|weakness +CWE-1023|Incomplete Comparison with Missing Factors|weakness +CWE-1024|Comparison of Incompatible Types|weakness +CWE-1025|Comparison Using Wrong Factors|weakness +CWE-1026|Weaknesses in OWASP Top Ten (2017)|view +CWE-1027|OWASP Top Ten 2017 Category A1 - Injection|category +CWE-1028|OWASP Top Ten 2017 Category A2 - Broken Authentication|category +CWE-1029|OWASP Top Ten 2017 Category A3 - Sensitive Data Exposure|category +CWE-1030|OWASP Top Ten 2017 Category A4 - XML External Entities (XXE)|category +CWE-1031|OWASP Top Ten 2017 Category A5 - Broken Access Control|category +CWE-1032|OWASP Top Ten 2017 Category A6 - Security Misconfiguration|category +CWE-1033|OWASP Top Ten 2017 Category A7 - Cross-Site Scripting (XSS)|category +CWE-1034|OWASP Top Ten 2017 Category A8 - Insecure Deserialization|category +CWE-1035|OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities|category +CWE-1036|OWASP Top Ten 2017 Category A10 - Insufficient Logging & Monitoring|category +CWE-1037|Processor Optimization Removal or Modification of Security-critical Code|weakness +CWE-1038|Insecure Automated Optimizations|weakness +CWE-1039|Automated Recognition Mechanism with Inadequate Detection or Handling of Adversarial Input Perturbations|weakness +CWE-1040|Quality Weaknesses with Indirect Security Impacts|view +CWE-1041|Use of Redundant Code|weakness +CWE-1042|Static Member Data Element outside of a Singleton Class Element|weakness +CWE-1043|Data Element Aggregating an Excessively Large Number of Non-Primitive Elements|weakness +CWE-1044|Architecture with Number of Horizontal Layers Outside of Expected Range|weakness +CWE-1045|Parent Class with a Virtual Destructor and a Child Class without a Virtual Destructor|weakness +CWE-1046|Creation of Immutable Text Using String Concatenation|weakness +CWE-1047|Modules with Circular Dependencies|weakness +CWE-1048|Invokable Control Element with Large Number of Outward Calls|weakness +CWE-1049|Excessive Data Query Operations in a Large Data Table|weakness +CWE-1050|Excessive Platform Resource Consumption within a Loop|weakness +CWE-1051|Initialization with Hard-Coded Network Resource Configuration Data|weakness +CWE-1052|Excessive Use of Hard-Coded Literals in Initialization|weakness +CWE-1053|Missing Documentation for Design|weakness +CWE-1054|Invocation of a Control Element at an Unnecessarily Deep Horizontal Layer|weakness +CWE-1055|Multiple Inheritance from Concrete Classes|weakness +CWE-1056|Invokable Control Element with Variadic Parameters|weakness +CWE-1057|Data Access Operations Outside of Expected Data Manager Component|weakness +CWE-1058|Invokable Control Element in Multi-Thread Context with non-Final Static Storable or Member Element|weakness +CWE-1059|Insufficient Technical Documentation|weakness +CWE-1060|Excessive Number of Inefficient Server-Side Data Accesses|weakness +CWE-1061|Insufficient Encapsulation|weakness +CWE-1062|Parent Class with References to Child Class|weakness +CWE-1063|Creation of Class Instance within a Static Code Block|weakness +CWE-1064|Invokable Control Element with Signature Containing an Excessive Number of Parameters|weakness +CWE-1065|Runtime Resource Management Control Element in a Component Built to Run on Application Servers|weakness +CWE-1066|Missing Serialization Control Element|weakness +CWE-1067|Excessive Execution of Sequential Searches of Data Resource|weakness +CWE-1068|Inconsistency Between Implementation and Documented Design|weakness +CWE-1069|Empty Exception Block|weakness +CWE-1070|Serializable Data Element Containing non-Serializable Item Elements|weakness +CWE-1071|Empty Code Block|weakness +CWE-1072|Data Resource Access without Use of Connection Pooling|weakness +CWE-1073|Non-SQL Invokable Control Element with Excessive Number of Data Resource Accesses|weakness +CWE-1074|Class with Excessively Deep Inheritance|weakness +CWE-1075|Unconditional Control Flow Transfer outside of Switch Block|weakness +CWE-1076|Insufficient Adherence to Expected Conventions|weakness +CWE-1077|Floating Point Comparison with Incorrect Operator|weakness +CWE-1078|Inappropriate Source Code Style or Formatting|weakness +CWE-1079|Parent Class without Virtual Destructor Method|weakness +CWE-1080|Source Code File with Excessive Number of Lines of Code|weakness +CWE-1081|Entries with Maintenance Notes|view +CWE-1082|Class Instance Self Destruction Control Element|weakness +CWE-1083|Data Access from Outside Expected Data Manager Component|weakness +CWE-1084|Invokable Control Element with Excessive File or Data Access Operations|weakness +CWE-1085|Invokable Control Element with Excessive Volume of Commented-out Code|weakness +CWE-1086|Class with Excessive Number of Child Classes|weakness +CWE-1087|Class with Virtual Method without a Virtual Destructor|weakness +CWE-1088|Synchronous Access of Remote Resource without Timeout|weakness +CWE-1089|Large Data Table with Excessive Number of Indices|weakness +CWE-1090|Method Containing Access of a Member Element from Another Class|weakness +CWE-1091|Use of Object without Invoking Destructor Method|weakness +CWE-1092|Use of Same Invokable Control Element in Multiple Architectural Layers|weakness +CWE-1093|Excessively Complex Data Representation|weakness +CWE-1094|Excessive Index Range Scan for a Data Resource|weakness +CWE-1095|Loop Condition Value Update within the Loop|weakness +CWE-1096|Singleton Class Instance Creation without Proper Locking or Synchronization|weakness +CWE-1097|Persistent Storable Data Element without Associated Comparison Control Element|weakness +CWE-1098|Data Element containing Pointer Item without Proper Copy Control Element|weakness +CWE-1099|Inconsistent Naming Conventions for Identifiers|weakness +CWE-1100|Insufficient Isolation of System-Dependent Functions|weakness +CWE-1101|Reliance on Runtime Component in Generated Code|weakness +CWE-1102|Reliance on Machine-Dependent Data Representation|weakness +CWE-1103|Use of Platform-Dependent Third Party Components|weakness +CWE-1104|Use of Unmaintained Third Party Components|weakness +CWE-1105|Insufficient Encapsulation of Machine-Dependent Functionality|weakness +CWE-1106|Insufficient Use of Symbolic Constants|weakness +CWE-1107|Insufficient Isolation of Symbolic Constant Definitions|weakness +CWE-1108|Excessive Reliance on Global Variables|weakness +CWE-1109|Use of Same Variable for Multiple Purposes|weakness +CWE-1110|Incomplete Design Documentation|weakness +CWE-1111|Incomplete I/O Documentation|weakness +CWE-1112|Incomplete Documentation of Program Execution|weakness +CWE-1113|Inappropriate Comment Style|weakness +CWE-1114|Inappropriate Whitespace Style|weakness +CWE-1115|Source Code Element without Standard Prologue|weakness +CWE-1116|Inaccurate Comments|weakness +CWE-1117|Callable with Insufficient Behavioral Summary|weakness +CWE-1118|Insufficient Documentation of Error Handling Techniques|weakness +CWE-1119|Excessive Use of Unconditional Branching|weakness +CWE-1120|Excessive Code Complexity|weakness +CWE-1121|Excessive McCabe Cyclomatic Complexity|weakness +CWE-1122|Excessive Halstead Complexity|weakness +CWE-1123|Excessive Use of Self-Modifying Code|weakness +CWE-1124|Excessively Deep Nesting|weakness +CWE-1125|Excessive Attack Surface|weakness +CWE-1126|Declaration of Variable with Unnecessarily Wide Scope|weakness +CWE-1127|Compilation with Insufficient Warnings or Errors|weakness +CWE-1128|CISQ Quality Measures (2016)|view +CWE-1129|CISQ Quality Measures (2016) - Reliability|category +CWE-1130|CISQ Quality Measures (2016) - Maintainability|category +CWE-1131|CISQ Quality Measures (2016) - Security|category +CWE-1132|CISQ Quality Measures (2016) - Performance Efficiency|category +CWE-1133|Weaknesses Addressed by the SEI CERT Oracle Coding Standard for Java|view +CWE-1134|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 00. Input Validation and Data Sanitization (IDS)|category +CWE-1135|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 01. Declarations and Initialization (DCL)|category +CWE-1136|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 02. Expressions (EXP)|category +CWE-1137|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 03. Numeric Types and Operations (NUM)|category +CWE-1138|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 04. Characters and Strings (STR)|category +CWE-1139|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 05. Object Orientation (OBJ)|category +CWE-1140|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 06. Methods (MET)|category +CWE-1141|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 07. Exceptional Behavior (ERR)|category +CWE-1142|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 08. Visibility and Atomicity (VNA)|category +CWE-1143|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 09. Locking (LCK)|category +CWE-1144|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 10. Thread APIs (THI)|category +CWE-1145|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 11. Thread Pools (TPS)|category +CWE-1146|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 12. Thread-Safety Miscellaneous (TSM)|category +CWE-1147|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 13. Input Output (FIO)|category +CWE-1148|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 14. Serialization (SER)|category +CWE-1149|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 15. Platform Security (SEC)|category +CWE-1150|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 16. Runtime Environment (ENV)|category +CWE-1151|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 17. Java Native Interface (JNI)|category +CWE-1152|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 49. Miscellaneous (MSC)|category +CWE-1153|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 50. Android (DRD)|category +CWE-1154|Weaknesses Addressed by the SEI CERT C Coding Standard|view +CWE-1155|SEI CERT C Coding Standard - Guidelines 01. Preprocessor (PRE)|category +CWE-1156|SEI CERT C Coding Standard - Guidelines 02. Declarations and Initialization (DCL)|category +CWE-1157|SEI CERT C Coding Standard - Guidelines 03. Expressions (EXP)|category +CWE-1158|SEI CERT C Coding Standard - Guidelines 04. Integers (INT)|category +CWE-1159|SEI CERT C Coding Standard - Guidelines 05. Floating Point (FLP)|category +CWE-1160|SEI CERT C Coding Standard - Guidelines 06. Arrays (ARR)|category +CWE-1161|SEI CERT C Coding Standard - Guidelines 07. Characters and Strings (STR)|category +CWE-1162|SEI CERT C Coding Standard - Guidelines 08. Memory Management (MEM)|category +CWE-1163|SEI CERT C Coding Standard - Guidelines 09. Input Output (FIO)|category +CWE-1164|Irrelevant Code|weakness +CWE-1165|SEI CERT C Coding Standard - Guidelines 10. Environment (ENV)|category +CWE-1166|SEI CERT C Coding Standard - Guidelines 11. Signals (SIG)|category +CWE-1167|SEI CERT C Coding Standard - Guidelines 12. Error Handling (ERR)|category +CWE-1168|SEI CERT C Coding Standard - Guidelines 13. Application Programming Interfaces (API)|category +CWE-1169|SEI CERT C Coding Standard - Guidelines 14. Concurrency (CON)|category +CWE-1170|SEI CERT C Coding Standard - Guidelines 48. Miscellaneous (MSC)|category +CWE-1171|SEI CERT C Coding Standard - Guidelines 50. POSIX (POS)|category +CWE-1172|SEI CERT C Coding Standard - Guidelines 51. Microsoft Windows (WIN) |category +CWE-1173|Improper Use of Validation Framework|weakness +CWE-1174|ASP.NET Misconfiguration: Improper Model Validation|weakness +CWE-1175|SEI CERT Oracle Secure Coding Standard for Java - Guidelines 18. Concurrency (CON)|category +CWE-1176|Inefficient CPU Computation|weakness +CWE-1177|Use of Prohibited Code|weakness +CWE-1178|Weaknesses Addressed by the SEI CERT Perl Coding Standard|view +CWE-1179|SEI CERT Perl Coding Standard - Guidelines 01. Input Validation and Data Sanitization (IDS)|category +CWE-1180|SEI CERT Perl Coding Standard - Guidelines 02. Declarations and Initialization (DCL)|category +CWE-1181|SEI CERT Perl Coding Standard - Guidelines 03. Expressions (EXP)|category +CWE-1182|SEI CERT Perl Coding Standard - Guidelines 04. Integers (INT)|category +CWE-1183|SEI CERT Perl Coding Standard - Guidelines 05. Strings (STR)|category +CWE-1184|SEI CERT Perl Coding Standard - Guidelines 06. Object-Oriented Programming (OOP)|category +CWE-1185|SEI CERT Perl Coding Standard - Guidelines 07. File Input and Output (FIO)|category +CWE-1186|SEI CERT Perl Coding Standard - Guidelines 50. Miscellaneous (MSC)|category +CWE-1187|DEPRECATED: Use of Uninitialized Resource|weakness +CWE-1188|Insecure Default Initialization of Resource|weakness +CWE-1189|Improper Isolation of Shared Resources on System-on-a-Chip (SoC)|weakness +CWE-1190|DMA Device Enabled Too Early in Boot Phase|weakness +CWE-1191|On-Chip Debug and Test Interface With Improper Access Control|weakness +CWE-1192|System-on-Chip (SoC) Using Components without Unique, Immutable Identifiers|weakness +CWE-1193|Power-On of Untrusted Execution Core Before Enabling Fabric Access Control|weakness +CWE-1194|Hardware Design|view +CWE-1195|Manufacturing and Life Cycle Management Concerns|category +CWE-1196|Security Flow Issues|category +CWE-1197|Integration Issues|category +CWE-1198|Privilege Separation and Access Control Issues|category +CWE-1199|General Circuit and Logic Design Concerns|category +CWE-1200|Weaknesses in the 2019 CWE Top 25 Most Dangerous Software Errors|view +CWE-1201|Core and Compute Issues|category +CWE-1202|Memory and Storage Issues|category +CWE-1203|Peripherals, On-chip Fabric, and Interface/IO Problems|category +CWE-1204|Generation of Weak Initialization Vector (IV)|weakness +CWE-1205|Security Primitives and Cryptography Issues|category +CWE-1206|Power, Clock, Thermal, and Reset Concerns|category +CWE-1207|Debug and Test Problems|category +CWE-1208|Cross-Cutting Problems|category +CWE-1209|Failure to Disable Reserved Bits|weakness +CWE-1210|Audit / Logging Errors|category +CWE-1211|Authentication Errors|category +CWE-1212|Authorization Errors|category +CWE-1213|Random Number Issues|category +CWE-1214|Data Integrity Issues|category +CWE-1215|Data Validation Issues|category +CWE-1216|Lockout Mechanism Errors|category +CWE-1217|User Session Errors|category +CWE-1218|Memory Buffer Errors|category +CWE-1219|File Handling Issues|category +CWE-1220|Insufficient Granularity of Access Control|weakness +CWE-1221|Incorrect Register Defaults or Module Parameters|weakness +CWE-1222|Insufficient Granularity of Address Regions Protected by Register Locks|weakness +CWE-1223|Race Condition for Write-Once Attributes|weakness +CWE-1224|Improper Restriction of Write-Once Bit Fields|weakness +CWE-1225|Documentation Issues|category +CWE-1226|Complexity Issues|category +CWE-1227|Encapsulation Issues|category +CWE-1228|API / Function Errors|category +CWE-1229|Creation of Emergent Resource|weakness +CWE-1230|Exposure of Sensitive Information Through Metadata|weakness +CWE-1231|Improper Prevention of Lock Bit Modification|weakness +CWE-1232|Improper Lock Behavior After Power State Transition|weakness +CWE-1233|Security-Sensitive Hardware Controls with Missing Lock Bit Protection|weakness +CWE-1234|Hardware Internal or Debug Modes Allow Override of Locks|weakness +CWE-1235|Incorrect Use of Autoboxing and Unboxing for Performance Critical Operations|weakness +CWE-1236|Improper Neutralization of Formula Elements in a CSV File|weakness +CWE-1237|SFP Primary Cluster: Faulty Resource Release|category +CWE-1238|SFP Primary Cluster: Failure to Release Memory|category +CWE-1239|Improper Zeroization of Hardware Register|weakness +CWE-1240|Use of a Cryptographic Primitive with a Risky Implementation|weakness +CWE-1241|Use of Predictable Algorithm in Random Number Generator|weakness +CWE-1242|Inclusion of Undocumented Features or Chicken Bits|weakness +CWE-1243|Sensitive Non-Volatile Information Not Protected During Debug|weakness +CWE-1244|Internal Asset Exposed to Unsafe Debug Access Level or State|weakness +CWE-1245|Improper Finite State Machines (FSMs) in Hardware Logic|weakness +CWE-1246|Improper Write Handling in Limited-write Non-Volatile Memories|weakness +CWE-1247|Improper Protection Against Voltage and Clock Glitches|weakness +CWE-1248|Semiconductor Defects in Hardware Logic with Security-Sensitive Implications|weakness +CWE-1249|Application-Level Admin Tool with Inconsistent View of Underlying Operating System|weakness +CWE-1250|Improper Preservation of Consistency Between Independent Representations of Shared State|weakness +CWE-1251|Mirrored Regions with Different Values|weakness +CWE-1252|CPU Hardware Not Configured to Support Exclusivity of Write and Execute Operations|weakness +CWE-1253|Incorrect Selection of Fuse Values|weakness +CWE-1254|Incorrect Comparison Logic Granularity|weakness +CWE-1255|Comparison Logic is Vulnerable to Power Side-Channel Attacks|weakness +CWE-1256|Improper Restriction of Software Interfaces to Hardware Features|weakness +CWE-1257|Improper Access Control Applied to Mirrored or Aliased Memory Regions|weakness +CWE-1258|Exposure of Sensitive System Information Due to Uncleared Debug Information|weakness +CWE-1259|Improper Restriction of Security Token Assignment|weakness +CWE-1260|Improper Handling of Overlap Between Protected Memory Ranges|weakness +CWE-1261|Improper Handling of Single Event Upsets|weakness +CWE-1262|Improper Access Control for Register Interface|weakness +CWE-1263|Improper Physical Access Control|weakness +CWE-1264|Hardware Logic with Insecure De-Synchronization between Control and Data Channels|weakness +CWE-1265|Unintended Reentrant Invocation of Non-reentrant Code Via Nested Calls|weakness +CWE-1266|Improper Scrubbing of Sensitive Data from Decommissioned Device|weakness +CWE-1267|Policy Uses Obsolete Encoding|weakness +CWE-1268|Policy Privileges are not Assigned Consistently Between Control and Data Agents|weakness +CWE-1269|Product Released in Non-Release Configuration|weakness +CWE-1270|Generation of Incorrect Security Tokens|weakness +CWE-1271|Uninitialized Value on Reset for Registers Holding Security Settings|weakness +CWE-1272|Sensitive Information Uncleared Before Debug/Power State Transition|weakness +CWE-1273|Device Unlock Credential Sharing|weakness +CWE-1274|Improper Access Control for Volatile Memory Containing Boot Code|weakness +CWE-1275|Sensitive Cookie with Improper SameSite Attribute|weakness +CWE-1276|Hardware Child Block Incorrectly Connected to Parent System|weakness +CWE-1277|Firmware Not Updateable|weakness +CWE-1278|Missing Protection Against Hardware Reverse Engineering Using Integrated Circuit (IC) Imaging Techniques|weakness +CWE-1279|Cryptographic Operations are run Before Supporting Units are Ready|weakness +CWE-1280|Access Control Check Implemented After Asset is Accessed|weakness +CWE-1281|Sequence of Processor Instructions Leads to Unexpected Behavior|weakness +CWE-1282|Assumed-Immutable Data is Stored in Writable Memory|weakness +CWE-1283|Mutable Attestation or Measurement Reporting Data|weakness +CWE-1284|Improper Validation of Specified Quantity in Input|weakness +CWE-1285|Improper Validation of Specified Index, Position, or Offset in Input|weakness +CWE-1286|Improper Validation of Syntactic Correctness of Input|weakness +CWE-1287|Improper Validation of Specified Type of Input|weakness +CWE-1288|Improper Validation of Consistency within Input|weakness +CWE-1289|Improper Validation of Unsafe Equivalence in Input|weakness +CWE-1290|Incorrect Decoding of Security Identifiers |weakness +CWE-1291|Public Key Re-Use for Signing both Debug and Production Code|weakness +CWE-1292|Incorrect Conversion of Security Identifiers|weakness +CWE-1293|Missing Source Correlation of Multiple Independent Data|weakness +CWE-1294|Insecure Security Identifier Mechanism|weakness +CWE-1295|Debug Messages Revealing Unnecessary Information|weakness +CWE-1296|Incorrect Chaining or Granularity of Debug Components|weakness +CWE-1297|Unprotected Confidential Information on Device is Accessible by OSAT Vendors|weakness +CWE-1298|Hardware Logic Contains Race Conditions|weakness +CWE-1299|Missing Protection Mechanism for Alternate Hardware Interface|weakness +CWE-1300|Improper Protection of Physical Side Channels|weakness +CWE-1301|Insufficient or Incomplete Data Removal within Hardware Component|weakness +CWE-1302|Missing Security Identifier|weakness +CWE-1303|Non-Transparent Sharing of Microarchitectural Resources|weakness +CWE-1304|Improperly Preserved Integrity of Hardware Configuration State During a Power Save/Restore Operation|weakness +CWE-1305|CISQ Quality Measures (2020)|view +CWE-1306|CISQ Quality Measures - Reliability|category +CWE-1307|CISQ Quality Measures - Maintainability|category +CWE-1308|CISQ Quality Measures - Security|category +CWE-1309|CISQ Quality Measures - Efficiency|category +CWE-1310|Missing Ability to Patch ROM Code|weakness +CWE-1311|Improper Translation of Security Attributes by Fabric Bridge|weakness +CWE-1312|Missing Protection for Mirrored Regions in On-Chip Fabric Firewall|weakness +CWE-1313|Hardware Allows Activation of Test or Debug Logic at Runtime|weakness +CWE-1314|Missing Write Protection for Parametric Data Values|weakness +CWE-1315|Improper Setting of Bus Controlling Capability in Fabric End-point|weakness +CWE-1316|Fabric-Address Map Allows Programming of Unwarranted Overlaps of Protected and Unprotected Ranges|weakness +CWE-1317|Improper Access Control in Fabric Bridge|weakness +CWE-1318|Missing Support for Security Features in On-chip Fabrics or Buses|weakness +CWE-1319|Improper Protection against Electromagnetic Fault Injection (EM-FI)|weakness +CWE-1320|Improper Protection for Outbound Error Messages and Alert Signals|weakness +CWE-1321|Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')|weakness +CWE-1322|Use of Blocking Code in Single-threaded, Non-blocking Context|weakness +CWE-1323|Improper Management of Sensitive Trace Data|weakness +CWE-1324|DEPRECATED: Sensitive Information Accessible by Physical Probing of JTAG Interface|weakness +CWE-1325|Improperly Controlled Sequential Memory Allocation|weakness +CWE-1326|Missing Immutable Root of Trust in Hardware|weakness +CWE-1327|Binding to an Unrestricted IP Address|weakness +CWE-1328|Security Version Number Mutable to Older Versions|weakness +CWE-1329|Reliance on Component That is Not Updateable|weakness +CWE-1330|Remanent Data Readable after Memory Erase|weakness +CWE-1331|Improper Isolation of Shared Resources in Network On Chip (NoC)|weakness +CWE-1332|Improper Handling of Faults that Lead to Instruction Skips|weakness +CWE-1333|Inefficient Regular Expression Complexity|weakness +CWE-1334|Unauthorized Error Injection Can Degrade Hardware Redundancy|weakness +CWE-1335|Incorrect Bitwise Shift of Integer|weakness +CWE-1336|Improper Neutralization of Special Elements Used in a Template Engine|weakness +CWE-1337|Weaknesses in the 2021 CWE Top 25 Most Dangerous Software Weaknesses|view +CWE-1338|Improper Protections Against Hardware Overheating|weakness +CWE-1339|Insufficient Precision or Accuracy of a Real Number|weakness +CWE-1340|CISQ Data Protection Measures|view +CWE-1341|Multiple Releases of Same Resource or Handle|weakness +CWE-1342|Information Exposure through Microarchitectural State after Transient Execution|weakness +CWE-1343|Weaknesses in the 2021 CWE Most Important Hardware Weaknesses List|view +CWE-1344|Weaknesses in OWASP Top Ten (2021)|view +CWE-1345|OWASP Top Ten 2021 Category A01:2021 - Broken Access Control|category +CWE-1346|OWASP Top Ten 2021 Category A02:2021 - Cryptographic Failures|category +CWE-1347|OWASP Top Ten 2021 Category A03:2021 - Injection|category +CWE-1348|OWASP Top Ten 2021 Category A04:2021 - Insecure Design|category +CWE-1349|OWASP Top Ten 2021 Category A05:2021 - Security Misconfiguration|category +CWE-1350|Weaknesses in the 2020 CWE Top 25 Most Dangerous Software Weaknesses|view +CWE-1351|Improper Handling of Hardware Behavior in Exceptionally Cold Environments|weakness +CWE-1352|OWASP Top Ten 2021 Category A06:2021 - Vulnerable and Outdated Components|category +CWE-1353|OWASP Top Ten 2021 Category A07:2021 - Identification and Authentication Failures|category +CWE-1354|OWASP Top Ten 2021 Category A08:2021 - Software and Data Integrity Failures|category +CWE-1355|OWASP Top Ten 2021 Category A09:2021 - Security Logging and Monitoring Failures|category +CWE-1356|OWASP Top Ten 2021 Category A10:2021 - Server-Side Request Forgery (SSRF)|category +CWE-1357|Reliance on Insufficiently Trustworthy Component|weakness +CWE-1358|Weaknesses in SEI ETF Categories of Security Vulnerabilities in ICS|view +CWE-1359|ICS Communications|category +CWE-1360|ICS Dependencies (& Architecture)|category +CWE-1361|ICS Supply Chain|category +CWE-1362|ICS Engineering (Constructions/Deployment)|category +CWE-1363|ICS Operations (& Maintenance)|category +CWE-1364|ICS Communications: Zone Boundary Failures|category +CWE-1365|ICS Communications: Unreliability|category +CWE-1366|ICS Communications: Frail Security in Protocols|category +CWE-1367|ICS Dependencies (& Architecture): External Physical Systems|category +CWE-1368|ICS Dependencies (& Architecture): External Digital Systems|category +CWE-1369|ICS Supply Chain: IT/OT Convergence/Expansion|category +CWE-1370|ICS Supply Chain: Common Mode Frailties|category +CWE-1371|ICS Supply Chain: Poorly Documented or Undocumented Features|category +CWE-1372|ICS Supply Chain: OT Counterfeit and Malicious Corruption|category +CWE-1373|ICS Engineering (Construction/Deployment): Trust Model Problems|category +CWE-1374|ICS Engineering (Construction/Deployment): Maker Breaker Blindness|category +CWE-1375|ICS Engineering (Construction/Deployment): Gaps in Details/Data|category +CWE-1376|ICS Engineering (Construction/Deployment): Security Gaps in Commissioning|category +CWE-1377|ICS Engineering (Construction/Deployment): Inherent Predictability in Design|category +CWE-1378|ICS Operations (& Maintenance): Gaps in obligations and training|category +CWE-1379|ICS Operations (& Maintenance): Human factors in ICS environments|category +CWE-1380|ICS Operations (& Maintenance): Post-analysis changes|category +CWE-1381|ICS Operations (& Maintenance): Exploitable Standard Operational Procedures|category +CWE-1382|ICS Operations (& Maintenance): Emerging Energy Technologies|category +CWE-1383|ICS Operations (& Maintenance): Compliance/Conformance with Regulatory Requirements|category +CWE-1384|Improper Handling of Physical or Environmental Conditions|weakness +CWE-1385|Missing Origin Validation in WebSockets|weakness +CWE-1386|Insecure Operation on Windows Junction / Mount Point|weakness +CWE-1387|Weaknesses in the 2022 CWE Top 25 Most Dangerous Software Weaknesses|view +CWE-1388|Physical Access Issues and Concerns|category +CWE-1389|Incorrect Parsing of Numbers with Different Radices|weakness +CWE-1390|Weak Authentication|weakness +CWE-1391|Use of Weak Credentials|weakness +CWE-1392|Use of Default Credentials|weakness +CWE-1393|Use of Default Password|weakness +CWE-1394|Use of Default Cryptographic Key|weakness +CWE-1395|Dependency on Vulnerable Third-Party Component|weakness +CWE-2000|Comprehensive CWE Dictionary|view diff --git a/lib/CSAF/Validator.pm b/lib/CSAF/Validator.pm new file mode 100644 index 0000000..4b428e5 --- /dev/null +++ b/lib/CSAF/Validator.pm @@ -0,0 +1,47 @@ +package CSAF::Validator; + +use 5.010001; +use strict; +use warnings; + +use CSAF::Validator::MandatoryTests; +use CSAF::Validator::OptionalTests; +use CSAF::Validator::Schema; +use CSAF::Validator::Message; + +use constant DEBUG => $ENV{CSAF_DEBUG}; + +use Moo; +extends 'CSAF::Validator::Base'; + +sub validate { + + my $self = shift; + + my @messages = (); + + # 9.1.14 Conformance Clause 14: CSAF basic validator + + my @schema_errors = CSAF::Validator::Schema->new($self->csaf)->validate; + push @messages, @schema_errors; + + my @mandatory_errors = CSAF::Validator::MandatoryTests->new($self->csaf)->validate; + push @messages, @mandatory_errors; + + my @optional_warnings = CSAF::Validator::OptionalTests->new($self->csaf)->validate; + push @messages, @optional_warnings; + + $self->messages(\@messages); + + if (DEBUG && @messages) { + say STDERR "\nValidation messages(s):"; + say STDERR sprintf('- %s', $_) for (@messages); + say STDERR ""; + } + + + return @{$self->messages}; + +} + +1; diff --git a/lib/CSAF/Validator/Base.pm b/lib/CSAF/Validator/Base.pm new file mode 100644 index 0000000..3603809 --- /dev/null +++ b/lib/CSAF/Validator/Base.pm @@ -0,0 +1,57 @@ +package CSAF::Validator::Base; + +use 5.010001; +use strict; +use warnings; + +use List::Util qw(first); + +use Moo; +extends 'CSAF::Base'; + +use constant DEBUG => $ENV{CSAF_DEBUG}; + +our %TESTS = (); + +has messages => (is => 'rw', default => sub { [] }); + +has tests => (is => 'rw', default => sub { [] }); + +sub validate { Carp::croak 'Method "validate" not implemented by subclass' } + +sub has_error { + (first { $_->type eq 'error' } @{$_[0]->messages}) ? 1 : 0; +} + +sub has_warning { + (first { $_->type eq 'warning' } @{$_[0]->messages}) ? 1 : 0; +} + +sub add_message { + + my ($self, $message) = @_; + + $self->{messages} ||= []; + push @{$self->{messages}}, $message; + +} + +sub exec_test { + + my ($self, $test_id) = @_; + + my $test_sub = "TEST_$test_id"; + $test_sub =~ tr/\./_/; + + if (my $code_ref = $self->can($test_sub)) { + + DEBUG and say STDERR sprintf '(I) %s - Execute test %s', ref($self), $test_id; + + eval { $code_ref->($self) }; + Carp::croak "Failed to execute test $test_id: $@" if ($@); + + } + +} + +1; diff --git a/lib/CSAF/Validator/MandatoryTests.pm b/lib/CSAF/Validator/MandatoryTests.pm new file mode 100644 index 0000000..1d99503 --- /dev/null +++ b/lib/CSAF/Validator/MandatoryTests.pm @@ -0,0 +1,1151 @@ +package CSAF::Validator::MandatoryTests; + +use 5.010001; +use strict; +use warnings; +use version; + +use CSAF::Util qw(get_weakness_name check_purl collect_product_ids schema_cache_path); +use CSAF::Validator::Message; + +use List::MoreUtils qw(uniq duplicates); +use List::Util qw(first); +use JSON::Validator; +use URI::PackageURL; + +use Moo; +extends 'CSAF::Validator::Base'; + +use constant DEBUG => $ENV{CSAF_DEBUG}; + +my @TESTS = ( + '6.1.1', '6.1.2', '6.1.3', '6.1.4', '6.1.5', '6.1.6', '6.1.7', '6.1.8', + '6.1.9', '6.1.10', '6.1.11', '6.1.12', '6.1.13', '6.1.14', '6.1.15', '6.1.16', + '6.1.17', '6.1.18', '6.1.19', '6.1.20', '6.1.21', '6.1.22', '6.1.23', '6.1.24', + '6.1.25', '6.1.26', '6.1.27.1', '6.1.27.2', '6.1.27.3', '6.1.27.4', '6.1.27.5', '6.1.27.6', + '6.1.27.7', '6.1.27.8', '6.1.27.9', '6.1.27.10', '6.1.27.11', '6.1.28', '6.1.29', '6.1.30', + '6.1.31', '6.1.32', '6.1.33', +); + +my $PURL_REGEX = qr{^pkg:[A-Za-z\\.\\-\\+][A-Za-z0-9\\.\\-\\+]*/.+}; + +my $SEMVER_REGEXP + = qr{^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$}; + + +sub validate { + + my $self = shift; + + foreach my $test_id (@TESTS) { + + $self->exec_test($test_id); + + if (DEBUG) { + + state $last_tot_msgs = 0; + + my $tot_msgs = @{$self->messages}; + my $test_tot_msgs = $tot_msgs - $last_tot_msgs; + + if ($test_tot_msgs > 0) { + say STDERR sprintf('(E) Mandatory Test %s --> found %s validation error(s)', $test_id, $test_tot_msgs); + } + + $last_tot_msgs = $tot_msgs; + + } + + } + + return @{$self->messages}; + +} + + +sub TEST_6_1_1 { # TODO INCOMPLETE + + my $self = shift; + + DEBUG and say STDERR '(W) Incomplete Mandatory Test 6.1.1'; + + my @product_ids = (); + + $self->csaf->product_tree->full_product_names->each(sub { + push @product_ids, $_[0]->product_id; + }); + + return unless @product_ids; + + my @product_statuses = ( + 'first_affected', 'first_fixed', 'fixed', 'known_affected', + 'known_not_affected', 'last_affected', 'recommended', 'under_investigation', + ); + + $self->csaf->vulnerabilities->each(sub { + + my ($vulnerability, $idx) = @_; + + # /vulnerabilities[]/product_status/first_affected[] + # /vulnerabilities[]/product_status/first_fixed[] + # /vulnerabilities[]/product_status/fixed[] + # /vulnerabilities[]/product_status/known_affected[] + # /vulnerabilities[]/product_status/known_not_affected[] + # /vulnerabilities[]/product_status/last_affected[] + # /vulnerabilities[]/product_status/recommended[] + # /vulnerabilities[]/product_status/under_investigation[] + + foreach my $product_status (@product_statuses) { + + my $method = $vulnerability->product_status->can($product_status); + my @products = @{$method->($vulnerability->product_status)}; + + foreach my $product (@products) { + if (!first { $product eq $_ } @product_ids) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => sprintf('/vulnerabilities/%s/product_status/%s', $idx, $product_status), + code => '6.1.1', + message => sprintf('Missing Definition of Product ID (%s)', $product) + )); + } + } + + } + + + # /vulnerabilities[]/scores[]/products[] + + $vulnerability->scores->each(sub { + + my ($score, $score_idx) = @_; + + foreach my $product (@{$score->products}) { + if (!first { $product eq $_ } @product_ids) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => sprintf('/vulnerabilities/%s/scores/%s/products', $idx, $score_idx), + code => '6.1.1', + message => sprintf('Missing Definition of Product ID (%s)', $product) + )); + } + } + + }); + + + # /vulnerabilities[]/remediations[]/product_ids[] + + $vulnerability->remediations->each(sub { + + my ($remediation, $remediation_idx) = @_; + + foreach my $product (@{$remediation->product_ids}) { + if (!first { $product eq $_ } @product_ids) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => sprintf('/vulnerabilities/%s/remediations/%s/product_ids', $idx, $remediation_idx), + code => '6.1.1', + message => sprintf('Missing Definition of Product ID (%s)', $product) + )); + } + } + + }); + + + # /vulnerabilities[]/threats[]/product_ids[] + + $vulnerability->threats->each(sub { + + my ($threat, $threat_idx) = @_; + + foreach my $product (@{$threat->product_ids}) { + if (!first { $product eq $_ } @product_ids) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => sprintf('/vulnerabilities/%s/threats/%s/product_ids', $idx, $threat_idx), + code => '6.1.1', + message => sprintf('Missing Definition of Product ID (%s)', $product) + )); + } + } + + }); + + }); + + + # /product_tree/product_groups[]/product_ids[] + + $self->csaf->product_tree->product_groups->each(sub { + + my ($product_group, $idx) = @_; + + foreach my $product (@{$product_group->product_ids}) { + if (!first { $product eq $_ } @product_ids) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => sprintf('/product_tree/product_groups/%s/product_ids', $idx), + code => '6.1.1', + message => sprintf('Missing Definition of Product ID (%s)', $product) + )); + } + } + + }); + + + # /product_tree/relationships[]/product_reference + # /product_tree/relationships[]/relates_to_product_reference + +} + +sub TEST_6_1_2 { # TODO INCOMPLETE + + my $self = shift; + + DEBUG and say STDERR '(W) Incomplete Mandatory Test 6.1.2'; + + if (@{$self->csaf->product_tree->branches->items}) { + + my @product_ids = (); + + $self->csaf->product_tree->branches->each(sub { + my ($branch) = @_; + push @product_ids, collect_product_ids($branch); + }); + + if (duplicates @product_ids) { + + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => '/product_tree/branches[](/branches[])*/product/product_id', + code => '6.1.2', + message => 'Multiple Definition of Product ID' + )); + + } + + } + + if ($self->csaf->product_tree->full_product_names->size) { + + my @product_ids = (); + + $self->csaf->product_tree->full_product_names->each(sub { + my ($product, $idx) = @_; + push @product_ids, collect_product_ids($product); + }); + + if (duplicates @product_ids) { + + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => '/product_tree/full_product_names[]/product_id', + code => '6.1.2', + message => 'Multiple Definition of Product ID' + )); + + } + + } + + # TODO + # /product_tree/relationships[]/full_product_name/product_id + +} + +sub TEST_6_1_6 { + + my $self = shift; + + return unless $self->csaf->vulnerabilities->size; + + $self->csaf->vulnerabilities->each(sub { + + my ($vulnerability, $idx) = @_; + + my $product_status = $vulnerability->product_status; + + my @affected_group = uniq( + @{$product_status->first_affected}, + @{$product_status->known_affected}, + @{$product_status->last_affected} + ); + + my @not_affected_group = uniq(@{$product_status->known_not_affected}); + my @fixed_group = uniq(@{$product_status->first_fixed}, @{$product_status->fixed}); + my @under_investigation_group = uniq(@{$product_status->under_investigation}); + + my @check = (@affected_group, @not_affected_group, @fixed_group, @under_investigation_group); + + if (duplicates @check) { + + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/vulnerabilities/$idx/product_status", + code => '6.1.6', + message => 'Contradicting Product Status' + )); + + } + + }); + +} + +sub TEST_6_1_7 { + + my $self = shift; + + $self->csaf->vulnerabilities->each(sub { + + my ($vulnerability, $idx) = @_; + + my $check = {}; + + $vulnerability->scores->each(sub { + + my ($score, $score_idx) = @_; + + foreach my $product (@{$score->products}) { + + $check->{$product}++; + + if ($check->{$product} > 1) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/vulnerabilities/$idx/score/$score_idx/products", + code => '6.1.7', + message => 'Multiple Scores with same Version per Product' + )); + } + + } + }); + + }); + +} + +sub TEST_6_1_8 { + + # /vulnerabilities[]/scores[]/cvss_v2 + # /vulnerabilities[]/scores[]/cvss_v3 + + my $self = shift; + + my $SCHEMAS = { + cvss2 => {'$ref' => 'https://www.first.org/cvss/cvss-v2.0.json'}, + cvss3 => { + oneOf => [ + {'$ref' => 'https://www.first.org/cvss/cvss-v3.0.json'}, + {'$ref' => 'https://www.first.org/cvss/cvss-v3.1.json'} + ] + } + }; + + $self->csaf->vulnerabilities->each(sub { + + my ($vulnerability, $idx) = @_; + + $vulnerability->scores->each(sub { + + my ($score, $score_idx) = @_; + + if (my $cvss3 = $score->cvss_v3) { + + my $jv = JSON::Validator->new; + + $jv->cache_paths([schema_cache_path]); + $jv->schema($SCHEMAS->{cvss3}); + + my @schema_errors = $jv->validate($cvss3->TO_JSON); + + foreach my $schema_error (@schema_errors) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/vulnerabilities/$idx/scores/$score_idx/cvss_v3" . $schema_error->path, + code => '6.1.8', + message => sprintf('Invalid CVSS: %s', $schema_error->message) + )); + } + + } + + if (my $cvss2 = $score->cvss_v2) { + + my $jv = JSON::Validator->new; + + $jv->cache_paths([schema_cache_path]); + $jv->schema($SCHEMAS->{cvss2}); + + my @schema_errors = $jv->validate($cvss2->TO_JSON); + + foreach my $schema_error (@schema_errors) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/vulnerabilities/$idx/scores/$score_idx/cvss_v2" . $schema_error->path, + code => '6.1.8', + message => sprintf('Invalid CVSS: %s', $schema_error->message) + )); + } + + } + + }); + }); + +} + +sub TEST_6_1_9 { # TODO INCOMPLETE + + my $self = shift; + + DEBUG and say STDERR '(W) Incomplete Mandatory Test 6.1.9'; + + my $cvss2_severity = {LOW => [0, 3.9], MEDIUM => [4, 6.9], HIGH => [7, 10]}; + my $cvss3_severity = {LOW => [0, 3.9], MEDIUM => [4, 6.9], HIGH => [7, 8.9], CRITICAL => [9, 10]}; + + $self->csaf->vulnerabilities->each(sub { + + my ($vulnerability, $idx) = @_; + + $vulnerability->scores->each(sub { + + my ($score, $score_idx) = @_; + + if (my $cvss3 = $score->cvss_v3) { + + my ($score_min, $score_max) = @{$cvss3_severity->{$cvss3->baseSeverity}}; + + unless ($cvss3->baseScore >= $score_min && $cvss3->baseScore <= $score_max) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/vulnerabilities/$idx/score/$score_idx/cvss_v3", + code => '6.1.9', + message => 'Invalid CVSS computation' + )); + } + + } + + }); + }); +} + +sub TEST_6_1_11 { + + my $self = shift; + + $self->csaf->vulnerabilities->each(sub { + + my ($vulnerability, $idx) = @_; + + if (my $cwe_id = $vulnerability->cwe->id) { + + if (!get_weakness_name($cwe_id)) { + + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/vulnerabilities/$idx/cwe/id", + code => '6.1.11', + message => 'Unknown CWE' + )); + + } + + } + + if (my $cwe_name = $vulnerability->cwe->name) { + + if (get_weakness_name($vulnerability->cwe->id) ne $cwe_name) { + + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/vulnerabilities/$idx/cwe/name", + code => '6.1.11', + message => 'CWE name differs from the official CWE catalog' + )); + + } + } + + }); + +} + +sub TEST_6_1_13 { # TODO INCOMPLETE + + my $self = shift; + + DEBUG and say STDERR '(W) Incomplete Mandatory Test 6.1.13'; + + # /product_tree/branches[](/branches[])*/product/product_identification_helper/purl + # /product_tree/full_product_names[]/product_identification_helper/purl + # /product_tree/relationships[]/full_product_name/product_identification_helper/purl + + $self->csaf->product_tree->full_product_names->each(sub { + + my ($full_product_name, $idx) = @_; + + return unless $full_product_name->product_identification_helper; + + my $purl = $full_product_name->product_identification_helper->purl; + + my $is_invalid = 0; + + $is_invalid = 1 if $purl !~ /$PURL_REGEX/; + + eval { URI::PackageURL->from_string($purl) }; + + if ($@) { + $is_invalid = 1 if $@; + DEBUG and say STDERR "$@"; + } + + if ($is_invalid) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/product_tree/full_product_names/$idx/product_identification_helper/purl", + code => '6.1.13', + message => 'Invalid purl' + )); + } + + }); + +} + +sub TEST_6_1_15 { + + my $self = shift; + + if ($self->csaf->document->publisher->category eq 'translator' && !$self->csaf->document->source_lang) { + + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => '/document/publisher/category', + code => '6.1.15', + message => 'Missing "source_lang" for "translator" publisher category' + )); + + } + +} + +sub TEST_6_1_16 { + + my $self = shift; + + my $current_version = 0; + my $last_version = undef; + + # TODO Use semver instead of version module + eval { + + foreach my $revision (@{$self->csaf->document->tracking->revision_history->items}) { + $last_version = $revision->number if (version->parse($current_version) < version->parse($revision->number)); + $current_version = $revision->number; + } + + if (version->parse($last_version) > version->parse($self->csaf->document->tracking->version)) { + + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => '/document/tracking/version', + code => '6.1.16', + message => 'Detected newer revision of document' + )); + + } + + } + +} + +sub TEST_6_1_17 { + + my $self = shift; + + my $document_version = $self->csaf->document->tracking->version; + my $document_status = $self->csaf->document->tracking->status; + + $document_version =~ /$SEMVER_REGEXP/; + + if ($document_status ne 'draft' && ($document_version eq '0' || (%+ && ($+{major} == 0 || $+{prerelease})))) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => '/document/tracking/version', + code => '6.1.17', + message => 'Incompatible document status & version' + )); + } + +} + +sub TEST_6_1_18 { + + my $self = shift; + + my $document_status = $self->csaf->document->tracking->status; + my $document_revisions = $self->csaf->document->tracking->revision_history; + + if ($document_status =~ /(final|interim)/) { + + $document_revisions->each(sub { + + my ($revision, $idx) = @_; + + $revision->number =~ /$SEMVER_REGEXP/; + + if ($revision->number eq '0' || (%+ && ($+{major} == 0))) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/document/tracking/revision_history/$idx/number", + code => '6.1.18', + message => 'Incompatible revision number with document status' + )); + } + + }); + + } + +} + +sub TEST_6_1_19 { + + my $self = shift; + + my $document_revisions = $self->csaf->document->tracking->revision_history; + + $document_revisions->each(sub { + + my ($revision, $idx) = @_; + + $revision->number =~ /$SEMVER_REGEXP/; + + if (%+ && $+{prerelease}) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/document/tracking/revision_history/$idx/number", + code => '6.1.19', + message => 'Revision History contains a pre-release' + )); + } + + }); + +} + +sub TEST_6_1_20 { + + my $self = shift; + + my $document_version = $self->csaf->document->tracking->version; + my $document_status = $self->csaf->document->tracking->status; + + if ($document_status =~ /(final|interim)/) { + + $document_version =~ /$SEMVER_REGEXP/; + + if (%+ && $+{prerelease}) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => '/document/tracking/version', + code => '6.1.20', + message => qq{Detected a pre-release version with "$document_status" document} + )); + } + } + + +} + +sub TEST_6_1_22 { + + my $self = shift; + + my $check = {}; + + $self->csaf->document->tracking->revision_history->each(sub { + + my ($revision, $idx) = @_; + + $check->{$revision->number}++; + + if ($check->{$revision->number} > 1) { + + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/document/tracking/revision_history/$idx/number", + code => '6.1.22', + message => 'Multiple Definition in Revision History' + )); + + } + + }); + +} + +sub TEST_6_1_23 { + + my $self = shift; + + my $check = {}; + + $self->csaf->vulnerabilities->each(sub { + + my ($vulnerability, $idx) = @_; + + return unless $vulnerability->cve; + + $check->{$vulnerability->cve}++; + + if ($check->{$vulnerability->cve} > 1) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/vulnerabilities/$idx/cve", + code => '6.1.23', + message => sprintf('Multiple Use of Same CVE (%s)', $vulnerability->cve) + )); + } + + }); + +} + +sub TEST_6_1_25 { # TODO INCOMPLETE + + my $self = shift; + + DEBUG and say STDERR '(W) Incomplete Mandatory Test 6.1.25'; + + # /product_tree/branches[](/branches[])*/product/product_identification_helper/hashes[]/file_hashes + + $self->_TEST_6_1_25_branches($self->csaf->product_tree->branches, '/product_tree/branches'); + + # /product_tree/relationships[]/full_product_name/product_identification_helper/hashes[]/file_hashes + + # TODO INCOMPLETE TEST + + # /product_tree/full_product_names[]/product_identification_helper/hashes[]/file_hashes + + my $full_product_names = $self->csaf->product_tree->full_product_names; + + $full_product_names->each(sub { + + my ($full_product_name, $idx) = @_; + + return unless $full_product_name->product_identification_helper; + + $full_product_name->product_identification_helper->hashes->each(sub { + + my ($hash, $hash_idx) = @_; + + my $check = {}; + + $hash->file_hashes->each(sub { + + my ($file_hash, $file_hash_idx) = @_; + + $check->{$file_hash->algorithm}++; + + if ($check->{$file_hash->algorithm} > 1) { + + my $path = "/product_tree/full_product_names/$idx/product_identification_helper" + . "/hashes/$hash_idx/file_hashes/$file_hash_idx/"; + + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => $path, + code => '6.1.25', + message => sprintf('Multiple Use of Same Hash Algorithm (%s)', $file_hash->algorithm) + )); + + } + + }); + + }); + + }); + +} + +sub TEST_6_1_26 { + + my $self = shift; + + my $document_category = $self->csaf->document->category; + + if ($document_category + !~ /(csaf_base|csaf_security_incident_response|csaf_informational_advisory|csaf_security_advisory|csaf_vex)/) + { + + if ($document_category =~ /^csaf_/i) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => '/document/category', + code => '6.1.26', + message => 'Reserved CSAF document category prefix' + )); + } + + my $check_similar_category = 0; + + my @similar_categories = qw( + informationaladvisory + securityincidentresponse + securityadvisory + vex + ); + + (my $normalized_category = lc $document_category) =~ s/[-_\s]//g; + + if (first { $normalized_category =~ /^$_/ } @similar_categories) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => '/document/category', + code => '6.1.26', + message => 'Prohibited document category' + )); + } + + } +} + +sub TEST_6_1_27_1 { + + my $self = shift; + + my $document_category = $self->csaf->document->category; + my $document_notes = $self->csaf->document->notes->items; + + if ($document_category =~ /(csaf_informational_advisory|csaf_security_incident_response)/) { + + my $have_valid_category = undef; + + foreach my $note (@{$document_notes}) { + foreach my $category (qw(description details general summary)) { + $have_valid_category = 1 if ($note->category eq $category); + } + } + + if (not $have_valid_category) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => '/document/notes', + code => '6.1.27.1', + message => + 'The document notes do not contain an item which has a category of "description", "details", "general" or "summary"' + )); + } + + + } + +} + +sub TEST_6_1_27_2 { + + my $self = shift; + + my $document_category = $self->csaf->document->category; + my $document_references = $self->csaf->document->references->items; + + if ($document_category =~ /(csaf_informational_advisory|csaf_security_incident_response)/) { + + my $have_external_references = undef; + + foreach my $reference (@{$document_references}) { + $have_external_references = 1 if ($reference->category eq 'external'); + } + + if (not $have_external_references) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => '/document/references', + code => '6.1.27.2', + message => 'The document references do not contain any item which has the category "external"' + )); + } + + + } + +} + +sub TEST_6_1_27_3 { + + my $self = shift; + + if ($self->csaf->document->category eq 'csaf_informational_advisory' && @{$self->csaf->vulnerabilities->items}) { + + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => '/vulnerabilities', + code => '6.1.27.3', + message => + 'The "csaf_informational_advisory" profile deals with information that are not classified as vulnerabilities. Therefore, it must not have the "/vulnerabilities" element' + )); + + } + +} + +sub TEST_6_1_27_4 { + + my $self = shift; + + my $document_category = $self->csaf->document->category; + my $product_tree = $self->csaf->product_tree->TO_BUILD; # TODO !? + + if ($document_category =~ /(csaf_security_advisory|csaf_vex)/ && !$product_tree) { + + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => '/product_tree', + code => '6.1.27.4', + message => 'The element "/product_tree" does not exist' + )); + + } + +} + +sub TEST_6_1_27_5 { + + my $self = shift; + + if ($self->csaf->document->category =~ /(csaf_security_advisory|csaf_vex)/) { + + $self->csaf->vulnerabilities->each(sub { + + my ($vulnerability, $idx) = @_; + + if (!$vulnerability->notes->size) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/vulnerabilities/$idx", + code => '6.1.27.5', + message => 'The vulnerability item has no "notes" element' + )); + } + + }); + + } + +} + +sub TEST_6_1_27_6 { + + my $self = shift; + + if ($self->csaf->document->category eq 'csaf_security_advisory') { + + $self->csaf->vulnerabilities->each(sub { + + my ($vulnerability, $idx) = @_; + + if (!$vulnerability->product_status->TO_BUILD) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/vulnerabilities/$idx", + code => '6.1.27.6', + message => 'The vulnerability item has no "product_status" element' + )); + } + + }); + + } + +} + +sub TEST_6_1_27_7 { + + my $self = shift; + + if ($self->csaf->document->category eq 'csaf_vex') { + + $self->csaf->vulnerabilities->each(sub { + + my ($vulnerability, $idx) = @_; + + my @check = ( + @{$vulnerability->product_status->fixed}, + @{$vulnerability->product_status->known_affected}, + @{$vulnerability->product_status->known_not_affected}, + @{$vulnerability->product_status->under_investigation} + ); + + unless (@check) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/vulnerabilities/$idx/product_status", + code => '6.1.27.7', + message => + 'None of the elements "fixed", "known_affected", "known_not_affected", or "under_investigation" is present in "product_status"' + )); + } + + }); + + } + +} + +sub TEST_6_1_27_8 { + + my $self = shift; + + if ($self->csaf->document->category eq 'csaf_vex') { + + $self->csaf->vulnerabilities->each(sub { + + my ($vulnerability, $idx) = @_; + + if (!$vulnerability->cve && $vulnerability->ids->size == 0) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "/vulnerabilities/$idx", + code => '6.1.27.8', + message => 'None of the elements "cve" or "ids" is present' + )); + } + + }); + + } + +} + +sub TEST_6_1_27_11 { + + my $self = shift; + + if ( $self->csaf->document->category =~ /(csaf_security_advisory|csaf_vex)/ + && $self->csaf->vulnerabilities->size == 0) + { + + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => '/vulnerabilities', + code => '6.1.27.11', + message => 'The element "/vulnerabilities" does not exist' + )); + + } + +} + +sub TEST_6_1_28 { + + my $self = shift; + + my $document_lang = $self->csaf->document->lang; + my $document_source_lang = $self->csaf->document->source_lang; + + if ($document_lang && $document_source_lang && ($document_lang eq $document_source_lang)) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => '/document/lang', + code => '6.1.28', + message => qq{The document language and the source language have the same value "$document_lang"} + )); + } + +} + +sub TEST_6_1_31 { + + my $self = shift; + + if ($self->csaf->product_tree) { + $self->_TEST_6_1_31_branches($self->csaf->product_tree->branches, "/product_tree/branches"); + } + +} + +sub _TEST_6_1_25_branches { + + my ($self, $branches, $path) = @_; + + $branches->each(sub { + + my ($branch, $idx) = @_; + + $self->_TEST_6_1_25_branches($branch->branches, "$path/$idx/branches"); + + if ( $branch->product + && $branch->product->product_identification_helper + && $branch->product->product_identification_helper->hashes->size) + { + + $branch->product->product_identification_helper->hashes->each(sub { + + my ($hash, $hash_idx) = @_; + + my $check = {}; + + $hash->file_hashes->each(sub { + + my ($file_hash, $file_hash_idx) = @_; + + $check->{$file_hash->algorithm}++; + + if ($check->{$file_hash->algorithm} > 1) { + + $self->add_message(CSAF::Validator::Message->new( + type => 'Mandatory Test', + path => + "/$path/$idx/product_identification_helper/hashes/$hash_idx/file_hashes/$file_hash_idx/", + code => '6.1.25', + message => sprintf('Multiple Use of Same Hash Algorithm (%s)', $file_hash->algorithm) + )); + + } + + }); + + }); + + } + + }); +} + +sub _TEST_6_1_31_branches { + + my ($self, $branches, $path) = @_; + + my @bad_ranges = qw( < <= > >= after all before earlier later prior versions ); + + $branches->each(sub { + + my ($branch, $idx) = @_; + + $self->_TEST_6_1_31_branches($branch->branches, "$path/$idx/branches"); + + if ($branch->category eq 'product_version') { + foreach (@bad_ranges) { + if (lc $branch->name =~ /$_/) { + $self->add_message(CSAF::Validator::Message->new( + context => 'Mandatory Test', + path => "$path/name", + code => '6.1.31', + message => 'Version Range in Product Version' + )); + } + } + } + + }); +} + +1; diff --git a/lib/CSAF/Validator/Message.pm b/lib/CSAF/Validator/Message.pm new file mode 100644 index 0000000..7397474 --- /dev/null +++ b/lib/CSAF/Validator/Message.pm @@ -0,0 +1,33 @@ +package CSAF::Validator::Message; + +use 5.010001; +use strict; +use warnings; + +use Moo; + +use overload '""' => \&to_string, bool => sub {1}, fallback => 1; + +has message => (is => 'ro', required => 1); +has code => (is => 'ro'); +has path => (is => 'ro'); +has type => (is => 'ro', default => 'error'); +has context => (is => 'ro', required => 1); + +sub to_string { + sprintf '[%s] %s: %s (%s - %s)', $_[0]->type, $_[0]->path, $_[0]->message, $_[0]->context, $_[0]->code; +} + +sub TO_JSON { + + return { + type => $_[0]->type, + context => $_[0]->context, + message => $_[0]->message, + path => $_[0]->path, + code => $_[0]->code + }; + +} + +1; diff --git a/lib/CSAF/Validator/OptionalTests.pm b/lib/CSAF/Validator/OptionalTests.pm new file mode 100644 index 0000000..e5ae36c --- /dev/null +++ b/lib/CSAF/Validator/OptionalTests.pm @@ -0,0 +1,196 @@ +package CSAF::Validator::OptionalTests; + +use 5.010001; +use strict; +use warnings; +use version; + +use CSAF::Util qw(get_weakness_name check_purl collect_product_ids schema_cache_path); +use CSAF::Validator::Message; + +use List::MoreUtils qw(uniq duplicates); +use List::Util qw(first); +use JSON::Validator; +use URI::PackageURL; + +use Moo; +extends 'CSAF::Validator::Base'; + +use constant DEBUG => $ENV{CSAF_DEBUG}; + +my @TESTS = ('6.2.2', '6.2.3', '6.2.4', '6.2.14'); + +sub validate { + + my $self = shift; + + foreach my $test_id (@TESTS) { + + $self->exec_test($test_id); + + if (DEBUG) { + + state $last_tot_msgs = 0; + + my $tot_msgs = @{$self->messages}; + my $test_tot_msgs = $tot_msgs - $last_tot_msgs; + + if ($test_tot_msgs > 0) { + say STDERR sprintf('(W) Optional Test %s --> found %s validation warning(s)', $test_id, $test_tot_msgs); + } + + $last_tot_msgs = $tot_msgs; + + } + + } + + return @{$self->messages}; + +} + +sub TEST_6_2_2 { + + my $self = shift; + + return unless $self->csaf->vulnerabilities->size; + + my @statuses = qw(first_affected known_affected last_affected under_investigation); + + $self->csaf->vulnerabilities->each(sub { + + my ($vulnerability, $vulnerability_idx) = @_; + + my $product_status = $vulnerability->product_status; + + for my $status (@statuses) { + + my @product_ids = @{$product_status->$status}; + my $product_idx = 0; + + foreach my $product_id (@product_ids) { + foreach my $remediation ($vulnerability->remediations->each) { + if (!first { $product_id eq $_ } @{$remediation->product_ids}) { + $self->add_message(CSAF::Validator::Message->new( + type => 'warning', + context => 'Mandatory Test', + path => "/vulnerabilities/$vulnerability_idx/product_status/$status/$product_idx", + code => '6.2.2', + message => 'Missing Remediation' + )); + } + } + $product_idx++; + } + + } + + }); + +} + +sub TEST_6_2_3 { + + my $self = shift; + + return unless $self->csaf->vulnerabilities->size; + + my @statuses = qw(first_affected known_affected last_affected); + + $self->csaf->vulnerabilities->each(sub { + + my ($vulnerability, $vulnerability_idx) = @_; + + my $product_status = $vulnerability->product_status; + + for my $status (@statuses) { + + my @product_ids = @{$product_status->$status}; + my $product_idx = 0; + + foreach my $product_id (@product_ids) { + foreach my $score ($vulnerability->scores->each) { + if (!first { $product_id eq $_ } @{$score->products}) { + $self->add_message(CSAF::Validator::Message->new( + type => 'warning', + context => 'Mandatory Test', + path => "/vulnerabilities/$vulnerability_idx/product_status/$status/$product_idx", + code => '6.2.3', + message => 'Missing Score' + )); + } + } + $product_idx++; + } + + } + + }); + +} + +sub TEST_6_2_4 { + + my $self = shift; + + my $document_revisions = $self->csaf->document->tracking->revision_history; + + $document_revisions->each(sub { + + my ($revision, $idx) = @_; + + if ($revision->number =~ /\+/) { + $self->add_message(CSAF::Validator::Message->new( + type => 'warning', + context => 'Mandatory Test', + path => "/document/tracking/revision_history/$idx/number", + code => '6.2.4', + message => 'Build Metadata in Revision History' + )); + } + + }); + +} + +sub TEST_6_2_14 { + + my $self = shift; + + if (my $lang = $self->csaf->document->lang) { + + if ($lang =~ /(q([a-t])([a-z]))/gi) { + + $self->add_message(CSAF::Validator::Message->new( + type => 'warning', + context => 'Optional Test', + path => '/document/lang', + code => '6.2.14', + message => 'Use of Private Language' + )); + + } + + } + + if (my $lang = $self->csaf->document->source_lang) { + + if ($lang =~ /(q([a-t])([a-z]))/gi) { + + $self->add_message(CSAF::Validator::Message->new( + type => 'warning', + context => 'Optional Test', + path => '/document/source_lang', + code => '6.2.14', + message => 'Use of Private Language' + )); + + } + + } + + +} + + +1; diff --git a/lib/CSAF/Validator/Schema.pm b/lib/CSAF/Validator/Schema.pm new file mode 100644 index 0000000..bd8741f --- /dev/null +++ b/lib/CSAF/Validator/Schema.pm @@ -0,0 +1,38 @@ +package CSAF::Validator::Schema; + +use 5.010001; +use strict; +use warnings; + +use CSAF::Util qw(schema_cache_path); +use CSAF::Builder; +use JSON::Validator; + +use Moo; +extends 'CSAF::Validator::Base'; + +sub validate { + + my ($self) = @_; + + my $jv = JSON::Validator->new; + + $jv->cache_paths([schema_cache_path]); + $jv->schema('https://docs.oasis-open.org/csaf/csaf/v2.0/os/schemas/csaf_json_schema.json'); + + my @errors = $jv->validate(CSAF::Builder->new(shift->csaf)->build(1)); + + foreach my $error (@errors) { + $self->add_message(CSAF::Validator::Message->new( + context => 'JSON Schema', + message => $error->message, + path => $error->path, + code => '9.1.14' + )); + } + + return @{$self->messages}; + +} + +1; diff --git a/lib/CSAF/Writer.pm b/lib/CSAF/Writer.pm new file mode 100644 index 0000000..02e67a2 --- /dev/null +++ b/lib/CSAF/Writer.pm @@ -0,0 +1,112 @@ +package CSAF::Writer; + +use 5.010001; +use strict; +use warnings; + +use Carp; +use CSAF::Util qw(tracking_id_to_well_filename); +use Digest::SHA qw(sha256_hex sha512_hex); +use File::Basename qw(basename dirname); +use File::Path qw(make_path); +use File::Spec::Functions qw(catfile); +use Tie::File; + +use Moo; +extends 'CSAF::Base'; + +use constant DEBUG => $ENV{CSAF_DEBUG}; + +use constant TRUE => !!1; +use constant FALSE => !!0; + + +has filename => (is => 'ro', default => sub { tracking_id_to_well_filename($_[0]->csaf->document->tracking->id) }); +has directory => (is => 'rw', isa => sub { Carp::croak qq{Output directory not found} unless -d $_[0] }); + +has update_index => (is => 'rw', default => FALSE); +has update_changes => (is => 'rw', default => FALSE); +has index_file => (is => 'rw', default => TRUE); +has changes_file => (is => 'rw', default => TRUE); +has sha256_checksum => (is => 'rw', default => TRUE); +has sha512_checksum => (is => 'rw', default => TRUE); + +sub write { + + my ($self) = @_; + + # 7 Distributing CSAF documents + + # 7.1.11 Requirement 11: One folder per year + # 7.1.12 Requirement 12: index.txt + # 7.1.13 Requirement 13: changes.csv + # 7.1.18 Requirement 18: Integrity + + # 7.1.19 Requirement 19: Signatures (TODO) + + my $csaf_json = $self->csaf->renderer->render; + my $csaf_directory = $self->directory; + my $csaf_filename = $self->filename; + my $csaf_file_basename = basename($csaf_filename, '.json'); + + my $csaf_file_year = $self->csaf->document->tracking->initial_release_date->year; + my $csaf_current_release_date = $self->csaf->document->tracking->current_release_date->datetime; + + my $json_file_path = catfile($csaf_directory, $csaf_file_year, $csaf_filename); + my $index_file_path = catfile($csaf_directory, 'index.txt'); + my $changes_file_path = catfile($csaf_directory, 'changes.csv'); + my $csaf_document_path = catfile($csaf_file_year, $csaf_filename); + my $sha256_file_path = catfile($csaf_directory, $csaf_file_year, $csaf_file_basename) . '.sha256'; + my $sha512_file_path = catfile($csaf_directory, $csaf_file_year, $csaf_file_basename) . '.sha512'; + + if (DEBUG) { + say STDERR "(I) Destination directory: $csaf_directory"; + say STDERR "(I) CSAF document: $csaf_filename"; + say STDERR "(I) CSAF document path: $json_file_path"; + say STDERR "(I) Index path: $index_file_path"; + say STDERR "(I) Changes path: $changes_file_path"; + } + + make_path(dirname($json_file_path)); + + open my $fh, '>', $json_file_path or Carp::croak "Can't open file: $!"; + $fh->autoflush(1); + + print $fh $csaf_json; + close $fh; + + if ($self->sha256_checksum) { + open my $fh, '>', $sha256_file_path or Carp::croak "Can't open file: $!"; + print $fh join ' ', sha256_hex($csaf_json), basename($csaf_document_path) . "\n"; + close $fh; + } + + if ($self->sha512_checksum) { + open my $fh, '>', $sha512_file_path or Carp::croak "Can't open file: $!"; + print $fh join ' ', sha512_hex($csaf_json), basename($csaf_document_path) . "\n"; + close $fh; + } + + if ($self->update_index) { + tie my @index_data, 'Tie::File', $index_file_path or Carp::croak "Unable to write $index_file_path"; + push @index_data, $csaf_document_path unless grep /^$csaf_document_path$/, @index_data; + + @index_data = ((), sort { $b cmp $a } @index_data); + } + + if ($self->update_changes) { + + my $changes_row = join ',', qq{"$csaf_document_path"}, qq{"$csaf_current_release_date"}; + + tie my @changes_data, 'Tie::File', $changes_file_path or Carp::croak "Unable to write $changes_file_path"; + push @changes_data, $changes_row unless grep /^$changes_row$/, @changes_data; + + @changes_data = ((), sort { (split(/\,/, $b))[1] cmp(split(/\,/, $a))[1] } @changes_data); + + } + + return 1; + +} + +1; diff --git a/lib/CSAF/resources/cache/6616ab5b335e47cd27e701999bd8eb3a b/lib/CSAF/resources/cache/6616ab5b335e47cd27e701999bd8eb3a new file mode 100644 index 0000000..0c2b2dd --- /dev/null +++ b/lib/CSAF/resources/cache/6616ab5b335e47cd27e701999bd8eb3a @@ -0,0 +1,143 @@ +{ + "license": [ + "Copyright (c) 2021, FIRST.ORG, INC.", + "All rights reserved.", + "", + "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the ", + "following conditions are met:", + "1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following ", + " disclaimer.", + "2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the ", + " following disclaimer in the documentation and/or other materials provided with the distribution.", + "3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote ", + " products derived from this software without specific prior written permission.", + "", + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, ", + "INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ", + "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ", + "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ", + "SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ", + "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ", + "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + ], + + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "JSON Schema for Common Vulnerability Scoring System version 3.1", + "$id": "https://www.first.org/cvss/cvss-v3.1.json?20211103", + "type": "object", + "definitions": { + "attackVectorType": { + "type": "string", + "enum": [ "NETWORK", "ADJACENT_NETWORK", "LOCAL", "PHYSICAL" ] + }, + "modifiedAttackVectorType": { + "type": "string", + "enum": [ "NETWORK", "ADJACENT_NETWORK", "LOCAL", "PHYSICAL", "NOT_DEFINED" ] + }, + "attackComplexityType": { + "type": "string", + "enum": [ "HIGH", "LOW" ] + }, + "modifiedAttackComplexityType": { + "type": "string", + "enum": [ "HIGH", "LOW", "NOT_DEFINED" ] + }, + "privilegesRequiredType": { + "type": "string", + "enum": [ "HIGH", "LOW", "NONE" ] + }, + "modifiedPrivilegesRequiredType": { + "type": "string", + "enum": [ "HIGH", "LOW", "NONE", "NOT_DEFINED" ] + }, + "userInteractionType": { + "type": "string", + "enum": [ "NONE", "REQUIRED" ] + }, + "modifiedUserInteractionType": { + "type": "string", + "enum": [ "NONE", "REQUIRED", "NOT_DEFINED" ] + }, + "scopeType": { + "type": "string", + "enum": [ "UNCHANGED", "CHANGED" ] + }, + "modifiedScopeType": { + "type": "string", + "enum": [ "UNCHANGED", "CHANGED", "NOT_DEFINED" ] + }, + "ciaType": { + "type": "string", + "enum": [ "NONE", "LOW", "HIGH" ] + }, + "modifiedCiaType": { + "type": "string", + "enum": [ "NONE", "LOW", "HIGH", "NOT_DEFINED" ] + }, + "exploitCodeMaturityType": { + "type": "string", + "enum": [ "UNPROVEN", "PROOF_OF_CONCEPT", "FUNCTIONAL", "HIGH", "NOT_DEFINED" ] + }, + "remediationLevelType": { + "type": "string", + "enum": [ "OFFICIAL_FIX", "TEMPORARY_FIX", "WORKAROUND", "UNAVAILABLE", "NOT_DEFINED" ] + }, + "confidenceType": { + "type": "string", + "enum": [ "UNKNOWN", "REASONABLE", "CONFIRMED", "NOT_DEFINED" ] + }, + "ciaRequirementType": { + "type": "string", + "enum": [ "LOW", "MEDIUM", "HIGH", "NOT_DEFINED" ] + }, + "scoreType": { + "type": "number", + "minimum": 0, + "maximum": 10 + }, + "severityType": { + "type": "string", + "enum": [ "NONE", "LOW", "MEDIUM", "HIGH", "CRITICAL" ] + } + }, + "properties": { + "version": { + "description": "CVSS Version", + "type": "string", + "enum": [ "3.1" ] + }, + "vectorString": { + "type": "string", + "pattern": "^CVSS:3[.]1/((AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])/)*(AV:[NALP]|AC:[LH]|PR:[NLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])$" + }, + "attackVector": { "$ref": "#/definitions/attackVectorType" }, + "attackComplexity": { "$ref": "#/definitions/attackComplexityType" }, + "privilegesRequired": { "$ref": "#/definitions/privilegesRequiredType" }, + "userInteraction": { "$ref": "#/definitions/userInteractionType" }, + "scope": { "$ref": "#/definitions/scopeType" }, + "confidentialityImpact": { "$ref": "#/definitions/ciaType" }, + "integrityImpact": { "$ref": "#/definitions/ciaType" }, + "availabilityImpact": { "$ref": "#/definitions/ciaType" }, + "baseScore": { "$ref": "#/definitions/scoreType" }, + "baseSeverity": { "$ref": "#/definitions/severityType" }, + "exploitCodeMaturity": { "$ref": "#/definitions/exploitCodeMaturityType" }, + "remediationLevel": { "$ref": "#/definitions/remediationLevelType" }, + "reportConfidence": { "$ref": "#/definitions/confidenceType" }, + "temporalScore": { "$ref": "#/definitions/scoreType" }, + "temporalSeverity": { "$ref": "#/definitions/severityType" }, + "confidentialityRequirement": { "$ref": "#/definitions/ciaRequirementType" }, + "integrityRequirement": { "$ref": "#/definitions/ciaRequirementType" }, + "availabilityRequirement": { "$ref": "#/definitions/ciaRequirementType" }, + "modifiedAttackVector": { "$ref": "#/definitions/modifiedAttackVectorType" }, + "modifiedAttackComplexity": { "$ref": "#/definitions/modifiedAttackComplexityType" }, + "modifiedPrivilegesRequired": { "$ref": "#/definitions/modifiedPrivilegesRequiredType" }, + "modifiedUserInteraction": { "$ref": "#/definitions/modifiedUserInteractionType" }, + "modifiedScope": { "$ref": "#/definitions/modifiedScopeType" }, + "modifiedConfidentialityImpact": { "$ref": "#/definitions/modifiedCiaType" }, + "modifiedIntegrityImpact": { "$ref": "#/definitions/modifiedCiaType" }, + "modifiedAvailabilityImpact": { "$ref": "#/definitions/modifiedCiaType" }, + "environmentalScore": { "$ref": "#/definitions/scoreType" }, + "environmentalSeverity": { "$ref": "#/definitions/severityType" } + }, + "required": [ "version", "vectorString", "baseScore", "baseSeverity" ] +} diff --git a/lib/CSAF/resources/cache/803157887dabfefc7425634a877ed27a b/lib/CSAF/resources/cache/803157887dabfefc7425634a877ed27a new file mode 100644 index 0000000..28b3c38 --- /dev/null +++ b/lib/CSAF/resources/cache/803157887dabfefc7425634a877ed27a @@ -0,0 +1,143 @@ +{ + "license": [ + "Copyright (c) 2017, FIRST.ORG, INC.", + "All rights reserved.", + "", + "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the ", + "following conditions are met:", + "1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following ", + " disclaimer.", + "2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the ", + " following disclaimer in the documentation and/or other materials provided with the distribution.", + "3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote ", + " products derived from this software without specific prior written permission.", + "", + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, ", + "INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ", + "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ", + "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ", + "SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ", + "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ", + "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + ], + + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "JSON Schema for Common Vulnerability Scoring System version 3.0", + "id": "https://www.first.org/cvss/cvss-v3.0.json?20170531", + "type": "object", + "definitions": { + "attackVectorType": { + "type": "string", + "enum": [ "NETWORK", "ADJACENT_NETWORK", "LOCAL", "PHYSICAL" ] + }, + "modifiedAttackVectorType": { + "type": "string", + "enum": [ "NETWORK", "ADJACENT_NETWORK", "LOCAL", "PHYSICAL", "NOT_DEFINED" ] + }, + "attackComplexityType": { + "type": "string", + "enum": [ "HIGH", "LOW" ] + }, + "modifiedAttackComplexityType": { + "type": "string", + "enum": [ "HIGH", "LOW", "NOT_DEFINED" ] + }, + "privilegesRequiredType": { + "type": "string", + "enum": [ "HIGH", "LOW", "NONE" ] + }, + "modifiedPrivilegesRequiredType": { + "type": "string", + "enum": [ "HIGH", "LOW", "NONE", "NOT_DEFINED" ] + }, + "userInteractionType": { + "type": "string", + "enum": [ "NONE", "REQUIRED" ] + }, + "modifiedUserInteractionType": { + "type": "string", + "enum": [ "NONE", "REQUIRED", "NOT_DEFINED" ] + }, + "scopeType": { + "type": "string", + "enum": [ "UNCHANGED", "CHANGED" ] + }, + "modifiedScopeType": { + "type": "string", + "enum": [ "UNCHANGED", "CHANGED", "NOT_DEFINED" ] + }, + "ciaType": { + "type": "string", + "enum": [ "NONE", "LOW", "HIGH" ] + }, + "modifiedCiaType": { + "type": "string", + "enum": [ "NONE", "LOW", "HIGH", "NOT_DEFINED" ] + }, + "exploitCodeMaturityType": { + "type": "string", + "enum": [ "UNPROVEN", "PROOF_OF_CONCEPT", "FUNCTIONAL", "HIGH", "NOT_DEFINED" ] + }, + "remediationLevelType": { + "type": "string", + "enum": [ "OFFICIAL_FIX", "TEMPORARY_FIX", "WORKAROUND", "UNAVAILABLE", "NOT_DEFINED" ] + }, + "confidenceType": { + "type": "string", + "enum": [ "UNKNOWN", "REASONABLE", "CONFIRMED", "NOT_DEFINED" ] + }, + "ciaRequirementType": { + "type": "string", + "enum": [ "LOW", "MEDIUM", "HIGH", "NOT_DEFINED" ] + }, + "scoreType": { + "type": "number", + "minimum": 0, + "maximum": 10 + }, + "severityType": { + "type": "string", + "enum": [ "NONE", "LOW", "MEDIUM", "HIGH", "CRITICAL" ] + } + }, + "properties": { + "version": { + "description": "CVSS Version", + "type": "string", + "enum": [ "3.0" ] + }, + "vectorString": { + "type": "string", + "pattern": "^CVSS:3[.]0/((AV:[NALP]|AC:[LH]|PR:[UNLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XUNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])/)*(AV:[NALP]|AC:[LH]|PR:[UNLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XUNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])$" + }, + "attackVector": { "$ref": "#/definitions/attackVectorType" }, + "attackComplexity": { "$ref": "#/definitions/attackComplexityType" }, + "privilegesRequired": { "$ref": "#/definitions/privilegesRequiredType" }, + "userInteraction": { "$ref": "#/definitions/userInteractionType" }, + "scope": { "$ref": "#/definitions/scopeType" }, + "confidentialityImpact": { "$ref": "#/definitions/ciaType" }, + "integrityImpact": { "$ref": "#/definitions/ciaType" }, + "availabilityImpact": { "$ref": "#/definitions/ciaType" }, + "baseScore": { "$ref": "#/definitions/scoreType" }, + "baseSeverity": { "$ref": "#/definitions/severityType" }, + "exploitCodeMaturity": { "$ref": "#/definitions/exploitCodeMaturityType" }, + "remediationLevel": { "$ref": "#/definitions/remediationLevelType" }, + "reportConfidence": { "$ref": "#/definitions/confidenceType" }, + "temporalScore": { "$ref": "#/definitions/scoreType" }, + "temporalSeverity": { "$ref": "#/definitions/severityType" }, + "confidentialityRequirement": { "$ref": "#/definitions/ciaRequirementType" }, + "integrityRequirement": { "$ref": "#/definitions/ciaRequirementType" }, + "availabilityRequirement": { "$ref": "#/definitions/ciaRequirementType" }, + "modifiedAttackVector": { "$ref": "#/definitions/modifiedAttackVectorType" }, + "modifiedAttackComplexity": { "$ref": "#/definitions/modifiedAttackComplexityType" }, + "modifiedPrivilegesRequired": { "$ref": "#/definitions/modifiedPrivilegesRequiredType" }, + "modifiedUserInteraction": { "$ref": "#/definitions/modifiedUserInteractionType" }, + "modifiedScope": { "$ref": "#/definitions/modifiedScopeType" }, + "modifiedConfidentialityImpact": { "$ref": "#/definitions/modifiedCiaType" }, + "modifiedIntegrityImpact": { "$ref": "#/definitions/modifiedCiaType" }, + "modifiedAvailabilityImpact": { "$ref": "#/definitions/modifiedCiaType" }, + "environmentalScore": { "$ref": "#/definitions/scoreType" }, + "environmentalSeverity": { "$ref": "#/definitions/severityType" } + }, + "required": [ "version", "vectorString", "baseScore", "baseSeverity" ] +} diff --git a/lib/CSAF/resources/cache/ac1b12ae2d190a0c468b534b8a1b9bd2 b/lib/CSAF/resources/cache/ac1b12ae2d190a0c468b534b8a1b9bd2 new file mode 100644 index 0000000..93ff152 --- /dev/null +++ b/lib/CSAF/resources/cache/ac1b12ae2d190a0c468b534b8a1b9bd2 @@ -0,0 +1,1414 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://docs.oasis-open.org/csaf/csaf/v2.0/csaf_json_schema.json", + "title": "Common Security Advisory Framework", + "description": "Representation of security advisory information as a JSON document.", + "type": "object", + "$defs": { + "acknowledgments_t": { + "title": "List of acknowledgments", + "description": "Contains a list of acknowledgment elements.", + "type": "array", + "minItems": 1, + "items": { + "title": "Acknowledgment", + "description": "Acknowledges contributions by describing those that contributed.", + "type": "object", + "minProperties": 1, + "properties": { + "names": { + "title": "List of acknowledged names", + "description": "Contains the names of contributors being recognized.", + "type": "array", + "minItems": 1, + "items": { + "title": "Name of the contributor", + "description": "Contains the name of a single contributor being recognized.", + "type": "string", + "minLength": 1, + "examples": [ + "Albert Einstein", + "Johann Sebastian Bach" + ] + } + }, + "organization": { + "title": "Contributing organization", + "description": "Contains the name of a contributing organization being recognized.", + "type": "string", + "minLength": 1, + "examples": [ + "CISA", + "Google Project Zero", + "Talos" + ] + }, + "summary": { + "title": "Summary of the acknowledgment", + "description": "SHOULD represent any contextual details the document producers wish to make known about the acknowledgment or acknowledged parties.", + "type": "string", + "minLength": 1, + "examples": [ + "First analysis of Coordinated Multi-Stream Attack (CMSA)" + ] + }, + "urls": { + "title": "List of URLs", + "description": "Specifies a list of URLs or location of the reference to be acknowledged.", + "type": "array", + "minItems": 1, + "items": { + "title": "URL of acknowledgment", + "description": "Contains the URL or location of the reference to be acknowledged.", + "type": "string", + "format": "uri" + } + } + } + } + }, + "branches_t": { + "title": "List of branches", + "description": "Contains branch elements as children of the current element.", + "type": "array", + "minItems": 1, + "items": { + "title": "Branch", + "description": "Is a part of the hierarchical structure of the product tree.", + "type": "object", + "maxProperties": 3, + "minProperties": 3, + "required": [ + "category", + "name" + ], + "properties": { + "branches": { + "$ref": "#/$defs/branches_t" + }, + "category": { + "title": "Category of the branch", + "description": "Describes the characteristics of the labeled branch.", + "type": "string", + "enum": [ + "architecture", + "host_name", + "language", + "legacy", + "patch_level", + "product_family", + "product_name", + "product_version", + "product_version_range", + "service_pack", + "specification", + "vendor" + ] + }, + "name": { + "title": "Name of the branch", + "description": "Contains the canonical descriptor or 'friendly name' of the branch.", + "type": "string", + "minLength": 1, + "examples": [ + "10", + "365", + "Microsoft", + "Office", + "PCS 7", + "SIMATIC", + "Siemens", + "Windows" + ] + }, + "product": { + "$ref": "#/$defs/full_product_name_t" + } + } + } + }, + "full_product_name_t": { + "title": "Full product name", + "description": "Specifies information about the product and assigns the product_id.", + "type": "object", + "required": [ + "name", + "product_id" + ], + "properties": { + "name": { + "title": "Textual description of the product", + "description": "The value should be the product’s full canonical name, including version number and other attributes, as it would be used in a human-friendly document.", + "type": "string", + "minLength": 1, + "examples": [ + "Cisco AnyConnect Secure Mobility Client 2.3.185", + "Microsoft Host Integration Server 2006 Service Pack 1" + ] + }, + "product_id": { + "$ref": "#/$defs/product_id_t" + }, + "product_identification_helper": { + "title": "Helper to identify the product", + "description": "Provides at least one method which aids in identifying the product in an asset database.", + "type": "object", + "minProperties": 1, + "properties": { + "cpe": { + "title": "Common Platform Enumeration representation", + "description": "The Common Platform Enumeration (CPE) attribute refers to a method for naming platforms external to this specification.", + "type": "string", + "pattern": "^(cpe:2\\.3:[aho\\*\\-](:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#\\$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|\\}~]))+(\\?*|\\*?))|[\\*\\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\\*\\-]))(:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#\\$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|\\}~]))+(\\?*|\\*?))|[\\*\\-])){4})|([c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9\\._\\-~%]*){0,6})$", + "minLength": 5 + }, + "hashes": { + "title": "List of hashes", + "description": "Contains a list of cryptographic hashes usable to identify files.", + "type": "array", + "minItems": 1, + "items": { + "title": "Cryptographic hashes", + "description": "Contains all information to identify a file based on its cryptographic hash values.", + "type": "object", + "required": [ + "file_hashes", + "filename" + ], + "properties": { + "file_hashes": { + "title": "List of file hashes", + "description": "Contains a list of cryptographic hashes for this file.", + "type": "array", + "minItems": 1, + "items": { + "title": "File hash", + "description": "Contains one hash value and algorithm of the file to be identified.", + "type": "object", + "required": [ + "algorithm", + "value" + ], + "properties": { + "algorithm": { + "title": "Algorithm of the cryptographic hash", + "description": "Contains the name of the cryptographic hash algorithm used to calculate the value.", + "type": "string", + "default": "sha256", + "minLength": 1, + "examples": [ + "blake2b512", + "sha256", + "sha3-512", + "sha384", + "sha512" + ] + }, + "value": { + "title": "Value of the cryptographic hash", + "description": "Contains the cryptographic hash value in hexadecimal representation.", + "type": "string", + "pattern": "^[0-9a-fA-F]{32,}$", + "minLength": 32, + "examples": [ + "37df33cb7464da5c7f077f4d56a32bc84987ec1d85b234537c1c1a4d4fc8d09dc29e2e762cb5203677bf849a2855a0283710f1f5fe1d6ce8d5ac85c645d0fcb3", + "4775203615d9534a8bfca96a93dc8b461a489f69124a130d786b42204f3341cc", + "9ea4c8200113d49d26505da0e02e2f49055dc078d1ad7a419b32e291c7afebbb84badfbd46dec42883bea0b2a1fa697c" + ] + } + } + } + }, + "filename": { + "title": "Filename", + "description": "Contains the name of the file which is identified by the hash values.", + "type": "string", + "minLength": 1, + "examples": [ + "WINWORD.EXE", + "msotadddin.dll", + "sudoers.so" + ] + } + } + } + }, + "model_numbers": { + "title": "List of models", + "description": "Contains a list of full or abbreviated (partial) model numbers.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "title": "Model number", + "description": "Contains a full or abbreviated (partial) model number of the component to identify.", + "type": "string", + "minLength": 1 + } + }, + "purl": { + "title": "package URL representation", + "description": "The package URL (purl) attribute refers to a method for reliably identifying and locating software packages external to this specification.", + "type": "string", + "format": "uri", + "pattern": "^pkg:[A-Za-z\\.\\-\\+][A-Za-z0-9\\.\\-\\+]*/.+", + "minLength": 7 + }, + "sbom_urls": { + "title": "List of SBOM URLs", + "description": "Contains a list of URLs where SBOMs for this product can be retrieved.", + "type": "array", + "minItems": 1, + "items": { + "title": "SBOM URL", + "description": "Contains a URL of one SBOM for this product.", + "type": "string", + "format": "uri" + } + }, + "serial_numbers": { + "title": "List of serial numbers", + "description": "Contains a list of full or abbreviated (partial) serial numbers.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "title": "Serial number", + "description": "Contains a full or abbreviated (partial) serial number of the component to identify.", + "type": "string", + "minLength": 1 + } + }, + "skus": { + "title": "List of stock keeping units", + "description": "Contains a list of full or abbreviated (partial) stock keeping units.", + "type": "array", + "minItems": 1, + "items": { + "title": "Stock keeping unit", + "description": "Contains a full or abbreviated (partial) stock keeping unit (SKU) which is used in the ordering process to identify the component.", + "type": "string", + "minLength": 1 + } + }, + "x_generic_uris": { + "title": "List of generic URIs", + "description": "Contains a list of identifiers which are either vendor-specific or derived from a standard not yet supported.", + "type": "array", + "minItems": 1, + "items": { + "title": "Generic URI", + "description": "Provides a generic extension point for any identifier which is either vendor-specific or derived from a standard not yet supported.", + "type": "object", + "required": [ + "namespace", + "uri" + ], + "properties": { + "namespace": { + "title": "Namespace of the generic URI", + "description": "Refers to a URL which provides the name and knowledge about the specification used or is the namespace in which these values are valid.", + "type": "string", + "format": "uri" + }, + "uri": { + "title": "URI", + "description": "Contains the identifier itself.", + "type": "string", + "format": "uri" + } + } + } + } + } + } + } + }, + "lang_t": { + "title": "Language type", + "description": "Identifies a language, corresponding to IETF BCP 47 / RFC 5646. See IETF language registry: https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry", + "type": "string", + "pattern": "^(([A-Za-z]{2,3}(-[A-Za-z]{3}(-[A-Za-z]{3}){0,2})?|[A-Za-z]{4,8})(-[A-Za-z]{4})?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-[A-WY-Za-wy-z0-9](-[A-Za-z0-9]{2,8})+)*(-[Xx](-[A-Za-z0-9]{1,8})+)?|[Xx](-[A-Za-z0-9]{1,8})+|[Ii]-[Dd][Ee][Ff][Aa][Uu][Ll][Tt]|[Ii]-[Mm][Ii][Nn][Gg][Oo])$", + "examples": [ + "de", + "en", + "fr", + "frc", + "jp" + ] + }, + "notes_t": { + "title": "List of notes", + "description": "Contains notes which are specific to the current context.", + "type": "array", + "minItems": 1, + "items": { + "title": "Note", + "description": "Is a place to put all manner of text blobs related to the current context.", + "type": "object", + "required": [ + "category", + "text" + ], + "properties": { + "audience": { + "title": "Audience of note", + "description": "Indicates who is intended to read it.", + "type": "string", + "minLength": 1, + "examples": [ + "all", + "executives", + "operational management and system administrators", + "safety engineers" + ] + }, + "category": { + "title": "Note category", + "description": "Contains the information of what kind of note this is.", + "type": "string", + "enum": [ + "description", + "details", + "faq", + "general", + "legal_disclaimer", + "other", + "summary" + ] + }, + "text": { + "title": "Note content", + "description": "Holds the content of the note. Content varies depending on type.", + "type": "string", + "minLength": 1 + }, + "title": { + "title": "Title of note", + "description": "Provides a concise description of what is contained in the text of the note.", + "type": "string", + "minLength": 1, + "examples": [ + "Details", + "Executive summary", + "Technical summary", + "Impact on safety systems" + ] + } + } + } + }, + "product_group_id_t": { + "title": "Reference token for product group instance", + "description": "Token required to identify a group of products so that it can be referred to from other parts in the document. There is no predefined or required format for the product_group_id as long as it uniquely identifies a group in the context of the current document.", + "type": "string", + "minLength": 1, + "examples": [ + "CSAFGID-0001", + "CSAFGID-0002", + "CSAFGID-0020" + ] + }, + "product_groups_t": { + "title": "List of product_group_ids", + "description": "Specifies a list of product_group_ids to give context to the parent item.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/$defs/product_group_id_t" + } + }, + "product_id_t": { + "title": "Reference token for product instance", + "description": "Token required to identify a full_product_name so that it can be referred to from other parts in the document. There is no predefined or required format for the product_id as long as it uniquely identifies a product in the context of the current document.", + "type": "string", + "minLength": 1, + "examples": [ + "CSAFPID-0004", + "CSAFPID-0008" + ] + }, + "products_t": { + "title": "List of product_ids", + "description": "Specifies a list of product_ids to give context to the parent item.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/$defs/product_id_t" + } + }, + "references_t": { + "title": "List of references", + "description": "Holds a list of references.", + "type": "array", + "minItems": 1, + "items": { + "title": "Reference", + "description": "Holds any reference to conferences, papers, advisories, and other resources that are related and considered related to either a surrounding part of or the entire document and to be of value to the document consumer.", + "type": "object", + "required": [ + "summary", + "url" + ], + "properties": { + "category": { + "title": "Category of reference", + "description": "Indicates whether the reference points to the same document or vulnerability in focus (depending on scope) or to an external resource.", + "type": "string", + "default": "external", + "enum": [ + "external", + "self" + ] + }, + "summary": { + "title": "Summary of the reference", + "description": "Indicates what this reference refers to.", + "type": "string", + "minLength": 1 + }, + "url": { + "title": "URL of reference", + "description": "Provides the URL for the reference.", + "type": "string", + "format": "uri" + } + } + } + }, + "version_t": { + "title": "Version", + "description": "Specifies a version string to denote clearly the evolution of the content of the document. Format must be either integer or semantic versioning.", + "type": "string", + "pattern": "^(0|[1-9][0-9]*)$|^((0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)$", + "examples": [ + "1", + "4", + "0.9.0", + "1.4.3", + "2.40.0+21AF26D3" + ] + } + }, + "required": [ + "document" + ], + "properties": { + "document": { + "title": "Document level meta-data", + "description": "Captures the meta-data about this document describing a particular set of security advisories.", + "type": "object", + "required": [ + "category", + "csaf_version", + "publisher", + "title", + "tracking" + ], + "properties": { + "acknowledgments": { + "title": "Document acknowledgments", + "description": "Contains a list of acknowledgment elements associated with the whole document.", + "$ref": "#/$defs/acknowledgments_t" + }, + "aggregate_severity": { + "title": "Aggregate severity", + "description": "Is a vehicle that is provided by the document producer to convey the urgency and criticality with which the one or more vulnerabilities reported should be addressed. It is a document-level metric and applied to the document as a whole — not any specific vulnerability. The range of values in this field is defined according to the document producer's policies and procedures.", + "type": "object", + "required": [ + "text" + ], + "properties": { + "namespace": { + "title": "Namespace of aggregate severity", + "description": "Points to the namespace so referenced.", + "type": "string", + "format": "uri" + }, + "text": { + "title": "Text of aggregate severity", + "description": "Provides a severity which is independent of - and in addition to - any other standard metric for determining the impact or severity of a given vulnerability (such as CVSS).", + "type": "string", + "minLength": 1, + "examples": [ + "Critical", + "Important", + "Moderate" + ] + } + } + }, + "category": { + "title": "Document category", + "description": "Defines a short canonical name, chosen by the document producer, which will inform the end user as to the category of document.", + "type": "string", + "pattern": "^[^\\s\\-_\\.](.*[^\\s\\-_\\.])?$", + "minLength": 1, + "examples": [ + "csaf_base", + "csaf_security_advisory", + "csaf_vex", + "Example Company Security Notice" + ] + }, + "csaf_version": { + "title": "CSAF version", + "description": "Gives the version of the CSAF specification which the document was generated for.", + "type": "string", + "enum": [ + "2.0" + ] + }, + "distribution": { + "title": "Rules for sharing document", + "description": "Describe any constraints on how this document might be shared.", + "type": "object", + "minProperties": 1, + "properties": { + "text": { + "title": "Textual description", + "description": "Provides a textual description of additional constraints.", + "type": "string", + "minLength": 1, + "examples": [ + "Copyright 2021, Example Company, All Rights Reserved.", + "Distribute freely.", + "Share only on a need-to-know-basis only." + ] + }, + "tlp": { + "title": "Traffic Light Protocol (TLP)", + "description": "Provides details about the TLP classification of the document.", + "type": "object", + "required": [ + "label" + ], + "properties": { + "label": { + "title": "Label of TLP", + "description": "Provides the TLP label of the document.", + "type": "string", + "enum": [ + "AMBER", + "GREEN", + "RED", + "WHITE" + ] + }, + "url": { + "title": "URL of TLP version", + "description": "Provides a URL where to find the textual description of the TLP version which is used in this document. Default is the URL to the definition by FIRST.", + "type": "string", + "default": "https://www.first.org/tlp/", + "format": "uri", + "examples": [ + "https://www.us-cert.gov/tlp", + "https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Kritis/Merkblatt_TLP.pdf" + ] + } + } + } + } + }, + "lang": { + "title": "Document language", + "description": "Identifies the language used by this document, corresponding to IETF BCP 47 / RFC 5646.", + "$ref": "#/$defs/lang_t" + }, + "notes": { + "title": "Document notes", + "description": "Holds notes associated with the whole document.", + "$ref": "#/$defs/notes_t" + }, + "publisher": { + "title": "Publisher", + "description": "Provides information about the publisher of the document.", + "type": "object", + "required": [ + "category", + "name", + "namespace" + ], + "properties": { + "category": { + "title": "Category of publisher", + "description": "Provides information about the category of publisher releasing the document.", + "type": "string", + "enum": [ + "coordinator", + "discoverer", + "other", + "translator", + "user", + "vendor" + ] + }, + "contact_details": { + "title": "Contact details", + "description": "Information on how to contact the publisher, possibly including details such as web sites, email addresses, phone numbers, and postal mail addresses.", + "type": "string", + "minLength": 1, + "examples": [ + "Example Company can be reached at contact_us@example.com, or via our website at https://www.example.com/contact." + ] + }, + "issuing_authority": { + "title": "Issuing authority", + "description": "Provides information about the authority of the issuing party to release the document, in particular, the party's constituency and responsibilities or other obligations.", + "type": "string", + "minLength": 1 + }, + "name": { + "title": "Name of publisher", + "description": "Contains the name of the issuing party.", + "type": "string", + "minLength": 1, + "examples": [ + "BSI", + "Cisco PSIRT", + "Siemens ProductCERT" + ] + }, + "namespace": { + "title": "Namespace of publisher", + "description": "Contains a URL which is under control of the issuing party and can be used as a globally unique identifier for that issuing party.", + "type": "string", + "format": "uri", + "examples": [ + "https://csaf.io", + "https://www.example.com" + ] + } + } + }, + "references": { + "title": "Document references", + "description": "Holds a list of references associated with the whole document.", + "$ref": "#/$defs/references_t" + }, + "source_lang": { + "title": "Source language", + "description": "If this copy of the document is a translation then the value of this property describes from which language this document was translated.", + "$ref": "#/$defs/lang_t" + }, + "title": { + "title": "Title of this document", + "description": "This SHOULD be a canonical name for the document, and sufficiently unique to distinguish it from similar documents.", + "type": "string", + "minLength": 1, + "examples": [ + "Cisco IPv6 Crafted Packet Denial of Service Vulnerability", + "Example Company Cross-Site-Scripting Vulnerability in Example Generator" + ] + }, + "tracking": { + "title": "Tracking", + "description": "Is a container designated to hold all management attributes necessary to track a CSAF document as a whole.", + "type": "object", + "required": [ + "current_release_date", + "id", + "initial_release_date", + "revision_history", + "status", + "version" + ], + "properties": { + "aliases": { + "title": "Aliases", + "description": "Contains a list of alternate names for the same document.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "title": "Alternate name", + "description": "Specifies a non-empty string that represents a distinct optional alternative ID used to refer to the document.", + "type": "string", + "minLength": 1, + "examples": [ + "CVE-2019-12345" + ] + } + }, + "current_release_date": { + "title": "Current release date", + "description": "The date when the current revision of this document was released", + "type": "string", + "format": "date-time" + }, + "generator": { + "title": "Document generator", + "description": "Is a container to hold all elements related to the generation of the document. These items will reference when the document was actually created, including the date it was generated and the entity that generated it.", + "type": "object", + "required": [ + "engine" + ], + "properties": { + "date": { + "title": "Date of document generation", + "description": "This SHOULD be the current date that the document was generated. Because documents are often generated internally by a document producer and exist for a nonzero amount of time before being released, this field MAY be different from the Initial Release Date and Current Release Date.", + "type": "string", + "format": "date-time" + }, + "engine": { + "title": "Engine of document generation", + "description": "Contains information about the engine that generated the CSAF document.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "title": "Engine name", + "description": "Represents the name of the engine that generated the CSAF document.", + "type": "string", + "minLength": 1, + "examples": [ + "Red Hat rhsa-to-cvrf", + "Secvisogram", + "TVCE" + ] + }, + "version": { + "title": "Engine version", + "description": "Contains the version of the engine that generated the CSAF document.", + "type": "string", + "minLength": 1, + "examples": [ + "0.6.0", + "1.0.0-beta+exp.sha.a1c44f85", + "2" + ] + } + } + } + } + }, + "id": { + "title": "Unique identifier for the document", + "description": "The ID is a simple label that provides for a wide range of numbering values, types, and schemes. Its value SHOULD be assigned and maintained by the original document issuing authority.", + "type": "string", + "pattern": "^[\\S](.*[\\S])?$", + "minLength": 1, + "examples": [ + "Example Company - 2019-YH3234", + "RHBA-2019:0024", + "cisco-sa-20190513-secureboot" + ] + }, + "initial_release_date": { + "title": "Initial release date", + "description": "The date when this document was first published.", + "type": "string", + "format": "date-time" + }, + "revision_history": { + "title": "Revision history", + "description": "Holds one revision item for each version of the CSAF document, including the initial one.", + "type": "array", + "minItems": 1, + "items": { + "title": "Revision", + "description": "Contains all the information elements required to track the evolution of a CSAF document.", + "type": "object", + "required": [ + "date", + "number", + "summary" + ], + "properties": { + "date": { + "title": "Date of the revision", + "description": "The date of the revision entry", + "type": "string", + "format": "date-time" + }, + "legacy_version": { + "title": "Legacy version of the revision", + "description": "Contains the version string used in an existing document with the same content.", + "type": "string", + "minLength": 1 + }, + "number": { + "$ref": "#/$defs/version_t" + }, + "summary": { + "title": "Summary of the revision", + "description": "Holds a single non-empty string representing a short description of the changes.", + "type": "string", + "minLength": 1, + "examples": [ + "Initial version." + ] + } + } + } + }, + "status": { + "title": "Document status", + "description": "Defines the draft status of the document.", + "type": "string", + "enum": [ + "draft", + "final", + "interim" + ] + }, + "version": { + "$ref": "#/$defs/version_t" + } + } + } + } + }, + "product_tree": { + "title": "Product tree", + "description": "Is a container for all fully qualified product names that can be referenced elsewhere in the document.", + "type": "object", + "minProperties": 1, + "properties": { + "branches": { + "$ref": "#/$defs/branches_t" + }, + "full_product_names": { + "title": "List of full product names", + "description": "Contains a list of full product names.", + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/full_product_name_t" + } + }, + "product_groups": { + "title": "List of product groups", + "description": "Contains a list of product groups.", + "type": "array", + "minItems": 1, + "items": { + "title": "Product group", + "description": "Defines a new logical group of products that can then be referred to in other parts of the document to address a group of products with a single identifier.", + "type": "object", + "required": [ + "group_id", + "product_ids" + ], + "properties": { + "group_id": { + "$ref": "#/$defs/product_group_id_t" + }, + "product_ids": { + "title": "List of Product IDs", + "description": "Lists the product_ids of those products which known as one group in the document.", + "type": "array", + "minItems": 2, + "uniqueItems": true, + "items": { + "$ref": "#/$defs/product_id_t" + } + }, + "summary": { + "title": "Summary of the product group", + "description": "Gives a short, optional description of the group.", + "type": "string", + "minLength": 1, + "examples": [ + "Products supporting Modbus.", + "The x64 versions of the operating system." + ] + } + } + } + }, + "relationships": { + "title": "List of relationships", + "description": "Contains a list of relationships.", + "type": "array", + "minItems": 1, + "items": { + "title": "Relationship", + "description": "Establishes a link between two existing full_product_name_t elements, allowing the document producer to define a combination of two products that form a new full_product_name entry.", + "type": "object", + "required": [ + "category", + "full_product_name", + "product_reference", + "relates_to_product_reference" + ], + "properties": { + "category": { + "title": "Relationship category", + "description": "Defines the category of relationship for the referenced component.", + "type": "string", + "enum": [ + "default_component_of", + "external_component_of", + "installed_on", + "installed_with", + "optional_component_of" + ] + }, + "full_product_name": { + "$ref": "#/$defs/full_product_name_t" + }, + "product_reference": { + "title": "Product reference", + "description": "Holds a Product ID that refers to the Full Product Name element, which is referenced as the first element of the relationship.", + "$ref": "#/$defs/product_id_t" + }, + "relates_to_product_reference": { + "title": "Relates to product reference", + "description": "Holds a Product ID that refers to the Full Product Name element, which is referenced as the second element of the relationship.", + "$ref": "#/$defs/product_id_t" + } + } + } + } + } + }, + "vulnerabilities": { + "title": "Vulnerabilities", + "description": "Represents a list of all relevant vulnerability information items.", + "type": "array", + "minItems": 1, + "items": { + "title": "Vulnerability", + "description": "Is a container for the aggregation of all fields that are related to a single vulnerability in the document.", + "type": "object", + "minProperties": 1, + "properties": { + "acknowledgments": { + "title": "Vulnerability acknowledgments", + "description": "Contains a list of acknowledgment elements associated with this vulnerability item.", + "$ref": "#/$defs/acknowledgments_t" + }, + "cve": { + "title": "CVE", + "description": "Holds the MITRE standard Common Vulnerabilities and Exposures (CVE) tracking number for the vulnerability.", + "type": "string", + "pattern": "^CVE-[0-9]{4}-[0-9]{4,}$" + }, + "cwe": { + "title": "CWE", + "description": "Holds the MITRE standard Common Weakness Enumeration (CWE) for the weakness associated.", + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "title": "Weakness ID", + "description": "Holds the ID for the weakness associated.", + "type": "string", + "pattern": "^CWE-[1-9]\\d{0,5}$", + "examples": [ + "CWE-22", + "CWE-352", + "CWE-79" + ] + }, + "name": { + "title": "Weakness name", + "description": "Holds the full name of the weakness as given in the CWE specification.", + "type": "string", + "minLength": 1, + "examples": [ + "Cross-Site Request Forgery (CSRF)", + "Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')", + "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')" + ] + } + } + }, + "discovery_date": { + "title": "Discovery date", + "description": "Holds the date and time the vulnerability was originally discovered.", + "type": "string", + "format": "date-time" + }, + "flags": { + "title": "List of flags", + "description": "Contains a list of machine readable flags.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "title": "Flag", + "description": "Contains product specific information in regard to this vulnerability as a single machine readable flag.", + "type": "object", + "required": [ + "label" + ], + "properties": { + "date": { + "title": "Date of the flag", + "description": "Contains the date when assessment was done or the flag was assigned.", + "type": "string", + "format": "date-time" + }, + "group_ids": { + "$ref": "#/$defs/product_groups_t" + }, + "label": { + "title": "Label of the flag", + "description": "Specifies the machine readable label.", + "type": "string", + "enum": [ + "component_not_present", + "inline_mitigations_already_exist", + "vulnerable_code_cannot_be_controlled_by_adversary", + "vulnerable_code_not_in_execute_path", + "vulnerable_code_not_present" + ] + }, + "product_ids": { + "$ref": "#/$defs/products_t" + } + } + } + }, + "ids": { + "title": "List of IDs", + "description": "Represents a list of unique labels or tracking IDs for the vulnerability (if such information exists).", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "title": "ID", + "description": "Contains a single unique label or tracking ID for the vulnerability.", + "type": "object", + "required": [ + "system_name", + "text" + ], + "properties": { + "system_name": { + "title": "System name", + "description": "Indicates the name of the vulnerability tracking or numbering system.", + "type": "string", + "minLength": 1, + "examples": [ + "Cisco Bug ID", + "GitHub Issue" + ] + }, + "text": { + "title": "Text", + "description": "Is unique label or tracking ID for the vulnerability (if such information exists).", + "type": "string", + "minLength": 1, + "examples": [ + "CSCso66472", + "oasis-tcs/csaf#210" + ] + } + } + } + }, + "involvements": { + "title": "List of involvements", + "description": "Contains a list of involvements.", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "title": "Involvement", + "description": "Is a container, that allows the document producers to comment on the level of involvement (or engagement) of themselves or third parties in the vulnerability identification, scoping, and remediation process.", + "type": "object", + "required": [ + "party", + "status" + ], + "properties": { + "date": { + "title": "Date of involvement", + "description": "Holds the date and time of the involvement entry.", + "type": "string", + "format": "date-time" + }, + "party": { + "title": "Party category", + "description": "Defines the category of the involved party.", + "type": "string", + "enum": [ + "coordinator", + "discoverer", + "other", + "user", + "vendor" + ] + }, + "status": { + "title": "Party status", + "description": "Defines contact status of the involved party.", + "type": "string", + "enum": [ + "completed", + "contact_attempted", + "disputed", + "in_progress", + "not_contacted", + "open" + ] + }, + "summary": { + "title": "Summary of the involvement", + "description": "Contains additional context regarding what is going on.", + "type": "string", + "minLength": 1 + } + } + } + }, + "notes": { + "title": "Vulnerability notes", + "description": "Holds notes associated with this vulnerability item.", + "$ref": "#/$defs/notes_t" + }, + "product_status": { + "title": "Product status", + "description": "Contains different lists of product_ids which provide details on the status of the referenced product related to the current vulnerability. ", + "type": "object", + "minProperties": 1, + "properties": { + "first_affected": { + "title": "First affected", + "description": "These are the first versions of the releases known to be affected by the vulnerability.", + "$ref": "#/$defs/products_t" + }, + "first_fixed": { + "title": "First fixed", + "description": "These versions contain the first fix for the vulnerability but may not be the recommended fixed versions.", + "$ref": "#/$defs/products_t" + }, + "fixed": { + "title": "Fixed", + "description": "These versions contain a fix for the vulnerability but may not be the recommended fixed versions.", + "$ref": "#/$defs/products_t" + }, + "known_affected": { + "title": "Known affected", + "description": "These versions are known to be affected by the vulnerability.", + "$ref": "#/$defs/products_t" + }, + "known_not_affected": { + "title": "Known not affected", + "description": "These versions are known not to be affected by the vulnerability.", + "$ref": "#/$defs/products_t" + }, + "last_affected": { + "title": "Last affected", + "description": "These are the last versions in a release train known to be affected by the vulnerability. Subsequently released versions would contain a fix for the vulnerability.", + "$ref": "#/$defs/products_t" + }, + "recommended": { + "title": "Recommended", + "description": "These versions have a fix for the vulnerability and are the vendor-recommended versions for fixing the vulnerability.", + "$ref": "#/$defs/products_t" + }, + "under_investigation": { + "title": "Under investigation", + "description": "It is not known yet whether these versions are or are not affected by the vulnerability. However, it is still under investigation - the result will be provided in a later release of the document.", + "$ref": "#/$defs/products_t" + } + } + }, + "references": { + "title": "Vulnerability references", + "description": "Holds a list of references associated with this vulnerability item.", + "$ref": "#/$defs/references_t" + }, + "release_date": { + "title": "Release date", + "description": "Holds the date and time the vulnerability was originally released into the wild.", + "type": "string", + "format": "date-time" + }, + "remediations": { + "title": "List of remediations", + "description": "Contains a list of remediations.", + "type": "array", + "minItems": 1, + "items": { + "title": "Remediation", + "description": "Specifies details on how to handle (and presumably, fix) a vulnerability.", + "type": "object", + "required": [ + "category", + "details" + ], + "properties": { + "category": { + "title": "Category of the remediation", + "description": "Specifies the category which this remediation belongs to.", + "type": "string", + "enum": [ + "mitigation", + "no_fix_planned", + "none_available", + "vendor_fix", + "workaround" + ] + }, + "date": { + "title": "Date of the remediation", + "description": "Contains the date from which the remediation is available.", + "type": "string", + "format": "date-time" + }, + "details": { + "title": "Details of the remediation", + "description": "Contains a thorough human-readable discussion of the remediation.", + "type": "string", + "minLength": 1 + }, + "entitlements": { + "title": "List of entitlements", + "description": "Contains a list of entitlements.", + "type": "array", + "minItems": 1, + "items": { + "title": "Entitlement of the remediation", + "description": "Contains any possible vendor-defined constraints for obtaining fixed software or hardware that fully resolves the vulnerability.", + "type": "string", + "minLength": 1 + } + }, + "group_ids": { + "$ref": "#/$defs/product_groups_t" + }, + "product_ids": { + "$ref": "#/$defs/products_t" + }, + "restart_required": { + "title": "Restart required by remediation", + "description": "Provides information on category of restart is required by this remediation to become effective.", + "type": "object", + "required": [ + "category" + ], + "properties": { + "category": { + "title": "Category of restart", + "description": "Specifies what category of restart is required by this remediation to become effective.", + "type": "string", + "enum": [ + "connected", + "dependencies", + "machine", + "none", + "parent", + "service", + "system", + "vulnerable_component", + "zone" + ] + }, + "details": { + "title": "Additional restart information", + "description": "Provides additional information for the restart. This can include details on procedures, scope or impact.", + "type": "string", + "minLength": 1 + } + } + }, + "url": { + "title": "URL to the remediation", + "description": "Contains the URL where to obtain the remediation.", + "type": "string", + "format": "uri" + } + } + } + }, + "scores": { + "title": "List of scores", + "description": "Contains score objects for the current vulnerability.", + "type": "array", + "minItems": 1, + "items": { + "title": "Score", + "description": "Specifies information about (at least one) score of the vulnerability and for which products the given value applies.", + "type": "object", + "minProperties": 2, + "required": [ + "products" + ], + "properties": { + "cvss_v2": { + "$ref": "https://www.first.org/cvss/cvss-v2.0.json" + }, + "cvss_v3": { + "oneOf": [ + { + "$ref": "https://www.first.org/cvss/cvss-v3.0.json" + }, + { + "$ref": "https://www.first.org/cvss/cvss-v3.1.json" + } + ] + }, + "products": { + "$ref": "#/$defs/products_t" + } + } + } + }, + "threats": { + "title": "List of threats", + "description": "Contains information about a vulnerability that can change with time.", + "type": "array", + "minItems": 1, + "items": { + "title": "Threat", + "description": "Contains the vulnerability kinetic information. This information can change as the vulnerability ages and new information becomes available.", + "type": "object", + "required": [ + "category", + "details" + ], + "properties": { + "category": { + "title": "Category of the threat", + "description": "Categorizes the threat according to the rules of the specification.", + "type": "string", + "enum": [ + "exploit_status", + "impact", + "target_set" + ] + }, + "date": { + "title": "Date of the threat", + "description": "Contains the date when the assessment was done or the threat appeared.", + "type": "string", + "format": "date-time" + }, + "details": { + "title": "Details of the threat", + "description": "Represents a thorough human-readable discussion of the threat.", + "type": "string", + "minLength": 1 + }, + "group_ids": { + "$ref": "#/$defs/product_groups_t" + }, + "product_ids": { + "$ref": "#/$defs/products_t" + } + } + } + }, + "title": { + "title": "Title", + "description": "Gives the document producer the ability to apply a canonical name or title to the vulnerability.", + "type": "string", + "minLength": 1 + } + } + } + } + } +} diff --git a/lib/CSAF/resources/cache/efea8b3a7512905f5aa6affa56ea3a68 b/lib/CSAF/resources/cache/efea8b3a7512905f5aa6affa56ea3a68 new file mode 100644 index 0000000..efe9917 --- /dev/null +++ b/lib/CSAF/resources/cache/efea8b3a7512905f5aa6affa56ea3a68 @@ -0,0 +1,104 @@ +{ + "license": [ + "Copyright (c) 2017, FIRST.ORG, INC.", + "All rights reserved.", + "", + "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the ", + "following conditions are met:", + "1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following ", + " disclaimer.", + "2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the ", + " following disclaimer in the documentation and/or other materials provided with the distribution.", + "3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote ", + " products derived from this software without specific prior written permission.", + "", + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, ", + "INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ", + "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ", + "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ", + "SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ", + "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ", + "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + ], + + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "JSON Schema for Common Vulnerability Scoring System version 2.0", + "id": "https://www.first.org/cvss/cvss-v2.0.json?20170531", + "type": "object", + "definitions": { + "accessVectorType": { + "type": "string", + "enum": [ "NETWORK", "ADJACENT_NETWORK", "LOCAL" ] + }, + "accessComplexityType": { + "type": "string", + "enum": [ "HIGH", "MEDIUM", "LOW" ] + }, + "authenticationType": { + "type": "string", + "enum": [ "MULTIPLE", "SINGLE", "NONE" ] + }, + "ciaType": { + "type": "string", + "enum": [ "NONE", "PARTIAL", "COMPLETE" ] + }, + "exploitabilityType": { + "type": "string", + "enum": [ "UNPROVEN", "PROOF_OF_CONCEPT", "FUNCTIONAL", "HIGH", "NOT_DEFINED" ] + }, + "remediationLevelType": { + "type": "string", + "enum": [ "OFFICIAL_FIX", "TEMPORARY_FIX", "WORKAROUND", "UNAVAILABLE", "NOT_DEFINED" ] + }, + "reportConfidenceType": { + "type": "string", + "enum": [ "UNCONFIRMED", "UNCORROBORATED", "CONFIRMED", "NOT_DEFINED" ] + }, + "collateralDamagePotentialType": { + "type": "string", + "enum": [ "NONE", "LOW", "LOW_MEDIUM", "MEDIUM_HIGH", "HIGH", "NOT_DEFINED" ] + }, + "targetDistributionType": { + "type": "string", + "enum": [ "NONE", "LOW", "MEDIUM", "HIGH", "NOT_DEFINED" ] + }, + "ciaRequirementType": { + "type": "string", + "enum": [ "LOW", "MEDIUM", "HIGH", "NOT_DEFINED" ] + }, + "scoreType": { + "type": "number", + "minimum": 0, + "maximum": 10 + } + }, + "properties": { + "version": { + "description": "CVSS Version", + "type": "string", + "enum": [ "2.0" ] + }, + "vectorString": { + "type": "string", + "pattern": "^((AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))/)*(AV:[NAL]|AC:[LMH]|Au:[MSN]|[CIA]:[NPC]|E:(U|POC|F|H|ND)|RL:(OF|TF|W|U|ND)|RC:(UC|UR|C|ND)|CDP:(N|L|LM|MH|H|ND)|TD:(N|L|M|H|ND)|[CIA]R:(L|M|H|ND))$" + }, + "accessVector": { "$ref": "#/definitions/accessVectorType" }, + "accessComplexity": { "$ref": "#/definitions/accessComplexityType" }, + "authentication": { "$ref": "#/definitions/authenticationType" }, + "confidentialityImpact": { "$ref": "#/definitions/ciaType" }, + "integrityImpact": { "$ref": "#/definitions/ciaType" }, + "availabilityImpact": { "$ref": "#/definitions/ciaType" }, + "baseScore": { "$ref": "#/definitions/scoreType" }, + "exploitability": { "$ref": "#/definitions/exploitabilityType" }, + "remediationLevel": { "$ref": "#/definitions/remediationLevelType" }, + "reportConfidence": { "$ref": "#/definitions/reportConfidenceType" }, + "temporalScore": { "$ref": "#/definitions/scoreType" }, + "collateralDamagePotential": { "$ref": "#/definitions/collateralDamagePotentialType" }, + "targetDistribution": { "$ref": "#/definitions/targetDistributionType" }, + "confidentialityRequirement": { "$ref": "#/definitions/ciaRequirementType" }, + "integrityRequirement": { "$ref": "#/definitions/ciaRequirementType" }, + "availabilityRequirement": { "$ref": "#/definitions/ciaRequirementType" }, + "environmentalScore": { "$ref": "#/definitions/scoreType" } + }, + "required": [ "version", "vectorString", "baseScore" ] +} diff --git a/lib/CSAF/resources/template/default.tt2 b/lib/CSAF/resources/template/default.tt2 new file mode 100644 index 0000000..db4e49b --- /dev/null +++ b/lib/CSAF/resources/template/default.tt2 @@ -0,0 +1,489 @@ + +[% # CSAF Document Template based on secvisogram template + # https://github.com/secvisogram/secvisogram/blob/main/app/lib/app/SecvisogramPage/View/shared/HTMLTemplate/Template.html + # %] + +[% USE dumper %] + +[% BLOCK document_note %] + [% IF note.title %]

[% note.title %]

[% END %] + [% IF note.audience %][% note.audience %][% END %] + [% IF note.text %]

[% note.text | html_line_break %]

[% END %] +[% END %] + +[% BLOCK vulnerability_note %] + [% IF note.title %][% note.title %][% END %] [% IF note.audience %] ([% note.audience %])[% END %] + [% IF note.text %]

[% note.text | html_line_break %]

[% END %] +[% END %] + +[% BLOCK product_status_header %] + + + Product + CVSS-Vector + CVSS Base Score + + +[% END %] + +[% BLOCK product_status_row %] + + + [% product_id FILTER product_name %] + + [% FOREACH score IN vulnerability.scores.each %] + [% FOREACH score_product_id IN score.products %] + [% IF score_product_id == product_id %] + [% score.cvss_v3.vectorString %] + [% score.cvss_v3.baseScore %] + [% END %] + [% END %] + [% END %] + + +[% END %] + +[% BLOCK reference %] +
  • + [% reference.summary %] ([% reference.category %]) [% PROCESS link url=reference.url %] +
  • +[% END %] + +[% BLOCK link %] + [% label || url %] + [% label = undef + url = undef %] +[% END %] + +[% BLOCK acknowledgment %] +
  • + [% acknowledgment.names.join(', ') %] + [% IF acknowledgment.organization %] + [% IF acknowledgment.names.size %] from [% END %] [% acknowledgment.organization %] + [% END %] + [% IF acknowledgment.summary %] + for [% acknowledgment.summary %] + [% END %] + [% IF acknowledgment.urls.size %] + (see: [% acknowledgment.urls.join(', ') %]) [% # TODO convert URL to hyperlink %] + [% END %] +
  • +[% END %] + +[% BLOCK remediation %] + +
    + [% remediation.category.replace('_', ' ').ucfirst %] + [% IF remediation.date %] + [% remediation.date.datetime() %] + [% END %] +
    +

    [% remediation.details %]

    + + [% IF remediation.product_ids.size %] +
    For products:
    +
      + [% FOREACH product_id IN remediation.product_ids %] +
    • [% product_id | product_name %]
    • + [% END %] +
    + [% END %] + + [% IF remediation.group_ids.size %] +
    For groups:
    +
      + [% FOREACH group_id IN remediation.group_ids %] +
    • [% group_id %]
    • + [% END %] +
    + [% END %] + + [% IF remediation.url %] +

    [% PROCESS link url = remediation.url %]

    + [% END %] + + [% IF remediation.entitlements.size %] + [% FOREACH entitlement IN remediation.entitlements %] +

    [% entitlement %]

    + [% END %] + [% END %] + + [% IF remediation.restart_required %] + Restart required: [% remediation.restart_required.category.replace('_', ' ').ucfirst %] +

    [% remediation.restart_required.details %]

    + [% END %] + +[% END %] + + +[% BLOCK threat %] +
    + [% threat.category.replace('_', ' ').ucfirst %] + [% IF threat.date %] + [% threat.date.datetime() %] + [% END %] +
    +

    [% threat.details %]

    + + [% IF threat.product_ids.size %] +
    For products:
    +
      + [% FOREACH product_id IN threat.product_ids %] +
    • [% product_id | product_name %]
    • + [% END %] +
    + [% END %] + + [% IF threat.group_ids.size %] +
    For groups:
    +
      + [% FOREACH group_id IN threat.group_ids %] +
    • [% group_id %]
    • + [% END %] +
    + [% END %] + +[% END %] + + + + + + + + + + + + + +

    [% IF document.tracking.id %][% document.tracking.id %]: [% END %] [% document.title %]

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Publisher: [% document.publisher.name %] + + Document category: [% document.category %] +
    + Initial release date: [% document.tracking.initial_release_date.datetime() %] + + Engine: [% "${document.tracking.generator.engine.name} ${document.tracking.generator.engine.version}" %] +
    + Current release date: [% document.tracking.current_release_date.datetime() %] + + Build Date: [% document.tracking.generator.date.datetime() %] +
    + Current version: [% document.tracking.version %] + + Status: [% document.tracking.status %] +
    CVSSv3.1 Base Score: [% max_base_score %]Severity: + [% IF document.aggregate_severity.text %] + [% IF document.aggregate_severity.namespace %] + [% PROCESS link url=document.aggregate_severity.namespace label=document.aggregate_severity.text %] + [% ELSE %] + [% document.aggregate_severity.text %] + [% END %] + [% END %] + +
    + Original language: [% document.source_lang %] + + Language: [% document.lang %] +
    Also referred to: [% document.tracking.aliases.join(', ') %]
    + +[% FOREACH note IN document.notes.get_category('summary') %][% PROCESS document_note %][% END %] +[% FOREACH note IN document.notes.get_category('details') %][% PROCESS document_note %][% END %] +[% FOREACH note IN document.notes.get_category('general') %][% PROCESS document_note %][% END %] +[% FOREACH note IN document.notes.get_category('description') %][% PROCESS document_note %][% END %] +[% FOREACH note IN document.notes.get_category('faq') %][% PROCESS document_note %][% END %] +[% FOREACH note IN document.notes.get_category('unknown') %][% PROCESS document_note %][% END %] + + +[% IF product_tree.product_groups.size %] +

    Product groups

    +{{#product_tree.product_groups}} + {{#summary}}{{.}}{{/summary}}{{^summary}}{{group_id}}{{/summary}} +
      + {{#product_ids}} +
    • {{name}}
    • + {{/product_ids}} +
    +{{/product_tree.product_groups}} +[% END %] + + +

    Vulnerabilities

    + +[% FOREACH vulnerability IN vulnerabilities.each %] + +

    [% vulnerability.title %][% IF vulnerability.cve %] ([% vulnerability.cve %])[% END %]

    + + [% FOREACH note IN vulnerability.notes.get_category('summary') %][% PROCESS vulnerability_note %][% END %] + [% FOREACH note IN vulnerability.notes.get_category('details') %][% PROCESS vulnerability_note %][% END %] + [% FOREACH note IN vulnerability.notes.get_category('general') %][% PROCESS vulnerability_note %][% END %] + [% FOREACH note IN vulnerability.notes.get_category('description') %][% PROCESS vulnerability_note %][% END %] + [% FOREACH note IN vulnerability.notes.get_category('faq') %][% PROCESS vulnerability_note %][% END %] + [% FOREACH note IN vulnerability.notes.get_category('unknown') %][% PROCESS vulnerability_note %][% END %] + + + [% IF vulnerability.cwe.id %] + + + + + [% END %] + [% FOREACH id IN vulnerability.ids.each %] + + + + + [% END %] + [% IF vulnerability.discovery_date %] + + + + + [% END %] + [% IF vulnerability.release_date %] + + + + + [% END %] +
    CWE: + [% vulnerability.cwe.id %] : [% vulnerability.cwe.name %] +
    ID:[% id.text %] ([% id.system_name %])
    Discovery date:[% vulnerability.discovery_date.datetime() %]
    Release date:[% vulnerability.release_date.datetime() %]
    + +

    Product status

    + + [% IF vulnerability.product_status.known_affected.size %] +
    Known affected
    + + [% PROCESS product_status_header %] + + [% FOREACH product_id IN vulnerability.product_status.known_affected %] + [% PROCESS product_status_row %] + [% END %] + +
    + [% END %] + + [% IF vulnerability.product_status.first_affected.size %] +
    First affected
    + + [% PROCESS product_status_header %] + + [% FOREACH product_id IN vulnerability.product_status.first_affected %] + [% PROCESS product_status_row %] + [% END %] + +
    + [% END %] + + [% IF vulnerability.product_status.last_affected.size %] +
    Last affected
    + + [% PROCESS product_status_header %] + + [% FOREACH product_id IN vulnerability.product_status.last_affected %] + [% PROCESS product_status_row %] + [% END %] + +
    + [% END %] + + [% IF vulnerability.product_status.known_not_affected.size %] +
    Known not affected
    +
      + [% FOREACH product_id IN vulnerability.product_status.known_not_affected %] +
    • [% product_id | product_name %]
    • + [% END %] +
    + [% END %] + + [% IF vulnerability.product_status.recommended.size %] +
    Recommended
    +
      + [% FOREACH product_id IN vulnerability.product_status.recommended %] +
    • [% product_id | product_name %]
    • + [% END %] +
    + [% END %] + + [% IF vulnerability.product_status.fixed.size %] +
    Fixed
    +
      + [% FOREACH product_id IN vulnerability.product_status.fixed %] +
    • [% product_id | product_name %]
    • + [% END %] +
    + [% END %] + + [% IF vulnerability.product_status.first_fixed.size %] +
    First fixed
    +
      + [% FOREACH product_id IN vulnerability.product_status.first_fixed %] +
    • [% product_id | product_name %]
    • + [% END %] +
    + [% END %] + + [% IF vulnerability.product_status.under_investigation.size %] +
    Under investgation
    +
      + [% FOREACH product_id IN vulnerability.product_status.under_investigation %] +
    • [% product_id | product_name %]
    • + [% END %] +
    + [% END %] + + [% IF vulnerability.remediations.size %] +

    Remediations

    + [% FOREACH remediation IN vulnerability.remediations.get_category('vendor_fix') %][% PROCESS remediation %][% END %] + [% FOREACH remediation IN vulnerability.remediations.get_category('mitigation') %][% PROCESS remediation %][% END %] + [% FOREACH remediation IN vulnerability.remediations.get_category('workaround') %][% PROCESS remediation %][% END %] + [% FOREACH remediation IN vulnerability.remediations.get_category('none_available') %][% PROCESS remediation %][% END %] + [% FOREACH remediation IN vulnerability.remediations.get_category('no_fix_planned') %][% PROCESS remediation %][% END %] + [% FOREACH remediation IN vulnerability.remediations.get_category('unknown') %][% PROCESS remediation %][% END %] + [% END %] + + [% IF vulnerability.acknowledgments.size %] +

    Acknowledgments

    +
      + [% FOREACH acknowledgment IN vulnerability.acknowledgments.each %] + [% PROCESS acknowledgment %] + [% END %] +
    + [% END %] + + [% IF vulnerability.involvements.size %] +

    Involvement

    +
      + [% FOREACH involvement IN vulnerability.involvements.each %] +
    • + [% IF involvement.date %][% involvement.date.datetime() %][% END %] [% involment.party.upper %] [% IF involvement.status %][% involment.status.replace('_', ' ') %][% END %] [% IF involment.summary %]: [% involment.summary %][% END %] +
    • + [% END %] +
    + [% END %] + + [% IF vulnerability.references.size %] +

    References

    +
      + [% FOREACH reference IN vulnerability.references.each %] + [% PROCESS reference %] + [% END %] +
    + [% END %] + + [% IF vulnerability.threats.size %] +

    Threats

    + [% FOREACH threat IN vulnerability.threats.get_category('exploit_status') %][% PROCESS threat %][% END %] + [% FOREACH threat IN vulnerability.threats.get_category('impact') %][% PROCESS threat %][% END %] + [% FOREACH threat IN vulnerability.threats.get_category('target_set') %][% PROCESS threat %][% END %] + [% FOREACH threat IN vulnerability.threats.get_category('unknown') %][% PROCESS threat %][% END %] + [% END %] + +[% END %] + +[% IF document.acknowledgments.size %] +

    Acknowledgments

    +[% document.publisher.name %] thanks the following parties for their efforts: +
      + [% FOREACH acknowledgment IN document.acknowledgments.each %] + [% PROCESS acknowledgment %] + [% END %] +
    +[% END %] + +[% FOREACH note IN document.notes.get_category('other') %][% PROCESS document_note %][% END %] + +

    [% document.publisher.name %]

    +

    Namespace: [% document.publisher.namespace %]

    +

    [% document.publisher.contact_details %]

    +

    [% document.publisher.issuing_authority %]

    + +[% IF document.references.size %] +

    References

    +
      + [% FOREACH reference IN document.references.each %] + [% PROCESS reference %] + [% END %] +
    +[% END %] + +

    Revision history

    + + + + + + + + + + [% FOREACH revision_history IN document.tracking.revision_history.each %] + + + + + + [% END %] + +
    VersionDate of the revisionSummary of the revision
    [% revision_history.number %][% revision_history.date %][% revision_history.summary %]
    + +

    Sharing rules

    +

    + [% IF document.distribution.tlp %] + TLP:[% document.distribution.tlp.label %]
    + For the TLP version see: [% INCLUDE link url=(document.distribution.tlp.url || "https://www.first.org/tlp/") xlabel=(document.distribution.tlp.url || "https://www.first.org/tlp/") %] + [% END %] +

    +

    [% document.distribution.text %]

    + +[% FOREACH note IN document.notes.get_category('legal_disclaimer') %][% PROCESS document_note %][% END %] + + + diff --git a/t/00-load.t b/t/00-load.t new file mode 100644 index 0000000..cf9499b --- /dev/null +++ b/t/00-load.t @@ -0,0 +1,16 @@ +#!perl -T + +use strict; +use warnings; + +use Test::More; + +use_ok('CSAF'); +use_ok('CSAF::Lite'); +use_ok('CSAF::Util'); +use_ok('CSAF::Type'); +use_ok('CSAF::Builder'); + +done_testing(); + +diag("CSAF $CSAF::VERSION, Perl $], $^X"); diff --git a/t/10-mandatory-6.1.11.t b/t/10-mandatory-6.1.11.t new file mode 100644 index 0000000..a4ebd96 --- /dev/null +++ b/t/10-mandatory-6.1.11.t @@ -0,0 +1,39 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(base_csaf_security_advisory exec_validator_mandatory_test); + +# 6.1.11 CWE + +# It MUST be tested that given CWE exists and is valid. + +# The relevant path for this test is: + +# /vulnerabilities[]/cwe + +# Fail test: + +# "cwe": { +# "id": "CWE-79", +# "name": "Improper Input Validation" +# } + +my $csaf = base_csaf_security_advisory(); + +$csaf->product_tree->full_product_names->add(name => 'Product A', product_id => 'CSAFPID-9080700'); + +my $vulns = $csaf->vulnerabilities; +my $vuln = $vulns->add(cve => 'CVE-2023-00000'); + +$vuln->cwe(id => 'CWE-79', name => 'Improper Input Validation'); + +exec_validator_mandatory_test($csaf, '6.1.11'); + +done_testing; diff --git a/t/10-mandatory-6.1.13.t b/t/10-mandatory-6.1.13.t new file mode 100644 index 0000000..a25afbe --- /dev/null +++ b/t/10-mandatory-6.1.13.t @@ -0,0 +1,47 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(base_csaf_security_advisory exec_validator_mandatory_test); + +# 6.1.13 PURL + +# It MUST be tested that given PURL is valid. + +# The relevant paths for this test are: + +# /product_tree/branches[](/branches[])*/product/product_identification_helper/purl +# /product_tree/full_product_names[]/product_identification_helper/purl +# /product_tree/relationships[]/full_product_name/product_identification_helper/purl + +# Fail test: + +# "product_tree": { +# "full_product_names": [ +# { +# "name": "Product A", +# "product_id": "CSAFPID-9080700", +# "product_identification_helper": { +# "purl": "pkg:maven/@1.3.4" +# } +# } +# ] +# } + +my $csaf = base_csaf_security_advisory(); + +my $product = $csaf->product_tree->full_product_names->add( + name => 'Product A', + product_id => 'CSAFPID-9080700', + product_identification_helper => {purl => 'pkg:maven/@1.3.4'} +); + +exec_validator_mandatory_test($csaf, '6.1.13'); + +done_testing; diff --git a/t/10-mandatory-6.1.15.t b/t/10-mandatory-6.1.15.t new file mode 100644 index 0000000..1bb4bf1 --- /dev/null +++ b/t/10-mandatory-6.1.15.t @@ -0,0 +1,56 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.15 Translator + +# It MUST be tested that /document/source_lang is present and set if the value translator is used for /document/publisher/category. + +# The relevant path for this test is: + +# /document/source_lang + +# Fail test: + +# "document": { +# // ... +# "publisher": { +# "category": "translator", +# "name": "CSAF TC Translator", +# "namespace": "https://csaf.io/translator" +# }, +# "title": "Mandatory test: Translator (failing example 1)", +# // ... +# } + + +my $csaf = CSAF->new; + +$csaf->document->title('Mandatory test: Translator (failing example 1)'); +$csaf->document->category('csaf_security_advisory'); +$csaf->document->publisher( + category => 'translator', + name => 'CSAF TC Translator', + namespace => 'https://csaf.io/translator' +); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1.0.0', + initial_release_date => 'now', + current_release_date => 'now' +); + +exec_validator_mandatory_test($csaf, '6.1.15'); + +done_testing; diff --git a/t/10-mandatory-6.1.16.t b/t/10-mandatory-6.1.16.t new file mode 100644 index 0000000..c585ddd --- /dev/null +++ b/t/10-mandatory-6.1.16.t @@ -0,0 +1,61 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.16 Latest Document Version + +# It MUST be tested that document version has the same value as the the number in the last item of Revision History when it is sorted ascending by date. Build metadata is ignored in the comparison. Any pre-release part is also ignored if the document status is draft. + +# The relevant path for this test is: + +# /document/tracking/version + +# Fail test: + +# "tracking": { +# // ... +# "revision_history": [ +# { +# "date": "2021-07-21T09:00:00.000Z", +# "number": "1", +# "summary": "Initial version." +# }, +# { +# "date": "2021-07-21T10:00:00.000Z", +# "number": "2", +# "summary": "Second version." +# } +# ], +# // ... +# "version": "1" +# } + +my $csaf = CSAF->new; + +$csaf->document->title('Base CSAF Document'); +$csaf->document->category('csaf_security_advisory'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1', + initial_release_date => 'now', + current_release_date => 'now' +); + +$tracking->revision_history->add(date => 'now', summary => 'Initial version.', number => '1'); +$tracking->revision_history->add(date => 'now', summary => 'Second version.', number => '2'); + +exec_validator_mandatory_test($csaf, '6.1.16'); + +done_testing; diff --git a/t/10-mandatory-6.1.17.t b/t/10-mandatory-6.1.17.t new file mode 100644 index 0000000..c4ccef7 --- /dev/null +++ b/t/10-mandatory-6.1.17.t @@ -0,0 +1,46 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.17 Document Status Draft + +# It MUST be tested that document status is draft if the document version is 0 or 0.y.z or contains the pre-release part. + +# The relevant path for this test is: + +# /document/tracking/status + +# Fail test: + +# "tracking": { +# // ... +# "status": "final", +# "version": "0.9.5" +# } + +my $csaf = CSAF->new; + +$csaf->document->title('Base CSAF Document'); +$csaf->document->category('csaf_security_advisory'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '0.9.5', + initial_release_date => 'now', + current_release_date => 'now' +); + +exec_validator_mandatory_test($csaf, '6.1.17'); + +done_testing; diff --git a/t/10-mandatory-6.1.18.t b/t/10-mandatory-6.1.18.t new file mode 100644 index 0000000..132fafc --- /dev/null +++ b/t/10-mandatory-6.1.18.t @@ -0,0 +1,61 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.18 Released Revision History + +# It MUST be tested that no item of the revision history has a number of 0 or 0.y.z when the document status is final or interim. + +# The relevant path for this test is: + +# /document/tracking/revision_history[]/number + +# Fail test: + +# "tracking": { +# // ... +# "revision_history": [ +# { +# "date": "2021-05-17T10:00:00.000Z", +# "number": "0", +# "summary": "First draft" +# }, +# { +# "date": "2021-07-21T10:00:00.000Z", +# "number": "1", +# "summary": "Initial version." +# } +# ], +# "status": "final", +# "version": "1" +# } + +my $csaf = CSAF->new; + +$csaf->document->title('Base CSAF Document'); +$csaf->document->category('csaf_security_advisory'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1', + initial_release_date => 'now', + current_release_date => 'now' +); + +$tracking->revision_history->add(date => 'now', summary => 'First draft', number => '0'); +$tracking->revision_history->add(date => 'now', summary => 'Initial version.', number => '1'); + +exec_validator_mandatory_test($csaf, '6.1.18'); + +done_testing; diff --git a/t/10-mandatory-6.1.19.t b/t/10-mandatory-6.1.19.t new file mode 100644 index 0000000..d5d9b93 --- /dev/null +++ b/t/10-mandatory-6.1.19.t @@ -0,0 +1,60 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.19 Revision History Entries for Pre-release Versions + +# It MUST be tested that no item of the revision history has a number which includes pre-release information. + +# The relevant path for this test is: + +# /document/tracking/revision_history[]/number + +# Fail test: + +# "revision_history": [ +# { +# "date": "2021-04-22T10:00:00.000Z", +# "number": "1.0.0-rc", +# "summary": "Release Candidate for initial version." +# }, +# { +# "date": "2021-04-23T10:00:00.000Z", +# "number": "1.0.0", +# "summary": "Initial version." +# } +# ] + +my $csaf = CSAF->new; + +$csaf->document->title('Base CSAF Document'); +$csaf->document->category('csaf_security_advisory'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1.0.0', + initial_release_date => 'now', + current_release_date => 'now' +); + +$tracking->revision_history->add( + date => 'now', + summary => 'Release Candidate for initial version.', + number => '1.0.0-rc' +); +$tracking->revision_history->add(date => 'now', summary => 'Initial version.', number => '1.0.0'); + +exec_validator_mandatory_test($csaf, '6.1.19'); + +done_testing; diff --git a/t/10-mandatory-6.1.20.t b/t/10-mandatory-6.1.20.t new file mode 100644 index 0000000..6733fa3 --- /dev/null +++ b/t/10-mandatory-6.1.20.t @@ -0,0 +1,50 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.20 Non-draft Document Version + +# It MUST be tested that document version does not contain a pre-release part if the document status is final or interim. + +# The relevant path for this test is: + +# /document/tracking/version + +# Fail test: + +# "tracking": { +# // ... +# "status": "interim", +# "version": "1.0.0-alpha" +# } + +foreach my $status (qw(interim final)) { + + my $csaf = CSAF->new; + + $csaf->document->title('Base CSAF Document'); + $csaf->document->category('csaf_security_advisory'); + $csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + + my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => $status, + version => '1.0.0-alpha', + initial_release_date => 'now', + current_release_date => 'now' + ); + + exec_validator_mandatory_test($csaf, '6.1.20'); + +} + +done_testing; diff --git a/t/10-mandatory-6.1.22.t b/t/10-mandatory-6.1.22.t new file mode 100644 index 0000000..7c3e383 --- /dev/null +++ b/t/10-mandatory-6.1.22.t @@ -0,0 +1,56 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.22 Multiple Definition in Revision History + +# It MUST be tested that items of the revision history do not contain the same version number. + +# The relevant path for this test is: + +# /document/tracking/revision_history + +# Fail test: + +# "revision_history": [ +# { +# "date": "2021-07-20T10:00:00.000Z", +# "number": "1", +# "summary": "Initial version." +# }, +# { +# "date": "2021-07-21T10:00:00.000Z", +# "number": "1", +# "summary": "Some other changes." +# } +# ] + +my $csaf = CSAF->new; + +$csaf->document->title('Base CSAF Document'); +$csaf->document->category('csaf_security_advisory'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1.0.0', + initial_release_date => 'now', + current_release_date => 'now' +); + +$tracking->revision_history->add(date => 'now', summary => 'Initial version.', number => '1'); +$tracking->revision_history->add(date => 'now', summary => 'Some other changes.', number => '1'); + +exec_validator_mandatory_test($csaf, '6.1.22'); + +done_testing; diff --git a/t/10-mandatory-6.1.23.t b/t/10-mandatory-6.1.23.t new file mode 100644 index 0000000..15e39fd --- /dev/null +++ b/t/10-mandatory-6.1.23.t @@ -0,0 +1,42 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(base_csaf_security_advisory exec_validator_mandatory_test); + +# 6.1.23 Multiple Use of Same CVE + +# It MUST be tested that a CVE is not used in multiple vulnerability items. + +# The relevant path for this test is: + +# /vulnerabilities[]/cve + +# Fail test: + +# "vulnerabilities": [ +# { +# "cve": "CVE-2017-0145" +# }, +# { +# "cve": "CVE-2017-0145" +# } +# ] + +my $csaf = base_csaf_security_advisory(); + +$csaf->product_tree->full_product_names->add(name => 'Product A', product_id => 'CSAFPID-9080700'); + +my $vulns = $csaf->vulnerabilities; +$vulns->add(cve => 'CVE-2017-0145'); +$vulns->add(cve => 'CVE-2017-0145'); + +exec_validator_mandatory_test($csaf, '6.1.23'); + +done_testing; diff --git a/t/10-mandatory-6.1.26.t b/t/10-mandatory-6.1.26.t new file mode 100644 index 0000000..333a279 --- /dev/null +++ b/t/10-mandatory-6.1.26.t @@ -0,0 +1,75 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.26 Prohibited Document Category Name + +# It MUST be tested that the document category is not equal to the (case insensitive) name (without the prefix csaf_) or value of any other profile than "CSAF Base". Any occurrences of dash, whitespace, and underscore characters are removed from the values on both sides before the match. Also the value MUST NOT start with the reserved prefix csaf_ except if the value is csaf_base. + +# This test does only apply for CSAF documents with the profile "CSAF Base". Therefore, it MUST be skipped if the document category matches one of the values defined for the profile other than "CSAF Base". + +# For CSAF 2.0, the test must be skipped for the following values in /document/category: + +# csaf_base +# csaf_security_incident_response +# csaf_informational_advisory +# csaf_security_advisory +# csaf_vex + +# This is the only mandatory test related to the profile "CSAF Base" as the required fields SHALL be checked by validating the JSON schema. + +# The relevant path for this test is: + +# /document/category + +# Currently prohibited values: + +# Csaf_a +# Informational Advisory +# security-incident-response +# Security Advisory +# veX +# V_eX + +# Fail test: + +# "category": "Security_Incident_Response" + +my $csaf = CSAF->new; + +$csaf->document->title('Mandatory test: 6.1.26'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1.0.0', + initial_release_date => 'now', + current_release_date => 'now' +); + +my @categories = ( + 'Security_Incident_Response', 'Csaf_a', + 'Informational Advisory', 'security-incident-response', + 'Security Advisory', 'veX', + 'V_eX' +); + +foreach my $category (@categories) { + + $csaf->document->category($category); + + exec_validator_mandatory_test($csaf, '6.1.26'); + +} + +done_testing; diff --git a/t/10-mandatory-6.1.27.1.t b/t/10-mandatory-6.1.27.1.t new file mode 100644 index 0000000..5405eb2 --- /dev/null +++ b/t/10-mandatory-6.1.27.1.t @@ -0,0 +1,65 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.27.1 Document Notes + +# It MUST be tested that at least one item in /document/notes exists which has a category of description, details, general or summary. + +# The relevant values for /document/category are: + +# csaf_informational_advisory +# csaf_security_incident_response + +# The relevant path for this test is: + +# /document/notes + +# Fail test: + +# "notes": [ +# { +# "category": "legal_disclaimer", +# "text": "The CSAF document is provided to You \"AS IS\" and \"AS AVAILABLE\" and with all faults and defects without warranty of any kind.", +# "title": "Terms of Use" +# } +# ] + +my $csaf = CSAF->new; + +$csaf->document->title('Mandatory test: 6.1.27.1'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1.0.0', + initial_release_date => 'now', + current_release_date => 'now' +); + +$csaf->document->notes->add( + category => 'legal_disclaimer', + title => 'Terms of Use', + text => + 'The CSAF document is provided to You \"AS IS\" and \"AS AVAILABLE\" and with all faults and defects without warranty of any kind.', +); + +foreach my $category (qw(csaf_informational_advisory csaf_security_incident_response)) { + + $csaf->document->category($category); + + exec_validator_mandatory_test($csaf, '6.1.27.1'); + +} + +done_testing; diff --git a/t/10-mandatory-6.1.27.11.t b/t/10-mandatory-6.1.27.11.t new file mode 100644 index 0000000..7b0a7d0 --- /dev/null +++ b/t/10-mandatory-6.1.27.11.t @@ -0,0 +1,59 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.27.11 Vulnerabilities + +# It MUST be tested that the element /vulnerabilities exists. + +# The relevant values for /document/category are: + +# csaf_security_advisory +# csaf_vex + +# The relevant path for this test is: + +# /vulnerabilities + +# Fail test: + +# { +# "document": { +# // ... +# }, +# "product_tree": [ +# // ... +# ] +# } + +my $csaf = CSAF->new; + +$csaf->document->title('Mandatory test: 6.1.27.11'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1.0.0', + initial_release_date => 'now', + current_release_date => 'now' +); + +foreach my $category (qw(csaf_security_advisory csaf_vex)) { + + $csaf->document->category($category); + + exec_validator_mandatory_test($csaf, '6.1.27.11'); + +} + +done_testing; diff --git a/t/10-mandatory-6.1.27.2.t b/t/10-mandatory-6.1.27.2.t new file mode 100644 index 0000000..2a00f42 --- /dev/null +++ b/t/10-mandatory-6.1.27.2.t @@ -0,0 +1,64 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.27.2 Document References + +# It MUST be tested that at least one item in /document/references exists that has links to an external source. + +# The relevant values for /document/category are: + +# csaf_informational_advisory +# csaf_security_incident_response + +# The relevant path for this test is: + +# /document/references + +# Fail test: + +# "references": [ +# { +# "category": "self", +# "summary": "The canonical URL.", +# "url": "https://example.com/security/data/csaf/2021/OASIS_CSAF_TC-CSAF_2_0-2021-6-1-27-02-01.json" +# } +# ] + +my $csaf = CSAF->new; + +$csaf->document->title('Mandatory test: 6.1.27.2'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1.0.0', + initial_release_date => 'now', + current_release_date => 'now' +); + +$csaf->document->references->add( + url => 'https://example.com/security/data/csaf/2021/OASIS_CSAF_TC-CSAF_2_0-2021-6-1-27-02-01.json', + summary => 'The canonical URL.', + category => 'self' +); + +foreach my $category (qw(csaf_informational_advisory csaf_security_incident_response)) { + + $csaf->document->category($category); + + exec_validator_mandatory_test($csaf, '6.1.27.2'); + +} + +done_testing; diff --git a/t/10-mandatory-6.1.27.3.t b/t/10-mandatory-6.1.27.3.t new file mode 100644 index 0000000..1178183 --- /dev/null +++ b/t/10-mandatory-6.1.27.3.t @@ -0,0 +1,53 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.27.3 Vulnerabilities + +# It MUST be tested that the element /vulnerabilities does not exist. + +# The relevant value for /document/category is: + +# csaf_informational_advisory + +# The relevant path for this test is: + +# /vulnerabilities + +# Fail test: + +# "vulnerabilities": [ +# { +# "title": "A vulnerability item that SHALL NOT exist" +# } +# ] + +my $csaf = CSAF->new; + +$csaf->document->title('Mandatory test: 6.1.27.3'); +$csaf->document->category('csaf_informational_advisory'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1.0.0', + initial_release_date => 'now', + current_release_date => 'now' +); + +my $vulns = $csaf->vulnerabilities; +my $vuln = $vulns->add(title => 'A vulnerability item that SHALL NOT exist'); + +exec_validator_mandatory_test($csaf, '6.1.27.3'); + +done_testing; diff --git a/t/10-mandatory-6.1.27.4.t b/t/10-mandatory-6.1.27.4.t new file mode 100644 index 0000000..2432808 --- /dev/null +++ b/t/10-mandatory-6.1.27.4.t @@ -0,0 +1,62 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.27.4 Product Tree + +# It MUST be tested that the element /product_tree exists. + +# The relevant values for /document/category are: + +# csaf_security_advisory +# csaf_vex + +# The relevant path for this test is: + +# /product_tree + +# Fail test: + +# { +# "document": { +# // ... +# }, +# "vulnerabilities": [ +# // ... +# ] +# } + +my $csaf = CSAF->new; + +$csaf->document->title('Mandatory test: 6.1.27.5'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1.0.0', + initial_release_date => 'now', + current_release_date => 'now' +); + +my $vulns = $csaf->vulnerabilities; +my $vuln = $vulns->add(title => 'CSAF without product_tree'); + +foreach my $category (qw(csaf_security_advisory csaf_vex)) { + + $csaf->document->category($category); + + exec_validator_mandatory_test($csaf, '6.1.27.4'); + +} + +done_testing; diff --git a/t/10-mandatory-6.1.27.5.t b/t/10-mandatory-6.1.27.5.t new file mode 100644 index 0000000..abcda97 --- /dev/null +++ b/t/10-mandatory-6.1.27.5.t @@ -0,0 +1,59 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.27.5 Vulnerability Notes + +# For each item in /vulnerabilities it MUST be tested that the element notes exists. + +# The relevant values for /document/category are: + +# csaf_security_advisory +# csaf_vex + +# The relevant path for this test is: + +# /vulnerabilities[]/notes + +# Fail test: + +# "vulnerabilities": [ +# { +# "title": "A vulnerability item without a note" +# } +# ] + +my $csaf = CSAF->new; + +$csaf->document->title('Mandatory test: 6.1.27.5'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1.0.0', + initial_release_date => 'now', + current_release_date => 'now' +); + +my $vulns = $csaf->vulnerabilities; +my $vuln = $vulns->add(title => 'A vulnerability item without a note'); + +foreach my $category (qw(csaf_security_advisory csaf_vex)) { + + $csaf->document->category($category); + + exec_validator_mandatory_test($csaf, '6.1.27.5'); + +} + +done_testing; diff --git a/t/10-mandatory-6.1.27.6.t b/t/10-mandatory-6.1.27.6.t new file mode 100644 index 0000000..f2cb356 --- /dev/null +++ b/t/10-mandatory-6.1.27.6.t @@ -0,0 +1,53 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.27.6 Product Status + +# For each item in /vulnerabilities it MUST be tested that the element product_status exists. + +# The relevant value for /document/category is: + +# csaf_security_advisory + +# The relevant path for this test is: + +# /vulnerabilities[]/product_status + +# Fail test: + +# "vulnerabilities": [ +# { +# "title": "A vulnerability item without a product status" +# } +# ] + +my $csaf = CSAF->new; + +$csaf->document->title('Mandatory test: 6.1.27.6'); +$csaf->document->category('csaf_security_advisory'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1.0.0', + initial_release_date => 'now', + current_release_date => 'now' +); + +my $vulns = $csaf->vulnerabilities; +my $vuln = $vulns->add(title => 'A vulnerability item without a product status'); + +exec_validator_mandatory_test($csaf, '6.1.27.6'); + +done_testing; diff --git a/t/10-mandatory-6.1.27.7.t b/t/10-mandatory-6.1.27.7.t new file mode 100644 index 0000000..ddec84b --- /dev/null +++ b/t/10-mandatory-6.1.27.7.t @@ -0,0 +1,64 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.27.7 VEX Product Status + +# For each item in /vulnerabilities it MUST be tested that at least one of the elements fixed, known_affected, known_not_affected, or under_investigation is present in product_status. + +# The relevant value for /document/category is: + +# csaf_vex + +# The relevant paths for this test are: + +# /vulnerabilities[]/product_status/fixed +# /vulnerabilities[]/product_status/known_affected +# /vulnerabilities[]/product_status/known_not_affected +# /vulnerabilities[]/product_status/under_investigation + +# Fail test: + +# "product_status": { +# "first_fixed": [ +# // ... +# ], +# "recommended": [ +# // ... +# ] +# } + +my $csaf = CSAF->new; + +$csaf->document->title('Mandatory test: 6.1.27.7'); +$csaf->document->category('csaf_vex'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1.0.0', + initial_release_date => 'now', + current_release_date => 'now' +); + +$csaf->product_tree->full_product_names->add(name => 'Product A', product_id => 'CSAFPID-9080700'); + +my $vulns = $csaf->vulnerabilities; +my $vuln = $vulns->add(cve => 'CVE-2023-00000'); + +$vuln->product_status->first_fixed(['CSAFPID-9080700']); +$vuln->product_status->recommended(['CSAFPID-9080700']); + +exec_validator_mandatory_test($csaf, '6.1.27.7'); + +done_testing; diff --git a/t/10-mandatory-6.1.27.8.t b/t/10-mandatory-6.1.27.8.t new file mode 100644 index 0000000..0aaf89c --- /dev/null +++ b/t/10-mandatory-6.1.27.8.t @@ -0,0 +1,55 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.27.8 Vulnerability ID + +# For each item in /vulnerabilities it MUST be tested that at least one of the elements cve or ids is present. + +# The relevant value for /document/category is: + +# csaf_vex + +# The relevant paths for this test are: + +# /vulnerabilities[]/cve +# /vulnerabilities[]/ids + + +# Fail test: + +# "vulnerabilities": [ +# { +# "title": "A vulnerability item without a CVE or ID" +# } +# ] + +my $csaf = CSAF->new; + +$csaf->document->title('Mandatory test: 6.1.27.8'); +$csaf->document->category('csaf_vex'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1.0.0', + initial_release_date => 'now', + current_release_date => 'now' +); + +my $vulns = $csaf->vulnerabilities; +my $vuln = $vulns->add(title => 'A vulnerability item without a CVE or ID'); + +exec_validator_mandatory_test($csaf, '6.1.27.8'); + +done_testing; diff --git a/t/10-mandatory-6.1.28.t b/t/10-mandatory-6.1.28.t new file mode 100644 index 0000000..d828169 --- /dev/null +++ b/t/10-mandatory-6.1.28.t @@ -0,0 +1,56 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(exec_validator_mandatory_test); +use CSAF; + +# 6.1.28 Translation + +# It MUST be tested that the given source language and document language are not the same. + +# The relevant path for this test is: + +# /document/lang +# /document/source_lang + +# Fail test: + +# "document": { +# // ... +# "lang": "en-US", +# // ... +# "source_lang": "en-US", +# // ... +# } + +my $csaf = CSAF->new; + +$csaf->document->title('Mandatory test: Translator (failing example 1)'); +$csaf->document->category('csaf_security_advisory'); +$csaf->document->publisher( + category => 'translator', + name => 'CSAF TC Translator', + namespace => 'https://csaf.io/translator' +); + +$csaf->document->lang('en-US'); +$csaf->document->source_lang('en-US'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1.0.0', + initial_release_date => 'now', + current_release_date => 'now' +); + +exec_validator_mandatory_test($csaf, '6.1.28'); + +done_testing; diff --git a/t/10-mandatory-6.1.31.t b/t/10-mandatory-6.1.31.t new file mode 100644 index 0000000..ad02b75 --- /dev/null +++ b/t/10-mandatory-6.1.31.t @@ -0,0 +1,54 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(base_csaf_security_advisory exec_validator_mandatory_test); +use CSAF::Validator::MandatoryTests; + +# 6.1.31 Version Range in Product Version + +# For each element of type /$defs/branches_t with category of product_version it MUST be tested that the value of name does not contain a version range. + +# To implement this test it is deemed sufficient that, when converted to lower case, the value of name does not contain any of the following strings: + +# < +# <= +# > +# >= +# after +# all +# before +# earlier +# later +# prior +# versions + +# The relevant paths for this test are: + +# /product_tree/branches[](/branches[])*/name + + +# Fail test: + +# "branches": [ +# { +# "category": "product_version", +# "name": "prior to 4.2", +# // ... +# } +# ] + +my $csaf = base_csaf_security_advisory(); + +my $branches = $csaf->product_tree->branches; +$branches->add(category => 'product_version', name => 'prior to 4.2'); + +exec_validator_mandatory_test($csaf, '6.1.31'); + +done_testing; diff --git a/t/10-mandatory-6.1.6.t b/t/10-mandatory-6.1.6.t new file mode 100644 index 0000000..d92c3ae --- /dev/null +++ b/t/10-mandatory-6.1.6.t @@ -0,0 +1,72 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(base_csaf_security_advisory exec_validator_mandatory_test); +use CSAF::Validator::MandatoryTests; + +# 6.1.6 Contradicting Product Status + +# For each item in /vulnerabilities it MUST be tested that the same Product ID is not member of contradicting product status groups. The sets formed by the contradicting groups within one vulnerability item MUST be pairwise disjoint. + +# Contradiction groups are: + +# Affected: +# /vulnerabilities[]/product_status/first_affected[] +# /vulnerabilities[]/product_status/known_affected[] +# /vulnerabilities[]/product_status/last_affected[] + +# Not affected: +# /vulnerabilities[]/product_status/known_not_affected[] + +# Fixed: +# /vulnerabilities[]/product_status/first_fixed[] +# /vulnerabilities[]/product_status/fixed[] + +# Under investigation: +# /vulnerabilities[]/product_status/under_investigation[] + +# Note: An issuer might recommend (/vulnerabilities[]/product_status/recommended) a product version from any group - also from the affected group, i.e. if it was discovered that fixed versions introduce a more severe vulnerability. + +# Fail test: + +# "product_tree": { +# "full_product_names": [ +# { +# "product_id": "CSAFPID-9080700", +# "name": "Product A" +# } +# ] +# }, +# "vulnerabilities": [ +# { +# "product_status": { +# "known_affected": [ +# "CSAFPID-9080700" +# ], +# "known_not_affected": [ +# "CSAFPID-9080700" +# ] +# } +# } +# ] + +my $csaf = base_csaf_security_advisory(); + +$csaf->product_tree->full_product_names->add(name => 'Product A', product_id => 'CSAFPID-9080700'); + +my $vulns = $csaf->vulnerabilities; +my $vuln = $vulns->add(cve => 'CVE-2023-00000'); + +$vuln->product_status->first_affected(['CSAFPID-9080700']); +$vuln->product_status->under_investigation(['CSAFPID-9080700']); + +exec_validator_mandatory_test($csaf, '6.1.6'); + +done_testing; diff --git a/t/10-mandatory-6.1.7.t b/t/10-mandatory-6.1.7.t new file mode 100644 index 0000000..3e7f89a --- /dev/null +++ b/t/10-mandatory-6.1.7.t @@ -0,0 +1,82 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(base_csaf_security_advisory exec_validator_mandatory_test); +use CSAF::Validator::MandatoryTests; + +# 6.1.7 Multiple Scores with same Version per Product + +# For each item in /vulnerabilities it MUST be tested that the same Product ID is not member of more than one CVSS-Vectors with the same version. + +# The relevant path for this test is: + +# /vulnerabilities[]/scores[] + +# Fail test: + +# "product_tree": { +# "full_product_names": [ +# { +# "product_id": "CSAFPID-9080700", +# "name": "Product A" +# } +# ] +# }, +# "vulnerabilities": [ +# { +# "scores": [ +# { +# "products": [ +# "CSAFPID-9080700" +# ], +# "cvss_v3": { +# "version": "3.1", +# "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H", +# "baseScore": 10, +# "baseSeverity": "CRITICAL" +# } +# }, +# { +# "products": [ +# "CSAFPID-9080700" +# ], +# "cvss_v3": { +# "version": "3.1", +# "vectorString": "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:H", +# "baseScore": 6.5, +# "baseSeverity": "MEDIUM" +# } +# } +# ] +# } +# ] + +my $csaf = base_csaf_security_advisory(); + +$csaf->product_tree->full_product_names->add(name => 'Product A', product_id => 'CSAFPID-9080700'); + +my $vulns = $csaf->vulnerabilities; +my $vuln = $vulns->add(cve => 'CVE-2023-00000'); + +$vuln->scores->add( + products => ['CSAFPID-9080700'], + cvss_v3 => + {baseSeverity => 'CRITICAL', baseScore => 10, vectorString => 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H'} +); + +$vuln->scores->add( + products => ['CSAFPID-9080700'], + cvss_v3 => + {baseSeverity => 'CRITICAL', baseScore => 10, vectorString => 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H'} +); + +exec_validator_mandatory_test($csaf, '6.1.7'); + +done_testing; diff --git a/t/10-mandatory-6.1.8.t b/t/10-mandatory-6.1.8.t new file mode 100644 index 0000000..1692118 --- /dev/null +++ b/t/10-mandatory-6.1.8.t @@ -0,0 +1,67 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(base_csaf_security_advisory exec_validator_mandatory_test); +use CSAF::Validator::MandatoryTests; + +# 6.1.8 Invalid CVSS + +# It MUST be tested that the given CVSS object is valid according to the referenced schema. + +# The relevant paths for this test are: + +# /vulnerabilities[]/scores[]/cvss_v2 +# /vulnerabilities[]/scores[]/cvss_v3 + +# Fail test: + +# "cvss_v3": { +# "version": "3.1", +# "vectorString": "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:H", +# "baseScore": 6.5 +# } + +my $csaf = base_csaf_security_advisory(); + +$csaf->product_tree->full_product_names->add(name => 'Product A', product_id => 'CSAFPID-9080700'); + +my $vulns = $csaf->vulnerabilities; +my $vuln = $vulns->add(cve => 'CVE-2023-00000'); + +{ + + # Check required attribute + + eval { + $vuln->scores->add( + products => ['CSAFPID-9080700'], + cvss_v3 => {baseScore => 6.5, vectorString => 'CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:H'} + ); + }; + + diag($@); + like($@, qr/baseSeverity/, 'Missing "baseSeverity" - Detect "Moo" coercion'); + +} + +{ + + # Check CVSS (2.0 and 3.x) JSON Schema + + $vuln->scores->add( + products => ['CSAFPID-9080700'], + cvss_v2 => {baseScore => 6.5, vectorString => 'CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:H'} + ); + + exec_validator_mandatory_test($csaf, '6.1.9'); + +} + +done_testing; diff --git a/t/10-mandatory-6.1.9.t b/t/10-mandatory-6.1.9.t new file mode 100644 index 0000000..38c0a6f --- /dev/null +++ b/t/10-mandatory-6.1.9.t @@ -0,0 +1,55 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(base_csaf_security_advisory exec_validator_mandatory_test); +use CSAF::Validator::MandatoryTests; + +# 6.1.9 Invalid CVSS computation + +# It MUST be tested that the given CVSS object has the values computed correctly according to the definition. + +# The vectorString SHOULD take precedence. + +# The relevant paths for this test are: + +# /vulnerabilities[]/scores[]/cvss_v2/baseScore +# /vulnerabilities[]/scores[]/cvss_v2/temporalScore +# /vulnerabilities[]/scores[]/cvss_v2/environmentalScore +# /vulnerabilities[]/scores[]/cvss_v3/baseScore +# /vulnerabilities[]/scores[]/cvss_v3/baseSeverity +# /vulnerabilities[]/scores[]/cvss_v3/temporalScore +# /vulnerabilities[]/scores[]/cvss_v3/temporalSeverity +# /vulnerabilities[]/scores[]/cvss_v3/environmentalScore +# /vulnerabilities[]/scores[]/cvss_v3/environmentalSeverity + +# Fail test: + +# "cvss_v3": { +# "version": "3.1", +# "vectorString": "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:H", +# "baseScore": 10.0, +# "baseSeverity": "LOW" +# } + +my $csaf = base_csaf_security_advisory(); + +$csaf->product_tree->full_product_names->add(name => 'Product A', product_id => 'CSAFPID-9080700'); + +my $vulns = $csaf->vulnerabilities; +my $vuln = $vulns->add(cve => 'CVE-2023-00000'); + +$vuln->scores->add( + products => ['CSAFPID-9080700'], + cvss_v3 => + {baseScore => 10.0, baseSeverity => 'LOW', vectorString => 'CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:H'} +); + +exec_validator_mandatory_test($csaf, '6.1.9'); +done_testing; diff --git a/t/10-optional-6.2.14.t b/t/10-optional-6.2.14.t new file mode 100644 index 0000000..fec93f7 --- /dev/null +++ b/t/10-optional-6.2.14.t @@ -0,0 +1,40 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(base_csaf_security_advisory exec_validator_optional_test); +use CSAF::Validator::OptionalTests; + +# 6.2.14 Use of Private Language + +# For each element of type /$defs/language_t it MUST be tested that the language code does not contain subtags reserved for private use. + +# The relevant paths for this test are: + +# /document/lang +# /document/source_lang + +# Fail test: +# "document": { +# // ... +# "lang": "qtx", +# // ... +# } + +# The language code qtx is reserved for private use. +# A tool MAY remove such subtag as a quick fix. + +my $csaf = base_csaf_security_advisory(); + +$csaf->document->lang('qtx'); +$csaf->document->source_lang('qtx'); + +exec_validator_optional_test($csaf, '6.2.14'); + +done_testing; diff --git a/t/10-optional-6.2.2.t b/t/10-optional-6.2.2.t new file mode 100644 index 0000000..e998e9c --- /dev/null +++ b/t/10-optional-6.2.2.t @@ -0,0 +1,62 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(base_csaf_security_advisory exec_validator_optional_test); +use CSAF::Validator::OptionalTests; + +# 6.2.2 Missing Remediation + +# For each Product ID (type /$defs/product_id_t) in the Product Status groups Affected and Under investigation it MUST be tested that a +# remediation exists. + +# The remediation might be of the category none_available or no_fix_planned . + + +# The relevant paths for this test are: +# /vulnerabilities[]/product_status/first_affected[] +# /vulnerabilities[]/product_status/known_affected[] +# /vulnerabilities[]/product_status/last_affected[] +# /vulnerabilities[]/product_status/under_investigation[] + +# Fail test: + +# "product_tree": { +# "full_product_names": [ +# { +# "product_id": "CSAFPID-9080700", +# "name": "Product A" +# } +# ] +# }, +# "vulnerabilities": [ +# { +# "product_status": { +# "last_affected": [ +# "CSAFPID-9080700" +# ] +# } +# } +# ] + +my $csaf = base_csaf_security_advisory(); + +$csaf->product_tree->full_product_names->add(name => 'Product A', product_id => 'CSAFPID-9080700'); +$csaf->product_tree->full_product_names->add(name => 'Product B', product_id => 'CSAFPID-9080701'); + +my $vulns = $csaf->vulnerabilities; +my $vuln = $vulns->add(cve => 'CVE-2023-00000'); + +$vuln->remediations->add(product_ids => ['CSAFPID-9080701'], category => 'test category', details => 'test details',); + +$vuln->product_status->last_affected(['CSAFPID-9080700']); + +exec_validator_optional_test($csaf, '6.2.2'); + +done_testing; diff --git a/t/10-optional-6.2.3.t b/t/10-optional-6.2.3.t new file mode 100644 index 0000000..5248c78 --- /dev/null +++ b/t/10-optional-6.2.3.t @@ -0,0 +1,57 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use Test::CSAF qw(base_csaf_security_advisory exec_validator_optional_test); + +# 6.2.3 Missing Score + +# For each Product ID (type /$defs/product_id_t) in the Product Status groups Affected it MUST be tested that a score object exists which +# covers this product. + +# The relevant paths for this test are: +# /vulnerabilities[]/product_status/first_affected[] +# /vulnerabilities[]/product_status/known_affected[] +# /vulnerabilities[]/product_status/last_affected[] + +# Fail test: + +# "product_tree": { +# "full_product_names": [ +# { +# "product_id": "CSAFPID-9080700", +# "name": "Product A" +# } +# ] +# }, +# "vulnerabilities": [ +# { +# "product_status": { +# "first_affected": [ +# "CSAFPID-9080700" +# ] +# } +# } +# ] + +my $csaf = base_csaf_security_advisory(); + +$csaf->product_tree->full_product_names->add(name => 'Product A', product_id => 'CSAFPID-9080700'); +$csaf->product_tree->full_product_names->add(name => 'Product B', product_id => 'CSAFPID-9080701'); + +my $vulns = $csaf->vulnerabilities; +my $vuln = $vulns->add(cve => 'CVE-2023-00000'); + +$vuln->scores->add(products => ['CSAFPID-9080701']); + +$vuln->product_status->first_affected(['CSAFPID-9080700']); + +exec_validator_optional_test($csaf, '6.2.3'); + +done_testing; diff --git a/t/10-optional-6.2.4.t b/t/10-optional-6.2.4.t new file mode 100644 index 0000000..8db1ebf --- /dev/null +++ b/t/10-optional-6.2.4.t @@ -0,0 +1,60 @@ +#!perl + +use 5.010001; +use strict; +use warnings; +use Test::More; + +use FindBin '$RealBin'; +use lib "$RealBin/lib"; + +use CSAF; +use Test::CSAF qw(exec_validator_optional_test); + +# 6.1.18 Released Revision History + +# It MUST be tested that no item of the revision history has a number of 0 or 0.y.z when the document status is final or interim. + +# The relevant path for this test is: + +# /document/tracking/revision_history[]/number + +# Fail test: + +# "tracking": { +# // ... +# "revision_history": [ +# { +# "date": "2021-05-17T10:00:00.000Z", +# "number": "0", +# "summary": "First draft" +# }, +# { +# "date": "2021-07-21T10:00:00.000Z", +# "number": "1", +# "summary": "Initial version." +# } +# ], +# "status": "final", +# "version": "1" +# } + +my $csaf = CSAF->new; + +$csaf->document->title('Base CSAF Document'); +$csaf->document->category('csaf_security_advisory'); +$csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + +my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1', + initial_release_date => 'now', + current_release_date => 'now' +); + +$tracking->revision_history->add(date => 'now', summary => 'Initial version.', number => '1.0.0+exp.sha.ac00785"'); + +exec_validator_optional_test($csaf, '6.2.4'); + +done_testing; diff --git a/t/lib/Test/CSAF.pm b/t/lib/Test/CSAF.pm new file mode 100644 index 0000000..b7ba9ee --- /dev/null +++ b/t/lib/Test/CSAF.pm @@ -0,0 +1,74 @@ +package Test::CSAF; + +use 5.010001; +use strict; +use warnings; + +use Exporter 'import'; + +use CSAF; +use CSAF::Validator::MandatoryTests; +use CSAF::Validator::OptionalTests; +use Test::More; + +our @EXPORT_OK = qw(base_csaf_security_advisory exec_validator_mandatory_test exec_validator_optional_test); + +sub base_csaf_security_advisory { + + my $csaf = CSAF->new; + + $csaf->document->title('Base CSAF Document'); + $csaf->document->category('csaf_security_advisory'); + $csaf->document->publisher(category => 'vendor', name => 'CSAF', namespace => 'https://csaf.io'); + + my $tracking = $csaf->document->tracking( + id => 'CSAF:2023-001', + status => 'final', + version => '1.0.0', + initial_release_date => 'now', + current_release_date => 'now' + ); + + $tracking->revision_history->add(date => 'now', summary => 'First release', number => '1'); + + return $csaf; + +} + +sub exec_validator_optional_test { + + my ($csaf, $test_id) = @_; + + my $v = CSAF::Validator::OptionalTests->new($csaf); + $v->exec_test($test_id); + + foreach my $message (@{$v->messages}) { + + is($message->code, $test_id, "Message code: $test_id"); + isa_ok($message, 'CSAF::Validator::Message'); + + diag($message); + + } + +} + +sub exec_validator_mandatory_test { + + my ($csaf, $test_id) = @_; + + my $v = CSAF::Validator::MandatoryTests->new($csaf); + $v->exec_test($test_id); + + foreach my $message (@{$v->messages}) { + + is($message->code, $test_id, "Message code: $test_id"); + isa_ok($message, 'CSAF::Validator::Message'); + + diag($message); + + } + +} + +1; diff --git a/t/manifest.t b/t/manifest.t new file mode 100644 index 0000000..672984c --- /dev/null +++ b/t/manifest.t @@ -0,0 +1,15 @@ +#!perl -T + +use strict; +use warnings; +use Test::More; + +unless ($ENV{RELEASE_TESTING}) { + plan(skip_all => "Author tests not required for installation"); +} + +my $min_tcm = 0.9; +eval "use Test::CheckManifest $min_tcm"; +plan skip_all => "Test::CheckManifest $min_tcm required" if $@; + +ok_manifest(); diff --git a/t/pod-coverage.t b/t/pod-coverage.t new file mode 100644 index 0000000..1bdfef7 --- /dev/null +++ b/t/pod-coverage.t @@ -0,0 +1,22 @@ +#!perl -T + +use strict; +use warnings; +use Test::More; + +unless ($ENV{RELEASE_TESTING}) { + plan(skip_all => "Author tests not required for installation"); +} + +# Ensure a recent version of Test::Pod::Coverage +my $min_tpc = 1.08; +eval "use Test::Pod::Coverage $min_tpc"; +plan skip_all => "Test::Pod::Coverage $min_tpc required for testing POD coverage" if $@; + +# Test::Pod::Coverage doesn't require a minimum Pod::Coverage version, +# but older versions don't recognize some common documentation styles +my $min_pc = 0.18; +eval "use Pod::Coverage $min_pc"; +plan skip_all => "Pod::Coverage $min_pc required for testing POD coverage" if $@; + +all_pod_coverage_ok(); diff --git a/t/pod.t b/t/pod.t new file mode 100644 index 0000000..b9da5b6 --- /dev/null +++ b/t/pod.t @@ -0,0 +1,16 @@ +#!perl -T + +use strict; +use warnings; +use Test::More; + +unless ($ENV{RELEASE_TESTING}) { + plan(skip_all => "Author tests not required for installation"); +} + +# Ensure a recent version of Test::Pod +my $min_tp = 1.22; +eval "use Test::Pod $min_tp"; +plan skip_all => "Test::Pod $min_tp required for testing POD" if $@; + +all_pod_files_ok();