Package: otrs2 / 5.0.16-1+deb9u6

18-CVE-2017-16854.diff Patch series | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# This fixes OSA-2017-08, also known as CVE-2017-16854: An attacker who is
# logged into OTRS as a customer can use the ticket search form to disclose
# internal article information of their customer tickets.
# URL: https://www.otrs.com/security-advisory-2017-08-security-update-otrs-framework/

diff -Naur otrs2-5.0.16.orig/Kernel/Modules/CustomerTicketSearch.pm otrs2-5.0.16/Kernel/Modules/CustomerTicketSearch.pm
--- otrs2-5.0.16.orig/Kernel/Modules/CustomerTicketSearch.pm	2017-01-17 03:39:35.000000000 +0100
+++ otrs2-5.0.16/Kernel/Modules/CustomerTicketSearch.pm	2017-12-07 13:35:37.915211744 +0100
@@ -585,7 +585,12 @@
                     }
                     else
                     {
+                        ARTICLE:
                         for my $Articles (@Article) {
+
+                            # Skip internal articles.
+                            next ARTICLE if $Articles->{ArticleType} =~ /-int/;
+
                             if ( $Articles->{Body} ) {
                                 $Data{ArticleTree}
                                     .= "\n-->||$Articles->{ArticleType}||$Articles->{From}||"
@@ -724,10 +729,9 @@
             my @PDFData;
             for my $TicketID (@ViewableTicketIDs) {
 
-                # get first article data
-                my %Data = $TicketObject->ArticleLastCustomerArticle(
+                # Get last customer or any other article if it doesn't exist.
+                my %Data = $Self->_LastCustomerArticle(
                     TicketID      => $TicketID,
-                    Extended      => 1,
                     DynamicFields => 0,
                 );
 
@@ -1058,10 +1062,9 @@
                     )
                 {
 
-                    # get first article data
-                    my %Article = $TicketObject->ArticleLastCustomerArticle(
+                    # Get last customer or any other article if it doesn't exist.
+                    my %Article = $Self->_LastCustomerArticle(
                         TicketID      => $TicketID,
-                        Extended      => 1,
                         DynamicFields => 1,
                     );
 
@@ -1902,4 +1905,47 @@
     return %StopWordsServerErrors;
 }
 
+sub _LastCustomerArticle {
+    my ( $Self, %Param ) = @_;
+
+    my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
+
+    # Get all customer articles.
+    my @Index = $TicketObject->ArticleIndex(
+        TicketID   => $Param{TicketID},
+        SenderType => 'customer',
+    );
+
+    # Go over articles in reverse order and return the last external one.
+    if (@Index) {
+        for my $CustomerArticleID ( reverse @Index ) {
+            my %LastCustomerArticle = $TicketObject->ArticleGet(
+                ArticleID     => $CustomerArticleID,
+                Extended      => 1,
+                DynamicFields => $Param{DynamicFields},
+            );
+            if ( $LastCustomerArticle{ArticleType} !~ /-int/ ) {
+                return %LastCustomerArticle;
+            }
+        }
+    }
+
+    # If no customer articles were found, return the last external one.
+    @Index = $TicketObject->ArticleIndex(
+        TicketID => $Param{TicketID},
+    );
+    for my $ArticleID ( reverse @Index ) {
+        my %LastArticle = $TicketObject->ArticleGet(
+            ArticleID     => $ArticleID,
+            Extended      => 1,
+            DynamicFields => $Param{DynamicFields},
+        );
+        if ( $LastArticle{StateType} eq 'merged' || $LastArticle{ArticleType} !~ /-int/ ) {
+            return %LastArticle;
+        }
+    }
+
+    return;
+}
+
 1;
diff -Naur otrs2-5.0.16.orig/scripts/test/Selenium/Customer/CustomerTicketSearch.t otrs2-5.0.16/scripts/test/Selenium/Customer/CustomerTicketSearch.t
--- otrs2-5.0.16.orig/scripts/test/Selenium/Customer/CustomerTicketSearch.t	2017-01-17 03:39:35.000000000 +0100
+++ otrs2-5.0.16/scripts/test/Selenium/Customer/CustomerTicketSearch.t	2017-12-07 13:35:37.915211744 +0100
@@ -85,6 +85,25 @@
             "Ticket ID $TicketID - created",
         );
 
+        # Add test article to the ticket.
+        #   Make it email-internal, with sender type customer, in order to check if it's filtered out correctly.
+        my $InternalArticleMessage = 'not for the customer';
+        my $ArticleID              = $TicketObject->ArticleCreate(
+            TicketID       => $TicketID,
+            ArticleType    => 'email-internal',
+            SenderType     => 'customer',
+            Subject        => $TitleRandom,
+            Body           => $InternalArticleMessage,
+            ContentType    => 'text/plain; charset=ISO-8859-15',
+            HistoryType    => 'EmailCustomer',
+            HistoryComment => 'Some free text!',
+            UserID         => 1,
+        );
+        $Self->True(
+            $ArticleID,
+            "Article is created - ID $ArticleID"
+        );
+
         # get test ticket number
         my %Ticket = $TicketObject->TicketGet(
             TicketID => $TicketID,
@@ -100,6 +119,12 @@
             "Ticket $TitleRandom found on page",
         );
 
+        # Check if internal article was not shown.
+        $Self->True(
+            index( $Selenium->get_page_source(), $InternalArticleMessage ) == -1,
+            'Internal article not found on page'
+        );
+
         # click on '← Change search options'
         $Selenium->find_element( "← Change search options", 'link_text' )->VerifiedClick();