Skip to content

Add CSP script-src directive and remove inline event handlers (CO-2720) #67

Merged
merged 8 commits into from
Jul 21, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
; This file is for unifying the coding style for different editors and IDEs.
; More information at https://editorconfig.org

root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = false
trim_trailing_whitespace = false

[*.bat]
end_of_line = crlf

[*.yml]
indent_size = 2

[*.twig]
insert_final_newline = false

[Makefile]
indent_style = tab
3 changes: 3 additions & 0 deletions app/src/Controller/AppController.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ public function beforeRender(EventInterface $event) {
$this->set('vv_menu_permissions',
$this->Authorization->menuPermissions($this->request->getSession()->read('Auth.User.username'), $mgid));
}

// Generate a nonce for use in JavaScript tags with the Content-Security-Policy script-src directive
$this->set('vv_js_nonce', base64_encode(random_bytes(16)));
}

/**
Expand Down
15 changes: 9 additions & 6 deletions app/templates/MatchgridSettings/fields.inc
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

use \App\Lib\Enum\ReferenceIdEnum;
?>
<script type="text/javascript">
<script nonce="<?= $vv_js_nonce ?>">
// JS specific to these fields

function fields_update_gadgets() {
Expand All @@ -43,17 +43,20 @@ use \App\Lib\Enum\ReferenceIdEnum;
$("#referenceid-start").closest('li').hide();
}
}
function js_local_onload() {

$(function() {
fields_update_gadgets();
}

$('#referenceid-method').change(function() {
fields_update_gadgets();
});
});
</script>
<?php
// This view does not support read-only
if($action == 'edit') {
print $this->Field->control('referenceid_method',
['empty' => true,
'onChange' => 'fields_update_gadgets();']);
['empty' => true]);

print $this->Field->control('referenceid_start',
['default' => 1001]);
Expand Down
8 changes: 4 additions & 4 deletions app/templates/Matchgrids/reconcile.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,12 @@
</table>
</div>

<script>
function js_local_onload() {
<script nonce="<?= $vv_js_nonce ?>">
$(function() {
// Handle display-mode switching
$('.view-controls input').click(function () {
// Remove existing view-mode classes and add the new class equal to the view-controls switch ID
$('#reconcile-table').removeClass('view-mode-diff view-mode-match view-mode-both').addClass($(this).attr('id'));
});
}
</script>
});
</script>
20 changes: 11 additions & 9 deletions app/templates/Permissions/fields.inc
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

use \App\Lib\Enum\PermissionEnum;
?>
<script type="text/javascript">
<script nonce="<?= $vv_js_nonce ?>">
// JS specific to these fields

function fields_update_gadgets() {
Expand All @@ -43,19 +43,21 @@ use \App\Lib\Enum\PermissionEnum;
$("#matchgrid-id").closest('li').show();
}
}
function js_local_onload() {

$(function() {
fields_update_gadgets();
}

$('#permission').change(function() {
fields_update_gadgets();
});
});
</script>
<?php
// This view does not support read-only
if($action == 'add' || $action == 'edit') {
print $this->Field->control('username');

print $this->Field->control('permission',
['empty' => true,
'onChange' => 'fields_update_gadgets();']);


print $this->Field->control('permission', ['empty' => true]);

print $this->Field->control('matchgrid_id', ['empty' => true]);
}
16 changes: 10 additions & 6 deletions app/templates/RuleAttributes/fields.inc
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
use \App\Lib\Enum\ConfidenceModeEnum;
use \App\Lib\Enum\SearchTypeEnum;
?>
<script type="text/javascript">
<script nonce="<?= $vv_js_nonce ?>">
// JS specific to these fields

function fields_update_gadgets() {
Expand All @@ -44,17 +44,21 @@ use \App\Lib\Enum\SearchTypeEnum;
$("#match-empty").closest('li').show();
}
}
function js_local_onload() {

$(function() {
fields_update_gadgets();
}

$('#required').change(function() {
fields_update_gadgets();
});
});
</script>
<?php
<?php
// This view does not support read-only
if($action == 'add' || $action == 'edit') {
print $this->Field->control('attribute_id', ['empty' => true]);
print $this->Field->control('crosscheck_attribute_id', ['empty' => true], false, __('match.fd.RuleAttributes.crosscheck_attribute_id'));
print $this->Field->control('search_type', ['empty' => true]);
print $this->Field->control('required', ['onChange'=>'fields_update_gadgets();'], false);
print $this->Field->control('required', [], false);
print $this->Field->control('match_empty', [], false);
}
17 changes: 10 additions & 7 deletions app/templates/SystemsOfRecord/fields.inc
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
use \App\Lib\Enum\ResolutionModeEnum;
use \App\Lib\Enum\TrustModeEnum;
?>
<script type="text/javascript">
<script nonce="<?= $vv_js_nonce ?>">
// JS specific to these fields

function fields_update_gadgets() {
Expand All @@ -43,9 +43,13 @@ use \App\Lib\Enum\TrustModeEnum;
}
}

function js_local_onload() {
$(function() {
fields_update_gadgets();
}

$('#resolution-mode').change(function() {
fields_update_gadgets();
});
});
</script>
<?php
// This view does not support read-only
Expand All @@ -56,9 +60,8 @@ if($action == 'add' || $action == 'edit') {
['empty' => true,
'default' => TrustModeEnum::Standard ]);

print $this->Field->control('resolution_mode',
['empty' => true,
'onChange' => 'fields_update_gadgets();']);

print $this->Field->control('resolution_mode',
['empty' => true]);

print $this->Field->control('notification_email', [], false);
}
2 changes: 1 addition & 1 deletion app/templates/element/dialog.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title" id="dialog-title"><?= __('match.op.confirm'); ?></h2>
<button type="button" class="btn-close nospin" data-bs-dismiss="modal" aria-label="Close"/>
<button type="button" class="btn-close nospin" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div id="dialog-text" class="modal-body">
</div>
Expand Down
9 changes: 6 additions & 3 deletions app/templates/element/httpHeaders.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,21 @@
* @license Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/

// As a general rule, all Match pages are post-login and so shouldn't be cached
// As a general rule, all Match pages are post-login and so shouldn't be cached
header("Expires: Thursday, 10-Jan-69 00:00:00 GMT");
header("Cache-Control: no-store, no-cache, max-age=0, must-revalidate");
header("Pragma: no-cache");

// CakePHP adds inline event handlers ("oninput" and "oninvalid") to fields as part of FormHelper.
// So as not to throw CSP errors, we must include "script-src-attr 'unsafe-inline'".
header("Content-Security-Policy: object-src 'none'; base-uri 'none'; frame-ancestors 'self'; script-src 'self' 'nonce-$vv_js_nonce'; script-src-attr 'unsafe-inline';");

header("Content-Security-Policy: object-src 'none'; base-uri 'none'; frame-ancestors 'self'");
header("X-Content-Type-Options: nosniff");
header("Permissions-Policy: accelerometer=(),autoplay=(),camera=(),cross-origin-isolated=(),display-capture=(),encrypted-media=(),fullscreen=(),geolocation=(),gyroscope=(),keyboard-map=(),magnetometer=(),microphone=(),midi=(),payment=(),picture-in-picture=(),publickey-credentials-get=(),screen-wake-lock=(),sync-xhr=(self),usb=(),web-share=(),xr-spatial-tracking=(),gamepad=(),hid=(),idle-detection=(),interest-cohort=(),serial=()");
header("Cross-Origin-Opener-Policy: same-origin");
header("X-Permitted-Cross-Domain-Policies: none");

// Add X-UA-Compatible header for IE
// Add X-UA-Compatible header for IE
if (isset($_SERVER['HTTP_USER_AGENT']) && (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false)) {
header('X-UA-Compatible: IE=edge,chrome=1');
}
Loading