[Rt-commit] rtir branch 5.0/cve-info created. 5.0.1-46-g90119c4e

BPS Git Server git at git.bestpractical.com
Wed Dec 15 22:15:26 UTC 2021


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rtir".

The branch, 5.0/cve-info has been created
        at  90119c4ec4e4a9b4ab35a883dec9e76f9c4c476d (commit)

- Log -----------------------------------------------------------------
commit 90119c4ec4e4a9b4ab35a883dec9e76f9c4c476d
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Thu Dec 16 05:55:25 2021 +0800

    Test CVE ID extraction

diff --git a/t/custom-fields/cve.t b/t/custom-fields/cve.t
new file mode 100644
index 00000000..95d77b00
--- /dev/null
+++ b/t/custom-fields/cve.t
@@ -0,0 +1,257 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use RT::IR::Test tests => undef;
+
+RT::Test->started_ok;
+my $agent = default_agent();
+
+my $cf;
+diag "load and check basic properties of the CVE ID CF";
+{
+    my $cfs = RT::CustomFields->new( $RT::SystemUser );
+    $cfs->Limit( FIELD => 'Name', VALUE => 'CVE ID', CASESENSITIVE => 0 );
+    is( $cfs->Count, 1, "found one CF with name 'CVE ID'" );
+
+    $cf = $cfs->First;
+    is( $cf->Type,       'Freeform',             'type check' );
+    is( $cf->LookupType, 'RT::Queue-RT::Ticket', 'lookup type check' );
+    ok( !$cf->MaxValues, "unlimited number of values" );
+    ok( !$cf->Disabled,  "not disabled" );
+}
+
+diag "check that CF applies to all RTIR's queues";
+{
+    foreach ( 'Incidents', 'Incident Reports', 'Investigations', 'Countermeasures' ) {
+        my $queue = RT::Queue->new( $RT::SystemUser );
+        $queue->Load( $_ );
+        ok( $queue->id, 'loaded queue ' . $_ );
+        my $cfs = $queue->TicketCustomFields;
+        $cfs->Limit( FIELD => 'id', VALUE => $cf->id, ENTRYAGGREGATOR => 'AND' );
+        is( $cfs->Count, 1, 'field applies to queue' );
+    }
+}
+
+my $rtir_user = RT::CurrentUser->new( rtir_user() );
+
+diag "create a ticket via web and set CVE ID";
+{
+    my $i = 0;
+    my $incident_id;    # countermeasure couldn't be created without incident id
+    foreach my $queue ( 'Incidents', 'Incident Reports', 'Investigations', 'Countermeasures' ) {
+        diag "create a ticket in the '$queue' queue";
+
+        my $val = 'CVE-2021-' . sprintf '%04d', ++$i;
+        my $id  = $agent->create_rtir_ticket_ok(
+            $queue,
+            { Subject => "test CVE ID", ( $queue eq 'Countermeasures' ? ( Incident => $incident_id ) : () ), },
+            { 'CVE ID' => $val },
+        );
+        $incident_id = $id if $queue eq 'Incidents';
+
+        $agent->content_like( qr/\Q$val/, "CVE ID on the page" );
+
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        $ticket->Load( $id );
+        ok( $ticket->id, 'loaded ticket' );
+        is( $ticket->FirstCustomFieldValue( 'CVE ID' ), $val, 'correct value' );
+    }
+}
+
+diag "create a ticket via web with CVE ID in message";
+{
+    my $i = 0;
+    my $incident_id;    # countermeasure couldn't be created without incident id
+    foreach my $queue ( 'Incidents', 'Incident Reports', 'Investigations', 'Countermeasures' ) {
+        diag "create a ticket in the '$queue' queue";
+
+        my $val = 'CVE-2021-' . sprintf '%04d', ++$i;
+        my $id  = $agent->create_rtir_ticket_ok(
+            $queue,
+            {
+                Subject => "test CVE ID in message",
+                ( $queue eq 'Countermeasures' ? ( Incident => $incident_id ) : () ), Content => "$val",
+            },
+        );
+        $incident_id = $id if $queue eq 'Incidents';
+
+        $agent->content_like( qr/\Q$val/, "CVE ID on the page" );
+
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        $ticket->Load( $id );
+        ok( $ticket->id, 'loaded ticket' );
+        is( $ticket->FirstCustomFieldValue( 'CVE ID' ), $val, 'correct value' );
+    }
+}
+
+diag "create a ticket and edit CVE ID field using Edit page";
+{
+    my $i = 0;
+    my $incident_id;    # countermeasure couldn't be created without incident id
+    foreach my $queue ( 'Incidents', 'Incident Reports', 'Investigations', 'Countermeasures' ) {
+        diag "create a ticket in the '$queue' queue";
+
+        my $id = $agent->create_rtir_ticket_ok(
+            $queue,
+            {
+                Subject => "test CVE ID in message",
+                ( $queue eq 'Countermeasures' ? ( Incident => $incident_id ) : () ),
+            },
+        );
+        $incident_id = $id if $queue eq 'Incidents';
+
+        my $field_name = "Object-RT::Ticket-$id-CustomField-" . $cf->id . "-Values";
+
+        diag "set CVE ID";
+        my $val = 'CVE-2021-1234';
+        $agent->follow_link_ok( { text => 'Edit', n => "1" }, "Followed 'Edit' link" );
+        $agent->form_number( 3 );
+        like( $agent->value( $field_name ), qr/^\s*$/, 'CVE ID is empty' );
+        $agent->field( $field_name => $val );
+        $agent->click( 'SaveChanges' );
+
+        $agent->content_like( qr/$val/, "CVE ID on the page" );
+
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        $ticket->Load( $id );
+        ok( $ticket->id, 'loaded ticket' );
+        my $values = $ticket->CustomFieldValues( 'CVE ID' );
+        my %has = map { $_->Content => 1 } @{ $values->ItemsArrayRef };
+        is( scalar values %has, 1, "one CVE ID were added" );
+        ok( $has{$val}, "has value" ) or diag "but has values " . join ", ", keys %has;
+
+        diag "set CVE ID with spaces around";
+        $val = "  CVE-2021-1234  \n  ";
+        $agent->follow_link_ok( { text => 'Edit', n => "1" }, "Followed 'Edit' link" );
+        $agent->form_number( 3 );
+        like( $agent->value( $field_name ), qr/^\s*CVE-2021-1234\s*$/, 'CVE ID is in input box' );
+        $agent->field( $field_name => $val );
+        $agent->click( 'SaveChanges' );
+
+        $agent->content_like( qr/CVE-2021-1234/, "CVE ID on the page" );
+
+        $ticket = RT::Ticket->new( $RT::SystemUser );
+        $ticket->Load( $id );
+        ok( $ticket->id, 'loaded ticket' );
+        $values = $ticket->CustomFieldValues( 'CVE ID' );
+        %has = map { $_->Content => 1 } @{ $values->ItemsArrayRef };
+        is( scalar values %has, 1, "one CVE ID were added" );
+        ok( $has{'CVE-2021-1234'}, "has value" ) or diag "but has values " . join ", ", keys %has;
+    }
+}
+
+diag "check that CVE IDs in messages don't add duplicates";
+{
+    my $id = $agent->create_ir( { Subject => "test CVE ID", Content => 'CVE-2021-1234 CVE-2021-1234' } );
+    ok( $id, "created first ticket" );
+
+    my $ticket = RT::Ticket->new( $RT::SystemUser );
+    $ticket->Load( $id );
+    ok( $ticket->id, 'loaded ticket' );
+
+    my $values = $ticket->CustomFieldValues( 'CVE ID' );
+    my %has;
+    $has{ $_->Content }++ foreach @{ $values->ItemsArrayRef };
+    is( scalar values %has, 1, "one CVE ID were added" );
+    ok( !grep( $_ != 1, values %has ), "no duplicated values" );
+    ok( $has{'CVE-2021-1234'}, "CVE ID is there" );
+}
+
+diag "search tickets by CVE ID";
+{
+    my $id = $agent->create_ir( { Subject => "test CVE ID", Content => 'CVE-2021-1234' } );
+    ok( $id, "created first ticket" );
+
+    my $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL( "id = $id AND CF.{CVE ID} = 'CVE-2021-1234'" );
+    ok( $tickets->Count, "found tickets" );
+
+    my $flag = 1;
+    while ( my $ticket = $tickets->Next ) {
+        my %has = map { $_->Content => 1 } @{ $ticket->CustomFieldValues( 'CVE ID' )->ItemsArrayRef };
+        next if $has{'CVE-2021-1234'};
+        $flag = 0;
+        ok( 0, "ticket #" . $ticket->id . " has no CVE ID CVE-2021-1234, but should" )
+          or diag "but has values " . join ", ", keys %has;
+        last;
+    }
+    ok( 1, "all tickets has CVE ID CVE-2021-1234" ) if $flag;
+}
+
+diag "merge ticket, CVE IDs should be merged";
+{
+    my $incident_id = $agent->create_rtir_ticket_ok( 'Incidents', { Subject => "test" }, );
+    my $b1_id = $agent->create_countermeasure(
+        { Subject => "test CVE ID", Incident => $incident_id, },
+        { 'CVE ID'  => 'CVE-2021-1234' },
+    );
+    my $b2_id = $agent->create_countermeasure(
+        { Subject => "test CVE ID", Incident => $incident_id, },
+        { 'CVE ID'  => 'CVE-2021-5678' },
+    );
+
+    $agent->display_ticket( $b1_id );
+    $agent->follow_link_ok( { text => 'Merge' }, "Followed merge link" );
+    $agent->form_number( 3 );
+    $agent->field( 'SelectedTicket', $b2_id );
+    $agent->submit;
+    $agent->ok_and_content_like( qr{Merge Successful}, 'Merge Successful' );
+
+    my $ticket = RT::Ticket->new( $RT::SystemUser );
+    $ticket->Load( $b1_id );
+    ok $ticket->id, 'loaded ticket';
+    my $values = $ticket->CustomFieldValues( 'CVE ID' );
+    my %has = map { $_->Content => 1 } @{ $values->ItemsArrayRef };
+    is( scalar values %has, 2, "both CVE IDs are there" );
+    ok( $has{'CVE-2021-1234'}, "has value" ) or diag "but has values " . join ", ", keys %has;
+    ok( $has{'CVE-2021-5678'},  "has value" ) or diag "but has values " . join ", ", keys %has;
+}
+
+diag "merge ticket with the same CVE ID";
+{
+    my $incident_id = $agent->create_rtir_ticket_ok( 'Incidents', { Subject => "test" }, );
+    my $b1_id = $agent->create_countermeasure(
+        { Subject => "test CVE ID", Incident => $incident_id, },
+        { 'CVE ID'  => 'CVE-2021-12345' },
+    );
+    my $b2_id = $agent->create_countermeasure(
+        { Subject => "test CVE ID", Incident => $incident_id, },
+        { 'CVE ID'  => 'CVE-2021-12345' },
+    );
+
+    $agent->display_ticket( $b1_id );
+    $agent->follow_link_ok( { text => 'Merge' }, "Followed merge link" );
+    $agent->form_number( 3 );
+    $agent->field( 'SelectedTicket', $b2_id );
+    $agent->submit;
+    $agent->ok_and_content_like( qr{Merge Successful}, 'Merge Successful' );
+
+    my $ticket = RT::Ticket->new( $RT::SystemUser );
+    $ticket->Load( $b1_id );
+    ok $ticket->id, 'loaded ticket';
+    my $values = $ticket->CustomFieldValues( 'CVE ID' );
+    my @has = map $_->Content, @{ $values->ItemsArrayRef };
+    is( scalar @has, 1, "only one CVE ID" ) or diag "values: @has";
+    is( $has[ 0 ], 'CVE-2021-12345', "has value" );
+}
+
+diag "test various invalid CVE IDs";
+{
+    my @invalid_cves = (
+        'cve-2021-',
+        'cve-20-3',
+        'cve-2021-123',
+    );
+
+    for my $cve ( @invalid_cves ) {
+        my $id = $agent->create_rtir_ticket_ok( 'Incident Reports', { Subject => "test", Content => $cve }, );
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        $ticket->Load( $id );
+        ok( $ticket->id,                                 'loaded ticket' );
+        ok( !$ticket->FirstCustomFieldValue( 'CVE ID' ), "Inalid CVE ID $cve is not defined" );
+    }
+}
+
+done_testing;

