Skip to content

Commit 6789a43

Browse files
committed
Add new options for CSP Violation Reports, adding Reporting-Endopoints response header field
closes #17 Signed-off-by: Giuseppe Foti <foti.giuseppe@gmail.com>
1 parent 1926cce commit 6789a43

11 files changed

+586
-122
lines changed

admin/class-no-unsafe-inline-admin.php

Lines changed: 217 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,26 @@ public function add_new_options( $new_ver, $old_ver ): void {
295295

296296
update_option( 'no-unsafe-inline', $options );
297297
}
298+
if ( version_compare( $old_ver, '1.2.3', '<' ) ) {
299+
$options = (array) get_option( 'no-unsafe-inline' );
300+
if ( isset( $options['endpoints'] ) &&
301+
Utils::is_one_dimensional_string_array( $options['endpoints'] )
302+
) {
303+
$new_endpoint = array();
304+
foreach ( $options['endpoints'] as $index => $endpoint ) {
305+
$new_endpoint[] = array(
306+
'url' => $endpoint,
307+
'name' => 'csp-endpoint-' . strval( $index ),
308+
);
309+
}
310+
$options['endpoints'] = $new_endpoint;
311+
}
312+
if ( isset( $options['use_reports'] ) && 1 === $options['use_reports'] ) {
313+
$options['use_report-to'] = 1;
314+
$options['add_Reporting-Endpoints'] = 1;
315+
}
316+
update_option( 'no-unsafe-inline', $options );
317+
}
298318
}
299319

300320
/**
@@ -767,6 +787,77 @@ public function register_options(): void {
767787
'no-unsafe-inline_report'
768788
);
769789

790+
add_settings_field(
791+
'use_report-to',
792+
sprintf(
793+
// translators: %1$s is report-uri link.
794+
esc_html__( 'Use %1$s reporting directive', 'no-unsafe-inline' ),
795+
'<a href="https://www.w3.org/TR/CSP3/#directive-report-to" target="_blank">report-to</a>'
796+
),
797+
array( $this, 'print_toggle_option' ),
798+
'no-unsafe-inline-options',
799+
'no-unsafe-inline_report',
800+
array(
801+
'option_name' => 'use_report-to',
802+
'label' => __( 'Use the report-to directive to send reports to a URI.', 'no-unsafe-inline' ),
803+
)
804+
);
805+
806+
add_settings_field(
807+
'use_report-uri',
808+
sprintf(
809+
// translators: %1$s is report-uri link.
810+
esc_html__( 'Use %1$s reporting directive (deprecated)', 'no-unsafe-inline' ),
811+
'<a href="https://www.w3.org/TR/CSP3/#directive-report-uri" target="_blank">report-uri</a>'
812+
),
813+
array( $this, 'print_toggle_option' ),
814+
'no-unsafe-inline-options',
815+
'no-unsafe-inline_report',
816+
array(
817+
'option_name' => 'use_report-uri',
818+
'label' => __( 'Use the report-uri directive to send reports to a URI.', 'no-unsafe-inline' ),
819+
)
820+
);
821+
822+
add_settings_field(
823+
'add_Reporting-Endpoints',
824+
sprintf(
825+
// translators: %1$s is report-uri link.
826+
esc_html__( 'Add the %1$s response header field to define the reporting endpoints.', 'no-unsafe-inline' ),
827+
'<a href="https://www.w3.org/TR/reporting-1/#header" target="_blank">Reporting-Endpoints</a>'
828+
),
829+
array( $this, 'print_toggle_option' ),
830+
'no-unsafe-inline-options',
831+
'no-unsafe-inline_report',
832+
array(
833+
'option_name' => 'add_Reporting-Endpoints',
834+
'label' => esc_html__( 'Add the Reporting-Endpoints response header field to define a set of reporting endpoints for a document or a worker script.', 'no-unsafe-inline' ) .
835+
'<br>' . __( 'This is a standard feature included in the Reporting API v1.', 'no-unsafe-inline' ),
836+
)
837+
);
838+
839+
add_settings_field(
840+
'add_Report-To',
841+
sprintf(
842+
// translators: %1$s is report-uri link.
843+
esc_html__( 'Add the %1$s response header field to define the reporting endpoints (legacy and deprecated)', 'no-unsafe-inline' ),
844+
'<a href="https://www.w3.org/TR/2016/NOTE-reporting-1-20160607/#header" target="_blank">Report-To</a>'
845+
),
846+
array( $this, 'print_toggle_option' ),
847+
'no-unsafe-inline-options',
848+
'no-unsafe-inline_report',
849+
array(
850+
'option_name' => 'add_Report-To',
851+
'label' => sprintf(
852+
__( 'Add the Report-To response header field to instruct the user agent to store a reporting endpoints for an origin.', 'no-unsafe-inline' ) .
853+
'<br>' . __( 'This is a legacy and deprecated feature, never standardized and included in the Reporting API v0.', 'no-unsafe-inline' ) .
854+
// translators: %s is a link.
855+
'<br>' . __( 'See %s.', 'no-unsafe-inline' ),
856+
'<a href="https://developer.chrome.com/blog/reporting-api-migration" target="_blank">Migrate to Reporting API v1</a>'
857+
),
858+
)
859+
);
860+
770861
add_settings_field(
771862
'group_name',
772863
esc_html__( 'Group name', 'no-unsafe-inline' ),
@@ -946,8 +1037,12 @@ public function register_base_rule(): void {
9461037
/**
9471038
* Sanitize the settings
9481039
*
1040+
* The returned array is the sanitized settings and should be in the format:
1041+
* array<string, string|int|array<int, array{url: string, name: string}>>
1042+
* Using mixed, because get_option() returns mixed and we need to merge it with array.
1043+
*
9491044
* @throws \NUNIL\Nunil_Exception Main option is not an array.
950-
* @param array<string|int|array<int|string>> $input Contains the settings.
1045+
* @param array<mixed> $input Contains the settings.
9511046
* @return array<mixed>
9521047
*/
9531048
public function sanitize_options( $input ) {
@@ -981,6 +1076,10 @@ public function sanitize_options( $input ) {
9811076
'fix_setattribute_style',
9821077
'add_wl_by_cluster_to_db',
9831078
'use_reports',
1079+
'use_report-to',
1080+
'use_report-uri',
1081+
'add_Reporting-Endpoints',
1082+
'add_Report-To',
9841083
'remove_tables',
9851084
'remove_options'
9861085
);
@@ -1100,14 +1199,33 @@ public function sanitize_options( $input ) {
11001199
}
11011200
}
11021201

