[Rt-commit] rtir branch 5.0-trunk updated. 5.0.1-57-g4bbd878e

BPS Git Server git at git.bestpractical.com
Thu Dec 23 20:08:34 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-trunk has been updated
       via  4bbd878e59f0fc2ceedc6f4e63fd8a1edea63e05 (commit)
       via  9fc324dabcfa517e15eed869eecb4c2224d18cbf (commit)
       via  fb918c99681314cafaaef115deea4fb87f2ccd8f (commit)
       via  d41f7e34c8fef4ff13a6b23b24c1bc29b08cba62 (commit)
       via  f871ba6c45ee0800bebbe378435b67a2624f8952 (commit)
      from  0436ccd41ca99c7f4a2f6aff38f7e07eae1da6bd (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 4bbd878e59f0fc2ceedc6f4e63fd8a1edea63e05
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Thu Dec 23 14:14:51 2021 -0500

    Add upgrading notes for CVE ID

diff --git a/docs/UPGRADING-5.0 b/docs/UPGRADING-5.0
index 94a02e88..0afdd92f 100644
--- a/docs/UPGRADING-5.0
+++ b/docs/UPGRADING-5.0
@@ -192,6 +192,19 @@ class, which is the default for RTIR. This should make the Response CF
 work as intended. If you are using the Content CF in some way, you
 can edit the Templates class configuration and enable it again.
 
+=item * CVE ID Custom Field Added
+
+A new default custom field called C<CVE ID> is added with this upgrade.
+On Incidents with a CVE defined, some information about the CVE from
+the NIST vulnerability system is displayed on the page with a link
+to the full description on the NIST website.
+
+CVE IDs can also now be parsed and automatically added to this
+custom field from incoming email, similar to IP addresses.
+
+If you don't track CVEs with your RTIR instance, you can disable
+the custom field and the new scrips.
+
 =back
 
 =cut

commit 9fc324dabcfa517e15eed869eecb4c2224d18cbf
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..22d97245
--- /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:Details-" . $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 fb918c99681314cafaaef115deea4fb87f2ccd8f
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.2/content b/etc/upgrade/5.0.2/content
index b45bdc4d..a50de0e1 100644
--- a/etc/upgrade/5.0.2/content
+++ b/etc/upgrade/5.0.2/content
@@ -13,6 +13,47 @@ 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'
+    },
+);
+
 our @Final = (
     sub {
         RT->Logger->debug("Converting homepages to dashboards");
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 d41f7e34c8fef4ff13a6b23b24c1bc29b08cba62
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 ba58ad6a..123cce37 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 72f8a7ec..175c3286 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 f871ba6c45ee0800bebbe378435b67a2624f8952
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/RTIR_Config.pm b/etc/RTIR_Config.pm
index 7c148dbf..69dde24f 100644
--- a/etc/RTIR_Config.pm
+++ b/etc/RTIR_Config.pm
@@ -684,7 +684,7 @@ Set(%CustomFieldGroupings,
     'RTIR::Ticket' => [
         'Networking'     => ['IP', 'Domain'],
         'Details' => ['How Reported','Reporter Type','Customer',
-                      'Description', 'Resolution', 'Function', 'Classification',
+                      'Description', 'Resolution', 'Function', 'Classification', 'CVE ID',
                       'Customer',
                       'Netmask','Port','Where Blocked'],
     ],
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.2/content b/etc/upgrade/5.0.2/content
index 7da0ec35..b45bdc4d 100644
--- a/etc/upgrade/5.0.2/content
+++ b/etc/upgrade/5.0.2/content
@@ -3,6 +3,16 @@ use warnings;
 
 our @ScripConditions;
 
+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',
+    },
+);
+
 our @Final = (
     sub {
         RT->Logger->debug("Converting homepages to dashboards");
@@ -157,3 +167,5 @@ our @Final = (
         }
     },
 );
+
+1;

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

Summary of changes:
 docs/UPGRADING-5.0                                 |  13 ++
 etc/RTIR_Config.pm                                 |   2 +-
 etc/initialdata                                    |  44 +++++
 etc/upgrade/5.0.2/content                          |  53 ++++++
 html/RTIR/Display.html                             |   2 +
 html/RTIR/Elements/ShowCVEDetails                  | 133 +++++++++++++++
 html/RTIR/Incident/Display.html                    |   3 +
 .../Action/{RTIR_FindDomain.pm => RTIR_FindCVE.pm} |  39 ++---
 .../Action/{RTIR_MergeIPs.pm => RTIR_MergeCVEs.pm} |   6 +-
 static/css/rtir-styles.css                         |   1 +
 t/custom-fields/{domain.t => cve.t}                | 178 ++++++++-------------
 11 files changed, 334 insertions(+), 140 deletions(-)
 create mode 100644 html/RTIR/Elements/ShowCVEDetails
 copy lib/RT/Action/{RTIR_FindDomain.pm => RTIR_FindCVE.pm} (72%)
 copy lib/RT/Action/{RTIR_MergeIPs.pm => RTIR_MergeCVEs.pm} (96%)
 copy t/custom-fields/{domain.t => cve.t} (51%)


hooks/post-receive
-- 
rtir


More information about the rt-commit mailing list