commit 7725274a260f32c80931cc8c25828221940eb569
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Thu Dec 16 04:46:55 2021 +0800

    Extract CVE IDs from content
    
    It's quite like how we extract IPs and Domains.

diff --git a/etc/initialdata b/etc/initialdata
index 707f3e70..1b720a2d 100644
--- a/etc/initialdata
+++ b/etc/initialdata
@@ -235,6 +235,14 @@ die "Please add RT::IR to your Plugins configuration before initializing the dat
         Description => 'Merge multiple Domains on ticket merge',             # loc
         ExecModule  => 'RTIR_MergeDomains',
     },
+    {   Name        => 'RTIR parse message for CVEs',                 # loc
+        Description => 'Set CVE custom field from message content',   # loc
+        ExecModule  => 'RTIR_FindCVE',
+    },
+    {   Name        => 'RTIR merge CVEs',                                 # loc
+        Description => 'Merge multiple CVEs on ticket merge',             # loc
+        ExecModule  => 'RTIR_MergeCVEs',
+    },
 );
 
 @ScripConditions = (
@@ -425,6 +433,33 @@ die "Please add RT::IR to your Plugins configuration before initializing the dat
         Template       => 'Blank'
     },
 
+    {
+        Description    => "SetCVEFromContent",
+        Queue          => [ 'Incidents', 'Incident Reports', 'Investigations', 'Countermeasures' ],
+        ScripCondition => 'On Correspond',
+        ScripAction    => 'RTIR parse message for CVEs',
+        Template       => 'Blank'
+    },
+    {
+        Description    => "SetCVEFromContent",
+        Queue          => [ 'Incidents', 'Incident Reports', 'Investigations', 'Countermeasures' ],
+        ScripCondition => 'On Create',
+        ScripAction    => 'RTIR parse message for CVEs',
+        Template       => 'Blank'
+    },
+    {   Description => "MergeCVEs",
+        Queue =>
+            [ 'Incidents', 'Incident Reports', 'Investigations', 'Countermeasures' ],
+        ScripCondition => 'RTIR Merge',
+        ScripAction    => 'RTIR merge CVEs',
+        Template       => 'Blank'
+    },
+    {   Description    => "On Linking To Incident Copy CVEs",
+        Queue          => 'Incident Reports',
+        ScripCondition => 'RTIR Linking To Incident',
+        ScripAction    => 'RTIR merge CVEs',
+        Template       => 'Blank'
+    },
 );
 
 # WARNING: If you change content of the templates, don't forget to