1103-
unset( $options['endpoints'] );
11041202
if ( isset( $input['endpoints'] ) && is_array( $input['endpoints'] ) ) {
11051203
$new_input['endpoints'] = array_map(
1106-
function ( $url ) {
1107-
return esc_url_raw( strval( $url ), array( 'https' ) );
1204+
function ( $endpoint ) {
1205+
/**
1206+
* Since trustworthy depends on user agent, we shouldnt' limit to https only.
1207+
* However almost all browsers consider only https as trustworthy to deply a report.
1208+
*
1209+
* See: https://w3c.github.io/reporting/#header
1210+
* See: https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy
1211+
*/
1212+
if (
1213+
is_array( $endpoint ) &&
1214+
array_key_exists( 'url', $endpoint ) &&
1215+
array_key_exists( 'name', $endpoint ) &&
1216+
$this->is_trustworthy_url( Utils::sanitize_text( $endpoint['url'], false ) )
1217+
) {
1218+
return array(
1219+
'url' => esc_url_raw( Utils::sanitize_text( $endpoint['url'], false ) ),
1220+
'name' => Utils::sanitize_text( $endpoint['name'], false ),
1221+
);
1222+
}
1223+
return null;
11081224
},
11091225
$input['endpoints']
11101226
);
1227+
// resetto da 0 le chiavi dell'array e elimino eventuali valori nulli.
1228+
$new_input['endpoints'] = array_values( array_filter( $new_input['endpoints'] ) );
11111229
}
11121230

11131231
if ( isset( $input['max_response_header_size'] ) ) {
@@ -1126,6 +1244,43 @@ function ( $url ) {
11261244
return $new_options;
11271245
}
11281246

1247+
/**
1248+
* Check if a URL is trustworthy.
1249+
*
1250+
* @param string $url_string The URL to check.
1251+
* @return bool True if the URL is trustworthy, false otherwise.
1252+
*/
1253+
private function is_trustworthy_url( $url_string ) {
1254+
if ( empty( $url_string ) ) {
1255+
return false;
1256+
}
1257+
1258+
// Sanitize the URL.
1259+
$url_string = esc_url_raw( $url_string );
1260+
1261+
// Parse the URL.
1262+
$url = wp_parse_url( $url_string );
1263+
1264+
if ( false === $url ) {
1265+
return false;
1266+
}
1267+
1268+
// Check if the protocol is https.
1269+
if ( isset( $url['scheme'] ) && 'https' === $url['scheme'] ) {
1270+
return true;
1271+
}
1272+
1273+
// Check if the host matches 127.0.0.0/8 or ::1/128 .
1274+
if ( isset( $url['host'] ) ) {
1275+
$host = $url['host'];
1276+
if ( 'localhost' === $host || '127.0.0.1' === $host || '::1' === $host ) {
1277+
return true;
1278+
}
1279+
}
1280+
1281+
return false;
1282+
}
1283+
11291284
/**
11301285
* Sanitize the tools status
11311286
*
@@ -1696,38 +1851,76 @@ public function print_endpoints(): void {
16961851

16971852
// Add new endpoint button.
16981853
printf(
1854+
'<div><b>' .
1855+
esc_html__( 'Required. An array of JSON objects that specify the actual URL of your report collector.', 'no-unsafe-inline' ) .
1856+
'</b></div>'
1857+
);
1858+
printf(
1859+
'<div class="nunil-new-endpoint-container">' .
1860+
'<div class="nunil-new-endpoint-button-wrapper">' .
16991861
'<input class="nunil-btn nunil-btn-addnew" type="button" id="no-unsafe-inline[add_new_endpoint]"' .
17001862
'name="no-unsafe-inline[add_new_endpoint]" value="%s" %s />' .
1863+
'</div>' .
1864+
'<div class="nunil-new-endpoint-url-wrapper">' .
1865+
'<label for="no-unsafe-inline[new_endpoint]" class="nunil_label_left">%s</label>' .
17011866
'<input class="nunil-new-endpoint" type="text" id="no-unsafe-inline[new_endpoint]"' .
1702-
'name="no-unsafe-inline[new_endpoint]" %s />
1703-
<label for="no-unsafe-inline[new_endpoint]">%s</label>',
1867+
'name="no-unsafe-inline[new_endpoint]" %s />' .
1868+
'</div>' .
1869+
'<div class="nunil-new-endpoint-name-wrapper">' .
1870+
'<label for="no-unsafe-inline[new_endpoint_name]" class="nunil_label_left">%s</label>' .
1871+
'<input class="nunil-new-endpoint-name" type="text" id="no-unsafe-inline[new_endpoint_name]"' .
1872+
'name="no-unsafe-inline[new_endpoint_name]" %s />' .
1873+
'</div>' .
1874+
'</div>',
17041875
esc_html__( 'Add a new endpoint', 'no-unsafe-inline' ),
17051876
esc_html( $disabled ),
1877+
esc_html__( 'endpoint URL', 'no-unsafe-inline' ) . ': ',
1878+
esc_html( $disabled ),
1879+
esc_html__( 'endpoint name', 'no-unsafe-inline' ) . ': ',
17061880
esc_html( $disabled ),
1707-
esc_html__( 'Required. An array of JSON objects that specify the actual URL of your report collector.', 'no-unsafe-inline' )
17081881
);
17091882

17101883
print( '<ol class="nunil-endpoints-list" id="nunil-endpoints-list">' );
1711-
// Add a line for each url.
1712-
foreach ( $endpoints as $index => $endpoint ) {
1713-
$endp_txt = strval( Utils::cast_strval( $endpoint ) );
1884+
if ( count( $endpoints ) > 0 ) {
1885+
// Add a header line.
17141886
printf(
1715-
'<li>' .
1716-
'<button class="nunil-btn nunil-btn-del-endpoint" ' .
1717-
'id="no-unsafe-inline[del-endpoint][%d]" name="no-unsafe-inline[del-endpoint][%d]">' .
1718-
'<span class="dashicons dashicons-remove"> </span></button>' .
1719-
'<span class="nunil-endpoint-string txt-active">%s</span>' .
1720-
'<input class="nunil-hidden-endpoint" type="hidden" id="no-unsafe-inline[endpoints][%d]" ' .
1721-
'name="no-unsafe-inline[endpoints][%d]" value="%s" />' .
1722-
'</li>',
1723-
esc_html( $index ),
1724-
esc_html( $index ),
1725-
esc_html( $endp_txt ),
1726-
esc_html( $index ),
1727-
esc_html( $index ),
1728-
esc_html( $endp_txt )
1887+
'<li><span class="nunil-btn nunil-btn-endpoint-list" disabled><span class="dashicons dashicons-editor-ul"></span></span>' .
1888+
'<span class="nunil-endpoint-string"><b>%s</b></span>' .
1889+
'<span class="nunil-endpoint-string"><b>%s</b></span></li>',
1890+
esc_html__( 'endpoint URL', 'no-unsafe-inline' ),
1891+
esc_html__( 'endpoint name', 'no-unsafe-inline' )
17291892
);
17301893
}
1894+
// Add a line for each url.
1895+
foreach ( $endpoints as $index => $endpoint ) {
1896+
if ( is_array( $endpoint ) && array_key_exists( 'url', $endpoint ) && array_key_exists( 'name', $endpoint ) ) {
1897+
$endp_url = strval( Utils::cast_strval( $endpoint['url'] ) );
1898+
$endp_name = strval( Utils::cast_strval( $endpoint['name'] ) );
1899+
printf(
1900+
'<li>' .
1901+
'<button class="nunil-btn nunil-btn-del-endpoint" ' .
1902+
'id="no-unsafe-inline[del-endpoint][%d]" name="no-unsafe-inline[del-endpoint][%d]">' .
1903+
'<span class="dashicons dashicons-remove"> </span></button>' .
1904+
'<span class="nunil-endpoint-string txt-active">%s</span>' .
1905+
'<input class="nunil-hidden-endpoint" type="hidden" id="no-unsafe-inline[endpoints][%d][url]" ' .
1906+
'name="no-unsafe-inline[endpoints][%d][url]" value="%s" />' .
1907+
'<span class="nunil-endpoint-string txt-active">%s</span>' .
1908+
'<input class="nunil-hidden-endpoint" type="hidden" id="no-unsafe-inline[endpoints][%d][name]" ' .
1909+
'name="no-unsafe-inline[endpoints][%d][name]" value="%s" />' .
1910+
'</li>',
1911+
esc_html( $index ),
1912+
esc_html( $index ),
1913+
esc_html( $endp_url ),
1914+
esc_html( $index ),
1915+
esc_html( $index ),
1916+
esc_html( $endp_url ),
1917+
esc_html( $endp_name ),
1918+
esc_html( $index ),
1919+
esc_html( $index ),
1920+
esc_html( $endp_name ),
1921+
);
1922+
}
1923+
}
17311924
print( '</ol>' );
17321925
}
17331926

admin/css/no-unsafe-inline-admin.css

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -525,9 +525,51 @@ div.nunil_temp_div {
525525
color: #fff;
526526
}
527527

528+
@media (max-width: 1260px) {
529+
.nunil-new-endpoint-container {
530+
flex-direction: column;
531+
}
532+
}
533+
534+
.nunil-new-endpoint-container {
535+
display: flex;
536+
flex-wrap: wrap;
537+
flex-direction: row;
538+
justify-content: space-between;
539+
align-items: normal;
540+
border-top: 1px solid;
541+
border-bottom: 1px solid;
542+
padding: 5px;
543+
}
544+
545+
.nunil-new-endpoint-button-wrapper {
546+
flex-grow: 1;
547+
flex-shrink: 2;
548+
flex-basis: auto;
549+
}
550+
551+
.nunil-new-endpoint-url-wrapper {
552+
flex-grow: 2;
553+
flex-shrink: 1;
554+
flex-basis: auto;
555+
position: relative;
556+
}
557+
558+
.nunil-new-endpoint-name-wrapper {
559+
flex-grow: 1;
560+
flex-shrink: 1;
561+
flex-basis: auto;
562+
position: relative;
563+
}
564+
565+
.nunil_label_left {
566+
bottom: 100%;
567+
left: 0;
568+
}
569+
528570
.nunil-btn {
529-
background-color: #4CAF50; /* Green */
530-
border: none;
571+
background-color: #4CAF50;
572+
border: 2px solid #4CAF50;
531573
border-radius: 4px;
532574
color: white;
533575
padding: 2px 2px;
@@ -633,18 +675,7 @@ div.nunil_temp_div {
633675
max-width: 19em;
634676
}
635677

636-
.nunil-text-group:focus {
637-
background-color: LightCyan;
638-
}
639-
640-
.nunil-text-maxage {
641-
}
642-
643-
.nunil-text-maxage:focus {
644-
background-color: LightCyan;
645-
}
646-
647-
.nunil-new-endpoint:focus {
678+
.nunil-text-group:focus, .nunil-new-endpoint-name:focus, .nunil-text-maxage:focus, .nunil-new-endpoint:focus {
648679
background-color: LightCyan;
649680
}
650681

0 commit comments

Comments
 (0)