CVE-2016-15009: Cross-Site Request Forgery (CSRF) (CWE-352)
value: A vulnerability classified as problematic has been found in OpenACS bug-tracker. Affected is an unknown function of the file lib/nav-bar.adp of the component Search. The manipulation leads to cross-site request forgery. It is possible to launch the attack remotely. The name of the patch is aee43e5714cd8b697355ec3bf83eefee176d3fc3. It is recommended to apply a patch to fix this issue. The identifier of this vulnerability is VDB-217440.
CWE description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
CVSS v3 base score: 8.8
Published: 2023-01-05T09:15Z
Repository: https://github.com/openacs/bug-tracker
Language: Other
Files changed: lib/nav-bar.adp, lib/one-bug.adp, www/bug.tcl, www/patch.adp, www/patch.tcl
Commit date: 2016-05-25 07:38:02 +0000
Commit message: - protect against too large bug numbers (causes pg errors)
- don't access @patch.patch_id@ for displayed_object_id, if it does
  not exist (view mode)
- don't perform message key subsitution in user contributed bug description
- add csrf protection for search

Diff: diff --git a/lib/nav-bar.adp b/lib/nav-bar.adp
index de91122..d63d666 100644
--- a/lib/nav-bar.adp
+++ b/lib/nav-bar.adp
@@ -8,11 +8,12 @@
   </form>
   <if @search_mounted_p@>
       <form action="@form_search_url@" method="get" name="search">
         <input name="q" type="text" size="15" class="bt_navbar" value="#bug-tracker.Search#" 
           onFocus="javascript:this.value='';">
-        <input name="search_package_id" type="hidden" value="@package_id@">
+          <input name="search_package_id" type="hidden" value="@package_id@">
+          <if @::__csrf_token@ defined><input type="hidden" name="__csrf_token" value="@::__csrf_token;literal@"></if>
           <input type="submit" value="#bug-tracker.Search#" class="bt_navbar_btn">
       </form>
   </if>
 
 </div>
diff --git a/lib/one-bug.adp b/lib/one-bug.adp
index 40907d0..3a403b4 100644
--- a/lib/one-bug.adp
+++ b/lib/one-bug.adp
@@ -7,14 +7,14 @@
   @param bug array of values as returned from bug_tracker::bug::get
   @param comments html chunk of comments
   @param style string (either "feed" or "display" -- default is display)
   @param base_url url to the package (ok for this to be empty if in the package, trailing / expected)
 </contract>
-<h1>Bug @bug.bug_number_display@ - @bug.summary@ [@bug.component_name@]</h1>
+<h1>Bug @bug.bug_number_display@ - @bug.summary;noi18n@ [@bug.component_name@]</h1>
 <p>State: @bug.pretty_state@</p>
 <if @bug.found_in_version_name@ not nil><p>Found in version: @bug.found_in_version_name@</p></if>
 <if @bug.fix_for_version_name@ not nil><p>Fix for version: @bug.fix_for_version_name@</p></if>
 <if @bug.fixed_in_version_name@ not nil><p>Fixed in version: @bug.fixed_in_version_name@</p></if>
 
 <multiple name="roles"><p>@roles.role_pretty@: <a href="@roles.user_url@">@roles.user_name@</a></p></multiple>
 
-@comments;noquote@
\ No newline at end of file
+@comments;literal@
\ No newline at end of file
diff --git a/www/bug.tcl b/www/bug.tcl
index 9388223..e4a152f 100644
--- a/www/bug.tcl
+++ b/www/bug.tcl
@@ -12,10 +12,15 @@ ad_page_contract {
     check_show_patch_status -requires show_patch_status {
 	if {$show_patch_status ni {"open" "all"}} {
 	    ad_complain "invalid value for show_patch_status"
 	}
     }
+    check_bug_number -requires bug_number:integer {
+	if {$bug_number > 2**31} {
+	    ad_complain "invalid bug number"
+	}
+    }
 }
 
 #####
 #
 # Setup
diff --git a/www/patch.adp b/www/patch.adp
index 675b9a7..34250e9 100644
--- a/www/patch.adp
+++ b/www/patch.adp
@@ -1,9 +1,9 @@
 <master src="../lib/master">
 <property name="doc(title)">@page_title;literal@</property>
 <property name="context">@context;literal@</property>
-<property name="displayed_object_id">@patch.patch_id;literal@</property>
+<if @patch.patch_id@ defined><property name="displayed_object_id">@patch.patch_id;literal@</property></if>
 
 <formtemplate id="patch"></formtemplate>
 
 <p>
 <if @button_form_export_vars@ not nil>