diff --git a/etc/upgrade/5.0.3/content b/etc/upgrade/5.0.3/content
index f76bde45..90396f86 100644
--- a/etc/upgrade/5.0.3/content
+++ b/etc/upgrade/5.0.3/content
@@ -11,4 +11,45 @@ our @CustomFields = (
     },
 );
 
+our @ScripActions = (
+    {   Name        => 'RTIR parse message for CVEs',                 # loc
+        Description => 'Set CVE custom field from message content',   # loc
+        ExecModule  => 'RTIR_FindCVE',
+    },
+    {   Name        => 'RTIR merge CVEs',                                 # loc
+        Description => 'Merge multiple CVEs on ticket merge',             # loc
+        ExecModule  => 'RTIR_MergeCVEs',
+    },
+);
+
+our @Scrips = (
+    {
+        Description    => "SetCVEFromContent",
+        Queue          => [ 'Incidents', 'Incident Reports', 'Investigations', 'Countermeasures' ],
+        ScripCondition => 'On Correspond',
+        ScripAction    => 'RTIR parse message for CVEs',
+        Template       => 'Blank'
+    },
+    {
+        Description    => "SetCVEFromContent",
+        Queue          => [ 'Incidents', 'Incident Reports', 'Investigations', 'Countermeasures' ],
+        ScripCondition => 'On Create',
+        ScripAction    => 'RTIR parse message for CVEs',
+        Template       => 'Blank'
+    },
+    {   Description => "MergeCVEs",
+        Queue =>
+            [ 'Incidents', 'Incident Reports', 'Investigations', 'Countermeasures' ],
+        ScripCondition => 'RTIR Merge',
+        ScripAction    => 'RTIR merge CVEs',
+        Template       => 'Blank'
+    },
+    {   Description    => "On Linking To Incident Copy CVEs",
+        Queue          => 'Incident Reports',
+        ScripCondition => 'RTIR Linking To Incident',
+        ScripAction    => 'RTIR merge CVEs',
+        Template       => 'Blank'
+    },
+);
+
 1;
diff --git a/lib/RT/Action/RTIR_FindCVE.pm b/lib/RT/Action/RTIR_FindCVE.pm
new file mode 100644
index 00000000..75f670ac
--- /dev/null
+++ b/lib/RT/Action/RTIR_FindCVE.pm
@@ -0,0 +1,111 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2021 Best Practical Solutions, LLC
+#                                          <sales at bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+
+use strict;
+use warnings;
+
+package RT::Action::RTIR_FindCVE;
+use base qw(RT::Action::RTIR);
+
+=head2 Commit
+
+Search for CVEs in the transaction's content.
+
+=cut
+
+sub Commit {
+    my $self   = shift;
+    my $ticket = $self->TicketObj;
+
+    my $cf = $ticket->LoadCustomFieldByIdentifier('CVE ID');
+    return 1 unless $cf && $cf->id;
+
+    my $attach = $self->TransactionObj->ContentObj;
+    return 1 unless $attach && $attach->id;
+
+    my %existing;
+    for ( @{ $cf->ValuesForObject($ticket)->ItemsArrayRef } ) {
+        $existing{ $_->Content } = 1;
+    }
+
+    my $how_many_can = $cf->MaxValues;
+    if ( $how_many_can && $how_many_can <= keys %existing ) {
+        RT->Logger->debug( "Ticket #" . $ticket->id . " already has maximum number of CVEs, skipping" );
+        return 1;
+    }
+
+    my $content = $attach->Content || '';
+    while ( $content =~ m/\b(CVE-\d{4}-\d{4,})/igo ) {
+        my $CVE = $1;
+        $self->AddCVE(
+            CVE         => $CVE,
+            CustomField => $cf,
+            Skip        => \%existing,
+        );
+    }
+
+    return 1;
+}
+
+sub AddCVE {
+    my $self = shift;
+    my %arg  = ( CustomField => undef, CVE => undef, Skip => {}, @_ );
+    return 0 if !$arg{'CVE'} || $arg{'Skip'}->{ $arg{'CVE'} }++;
+
+    my ( $status, $msg ) = $self->TicketObj->AddCustomFieldValue(
+        Value => $arg{'CVE'},
+        Field => $arg{'CustomField'},
+    );
+    RT->Logger->error("Couldn't add CVE: $msg") unless $status;
+
+    return 1;
+}
+
+RT::IR->ImportOverlays;
+
+1;
diff --git a/lib/RT/Action/RTIR_MergeCVEs.pm b/lib/RT/Action/RTIR_MergeCVEs.pm
new file mode 100644
index 00000000..b9e10655
--- /dev/null
+++ b/lib/RT/Action/RTIR_MergeCVEs.pm
@@ -0,0 +1,85 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2021 Best Practical Solutions, LLC
+#                                          <sales at bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+
+package RT::Action::RTIR_MergeCVEs;
+use strict;
+use warnings;
+use base 'RT::Action::RTIR';
+
+=head2 Commit
+
+Copy CVEs from one ticket to another
+
+=cut
+
+sub Commit {
+    my $self = shift;
+
+    my $txn = $self->TransactionObj;
+    my $uri = $txn->NewValue or return 1;
+
+    my $uri_obj = RT::URI->new( $self->CurrentUser );
+    my ($status) = $uri_obj->FromURI( $uri );
+    unless ( $status && $uri_obj->Resolver && $uri_obj->Scheme ) {
+        RT->Logger->error( "Couldn't resolve '$uri' into a URI." );
+        return 1;
+    }
+
+    my $target = $uri_obj->Object;
+    return 1 if $target->id eq $txn->ObjectId;
+
+    my $source = RT::Ticket->new( $self->CurrentUser );
+    $source->LoadById( $txn->ObjectId );
+
+    return $self->CopyCustomFields( To => $target, From => $source, CF => 'CVE ID' );
+
+}
+
+RT::IR->ImportOverlays;
+
+1;