diff --git a/www/patch.tcl b/www/patch.tcl
index 715dda9..6336900 100644
--- a/www/patch.tcl
+++ b/www/patch.tcl
@@ -4,11 +4,11 @@ ad_page_contract {
     @author Peter Marklund (peter@collaboraid.biz)
     @date 2002-09-04
     @cvs-id $Id$
 } {
     patch_number:integer,notnull
-    mode:optional
+    mode:word,optional
     cancel_edit:optional    
     edit:optional
     accept:optional
     refuse:optional
     delete:optional    
@@ -38,11 +38,11 @@ set patch_status [db_string patch_status {}]
 
 # Is this project using multiple versions?
 set versions_p [bug_tracker::versions_p]
 
 # Abort editing and return to view mode if the user hit cancel on the edit form
-if { ([info exists cancel_edit] && $cancel_edit ne "") } {
+if { [info exists cancel_edit] && $cancel_edit ne "" } {
     ad_returnredirect $view_patch_url
     ad_script_abort
 }
 
 # If the download link was clicked - return the text content of the patch
@@ -56,21 +56,21 @@ if { ([info exists download] && $download ne "") } {
 }
 
 # Initialize the page mode variable
 # We are in view mode per default
 if { ![info exists mode] } {
-    if { ([info exists edit] && $edit ne "") } {
+    if { [info exists edit] && $edit ne "" } {
         set mode edit
-    } elseif { ([info exists accept] && $accept ne "") } {        
+    } elseif { [info exists accept] && $accept ne "" } {        
         set mode accept
-    } elseif { ([info exists refuse] && $refuse ne "") } {
+    } elseif { [info exists refuse] && $refuse ne "" } {
         set mode refuse
-    } elseif { ([info exists delete] && $delete ne "") } {
+    } elseif { [info exists delete] && $delete ne "" } {
         set mode delete
-    } elseif { ([info exists reopen] && $reopen ne "") } {
+    } elseif { [info exists reopen] && $reopen ne "" } {
         set mode reopen
-    } elseif { ([info exists comment] && $comment ne "") } {
+    } elseif { [info exists comment] && $comment ne "" } {
         set mode comment
     } else {
         set mode view
     }
 }
@@ -414,11 +414,11 @@ if { [form is_request patch] } {
     if { !$versions_p } {
         element set_properties patch generated_from_version -widget hidden
     }
 }
 
-if { [form is_valid patch] } {
+if { [form is_valid patch] && $mode ne "view"} {
     # A valid submit of the form
 
     set update_exprs [list]
 
     form get_values patch patch_number

Vulnerable code:         <input name="search_package_id" type="hidden" value="@package_id@">
<h1>Bug @bug.bug_number_display@ - @bug.summary@ [@bug.component_name@]</h1>
@comments;noquote@
<property name="displayed_object_id">@patch.patch_id;literal@</property>
    mode:optional
if { ([info exists cancel_edit] && $cancel_edit ne "") } {
    if { ([info exists edit] && $edit ne "") } {
    } elseif { ([info exists accept] && $accept ne "") } {        
    } elseif { ([info exists refuse] && $refuse ne "") } {
    } elseif { ([info exists delete] && $delete ne "") } {
    } elseif { ([info exists reopen] && $reopen ne "") } {
    } elseif { ([info exists comment] && $comment ne "") } {
if { [form is_valid patch] } {
Fixed code:           <input name="search_package_id" type="hidden" value="@package_id@">
          <if @::__csrf_token@ defined><input type="hidden" name="__csrf_token" value="@::__csrf_token;literal@"></if>
<h1>Bug @bug.bug_number_display@ - @bug.summary;noi18n@ [@bug.component_name@]</h1>
@comments;literal@
    check_bug_number -requires bug_number:integer {
	if {$bug_number > 2**31} {
	    ad_complain "invalid bug number"
	}
    }
<if @patch.patch_id@ defined><property name="displayed_object_id">@patch.patch_id;literal@</property></if>
    mode:word,optional
if { [info exists cancel_edit] && $cancel_edit ne "" } {
    if { [info exists edit] && $edit ne "" } {
    } elseif { [info exists accept] && $accept ne "" } {        
    } elseif { [info exists refuse] && $refuse ne "" } {
    } elseif { [info exists delete] && $delete ne "" } {
    } elseif { [info exists reopen] && $reopen ne "" } {
    } elseif { [info exists comment] && $comment ne "" } {
if { [form is_valid patch] && $mode ne "view"} {