commit d13c64717300c3d7205cad51449e6cf5d3ca0339
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Sat Oct 23 02:34:03 2021 +0800

    Add CVE widget to show info from nvd.nist.gov

diff --git a/html/RTIR/Display.html b/html/RTIR/Display.html
index aa7c92e5..a0f0165d 100644
--- a/html/RTIR/Display.html
+++ b/html/RTIR/Display.html
@@ -228,6 +228,8 @@
 
 <& /RTIR/Elements/ShowArticles, Ticket => $Ticket &>
 
+<& /RTIR/Elements/ShowCVEDetails, Ticket => $Ticket &>
+
 <& /Ticket/Elements/ShowAttachments, Ticket => $Ticket,
    Attachments => $attachments &>
 
diff --git a/html/RTIR/Elements/ShowCVEDetails b/html/RTIR/Elements/ShowCVEDetails
new file mode 100644
index 00000000..20afce31
--- /dev/null
+++ b/html/RTIR/Elements/ShowCVEDetails
@@ -0,0 +1,133 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2021 Best Practical Solutions, LLC
+%#                                          <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<&| /Widgets/TitleBox,
+    title          => loc("CVE Details"),
+    title_class    => 'inverse',
+    class          => 'ticket-info-cve',
+&>
+
+<div id="cve-accordion" class="rt-accordion accordion">
+
+% while ( my $cve = $cves->Next ) {
+  <div class="accordion-item">
+    <span class="accordion-title collapsed toggle" data-toggle="collapse"
+      data-target="#accordion-cve-<% $cve->Content %>"
+      aria-expanded="false" aria-controls="accordion-cve-<% $cve->Content %>"
+      id="accordion-cve-<% $cve->Content %>-title">
+      <% $cve->Content %>
+    </span>
+    <div id="accordion-cve-<% $cve->Content %>" class="accordion-content collapse"
+      data-cve-id="<% $cve->Content %>" aria-labelledby="accordion-cve-<% $cve->Content %>-title">
+%     for my $item ( qw/description cvss-3x-severity published-date last-modified-date more-info/ ) {
+      <div class="form-row cve-<% $item %>">
+        <div class="col-3 label"><% loc($label{$item}) %>:</div>
+        <div class="col-9 value">
+          <span class="current-value">
+%         if ( $item eq 'more-info' ) {
+            <a href="https://nvd.nist.gov/vuln/detail/<% $cve->Content %>#vulnCurrentDescriptionTitle" target="_blank"><% loc('NIST CVE Detail') %></a>
+% }
+          </span>
+        </div>
+      </div>
+%     }
+    </div>
+  </div>
+% }
+
+</div>
+
+</&>
+
+<script type="text/javascript">
+jQuery( function() {
+    jQuery('.ticket-info-cve div[data-cve-id]').each(function() {
+      var div = jQuery(this);
+      var cve_id = div.data('cve-id');
+      div.find('.current-value:empty').text(RT.I18N.Catalog.loading);
+      jQuery.get("https://services.nvd.nist.gov/rest/json/cve/1.0/" + cve_id, function(data) {
+          if ( data.result && data.result.CVE_Items && data.result.CVE_Items[0] ) {
+              var info = data.result.CVE_Items[0];
+              div.find('.cve-published-date .current-value').text(info.publishedDate);
+              div.find('.cve-last-modified-date .current-value').text(info.lastModifiedDate);
+
+              jQuery.each(info.cve.description.description_data, function(index, value) {
+                  if ( value.lang == 'en' ) {
+                      div.find('.cve-description .current-value').text(value.value);
+                      return false;
+                  }
+              });
+
+              if ( info.impact && info.impact.baseMetricV3 && info.impact.baseMetricV3.cvssV3 ) {
+                  var v3 = info.impact.baseMetricV3.cvssV3;
+                  div.find('.cve-cvss-3x-severity .current-value').text(v3.baseScore + ' ' + v3.baseSeverity);
+              }
+          }
+      }, 'json').fail( function(xhr) {
+          jQuery('<p class="mt-3 mb-1 ml-3 text-danger">').text(xhr.responseJSON.message).insertBefore(div.find('.form-row:first'));
+          div.find('.form-row').hide();
+      });
+    });
+
+});
+</script>
+<%ARGS>
+$Ticket
+</%ARGS>
+<%INIT>
+my $cves = $Ticket->CustomFieldValues('CVE ID');
+
+return unless $cves->Count;
+
+my %label = (
+    'published-date'     => loc('NVD Published Date'),
+    'last-modified-date' => loc('NVD Last Modified'),
+    'cvss-3x-severity'   => loc('CVSS 3.x Severity'),
+    'description'        => loc('Description'),
+    'more-info'          => loc('More Info'),
+);
+</%INIT>
diff --git a/html/RTIR/Incident/Display.html b/html/RTIR/Incident/Display.html
index c6e697d7..6e10f6d6 100644
--- a/html/RTIR/Incident/Display.html
+++ b/html/RTIR/Incident/Display.html
@@ -302,6 +302,9 @@
 &>
 
 <& /RTIR/Elements/ShowArticles, Ticket => $TicketObj &>
+
+<& /RTIR/Elements/ShowCVEDetails, Ticket => $TicketObj &>
+
 % $m->callback( %ARGS, Ticket => $TicketObj, CallbackName => 'RightColumnEnd' );
   </div>
 </div>
diff --git a/static/css/rtir-styles.css b/static/css/rtir-styles.css
index 6f15c9aa..6f5bcfde 100644
--- a/static/css/rtir-styles.css
+++ b/static/css/rtir-styles.css
@@ -76,6 +76,7 @@ body.rtir .titlebox.tickets-list-investigation, body.rtir .titlebox.tickets-list
 body.rtir .titlebox.ticket-info-time { border-top: 3px solid #7B1FA2; }
 body.rtir .titlebox.ticket-info-message { border-top: 3px solid #1976D2; }
 body.rtir .titlebox.ticket-info-details { border-top: 3px solid #D32F2F; }
+body.rtir .titlebox.ticket-info-cve { border-top: 3px solid #1574b3; } /* The color is from header of https://nvd.nist.gov/ */
 
 body.rtir #comp-RTIR-Search #body {
     position: relative;

commit c71b7b35d85b9fb8e69e392132c633e5a37350a6
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Fri Oct 15 13:47:16 2021 +0800

    Add Custom Field "CVE ID" to keep track of CVE

diff --git a/etc/initialdata b/etc/initialdata
index 98f9398d..707f3e70 100644
--- a/etc/initialdata
+++ b/etc/initialdata
@@ -87,6 +87,15 @@ die "Please add RT::IR to your Plugins configuration before initializing the dat
             { Name => "Piracy",            SortOrder => 6 },
         ],
     },
+    {
+        Name        => 'CVE ID',
+        Type        => 'FreeformMultiple',
+        Queue       => [ 'Incidents', 'Incident Reports', 'Investigations', 'Countermeasures' ],
+        Disabled    => 0,
+        Description => 'CVE ID for RTIR queues',
+        LinkValueTo => 'https://nvd.nist.gov/vuln/detail/__CustomField__#vulnCurrentDescriptionTitle',
+    },
+
     {   Name       => 'How Reported',
         Type       => 'SelectSingle',
         RenderType => 'Dropdown',
diff --git a/etc/upgrade/5.0.3/content b/etc/upgrade/5.0.3/content
new file mode 100644
index 00000000..f76bde45
--- /dev/null
+++ b/etc/upgrade/5.0.3/content
@@ -0,0 +1,14 @@
+use strict;
+use warnings;
+
+our @CustomFields = (
+    {   Name        => 'CVE ID',
+        Type        => 'FreeformMultiple',
+        Queue       => [ 'Incidents', 'Incident Reports', 'Investigations', 'Countermeasures' ],
+        Disabled    => 0,
+        Description => 'CVE ID for RTIR queues',
+        LinkValueTo => 'https://nvd.nist.gov/vuln/detail/__CustomField__#vulnCurrentDescriptionTitle',
+    },
+);
+
+1;

-----------------------------------------------------------------------


hooks/post-receive
-- 
rtir


More information about the rt-commit mailing list