Skip to content

Commit 459c509

Browse files
committed
feat: Added Add Security Headers rule
1 parent 5a3e526 commit 459c509

8 files changed

+128
-2
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.env
12
vendor/
23
.idea/
34
.phpunit.result.cache

src/RuleContent.php

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
class RuleContent {
66

7-
87
public array $content;
98
public array $templateVars;
109

src/SecureCommand.php

+35
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use WP_CLI\Process;
77
use WP_CLI\Utils;
88
use WP_CLI_Command;
9+
use WP_CLI_Secure\SubCommands\AddSecurityHeaders;
910
use WP_CLI_Secure\SubCommands\BlockAccessToHtaccess;
1011
use WP_CLI_Secure\SubCommands\BlockAccessToSensitiveDirectories;
1112
use WP_CLI_Secure\SubCommands\BlockAccessToSensitiveFiles;
@@ -283,6 +284,40 @@ public function block_access_to_sensitive_directories($args, $assoc_args) : void
283284
(new BlockAccessToSensitiveDirectories($assoc_args))->output();
284285
}
285286

287+
/**
288+
* Set Security Headers.
289+
*
290+
* Set Security Headers.
291+
*
292+
* ## OPTIONS
293+
*
294+
* [--remove]
295+
* : Removes the rule from .htaccess or nginx.conf.
296+
*
297+
* [--headers]
298+
* : Custom comma separated header list to add.
299+
*
300+
* [--output]
301+
* : Use this option to display the actual code that you can manually copy and paste into some other file
302+
*
303+
* [--headers=<headers>]
304+
* : List of headers you want to be added.
305+
*
306+
* [--server=<server>]
307+
* : Set a server type. Possible options are "apache" and "nginx". Default is "apache" and all rules are stored in
308+
* .htaccess file
309+
*
310+
* ## EXAMPLES
311+
*
312+
* $ wp secure add_security_headers
313+
* Success: Add Security Headers rule has been deployed.
314+
*
315+
* @when before_wp_load
316+
*/
317+
public function add_security_headers($args, $assoc_args) : void {
318+
(new AddSecurityHeaders($assoc_args))->output();
319+
}
320+
286321
/**
287322
* Blocks direct access to .htaccess
288323
*
+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
namespace WP_CLI_Secure\SubCommands;
4+
5+
class AddSecurityHeaders extends SubCommand {
6+
public string $ruleTemplate = 'add_security_headers';
7+
public string $ruleName = 'SECURITY HEADERS';
8+
public string $successMessage = 'Add Security Headers rule has been deployed.';
9+
public string $removalMessage= 'Add Security Headers rule has been removed.';
10+
11+
public function getTemplateVars() {
12+
13+
$default_headers = [
14+
'Strict-Transport-Security' => '"max-age=63072000; includeSubDomains; preload"',
15+
'Referrer-Policy' => 'strict-origin-when-cross-origin',
16+
'X-Content-Type-Options' => 'nosniff',
17+
'X-Frame-Options' => 'SAMEORIGIN',
18+
'X-XSS-Protection' => '"1; mode=block"'
19+
];
20+
21+
$headers = isset( $this->commandArguments['headers'] ) ? $this->commandArguments['headers'] : array_keys( $default_headers );
22+
if ( ! empty( $headers ) ) {
23+
if ( is_string( $headers ) ) {
24+
$headers = explode( ',', $headers );
25+
}
26+
$headers = array_map( 'trim', $headers );
27+
28+
foreach ( $headers as $h ) {
29+
$header = '';
30+
$value = '';
31+
foreach ( $default_headers as $key => $v ) {
32+
if ( strtolower( $key ) === strtolower( $h ) ) {
33+
$header = $key;
34+
$value = $v;
35+
}
36+
}
37+
if ( empty( $header ) ) {
38+
continue;
39+
}
40+
$headers_array[] =
41+
[
42+
'header' => $header,
43+
'value' => $value
44+
];
45+
}
46+
return $headers_array;
47+
}
48+
return [];
49+
}
50+
}

src/SubCommands/BlockAccessToSensitiveDirectories.php

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ public function getTemplateVars() {
1313
if ( ! empty( $directories ) ) {
1414
$directories = explode( ',', $directories );
1515
$directories = array_map( 'trim', $directories );
16-
$directories_array = [];
1716

1817
return [
1918
[ 'directories' => implode( '|', array_map( 'preg_quote', $directories ) ) ]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Header always set {{header}} {{value}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
add_header {{header}} {{value}} always;
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace Tests\Feature;
4+
5+
use Tests\BaseTestCase;
6+
use WP_CLI_Secure\SubCommands\AddSecurityHeaders;
7+
8+
class AddSecurityHeadersTest extends BaseTestCase {
9+
public function setUp(): void {
10+
parent::setUp();
11+
12+
$command = new AddSecurityHeaders($this->nginxAssocArgs);
13+
$command->output();
14+
15+
$command = new AddSecurityHeaders($this->apacheAssocArgs);
16+
$command->output();
17+
18+
exec('cd ' . dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . $_ENV['WORDPRESS_NGINX_PATH'] . '&& ddev exec nginx -s reload');
19+
}
20+
21+
public function testItWillContainAllHeadersOnNginx() : void {
22+
$response = $this->nginxHttpClient->get('', ['http_errors' => false]);
23+
24+
$this->assertNotEmpty($response->getHeaderLine( 'Strict-Transport-Security' ));
25+
$this->assertNotEmpty($response->getHeaderLine( 'Referrer-Policy' ));
26+
$this->assertNotEmpty($response->getHeaderLine( 'x-content-type-options' ));
27+
$this->assertNotEmpty($response->getHeaderLine( 'X-Frame-Options' ));
28+
$this->assertNotEmpty($response->getHeaderLine( 'X-XSS-Protection' ));
29+
}
30+
31+
public function testItWillContainAllHeadersOnApache() : void {
32+
$response = $this->apacheHttpClient->get('', ['http_errors' => false]);
33+
34+
$this->assertNotEmpty($response->getHeaderLine( 'Strict-Transport-Security' ));
35+
$this->assertNotEmpty($response->getHeaderLine( 'Referrer-Policy' ));
36+
$this->assertNotEmpty($response->getHeaderLine( 'x-content-type-options' ));
37+
$this->assertNotEmpty($response->getHeaderLine( 'X-Frame-Options' ));
38+
$this->assertNotEmpty($response->getHeaderLine( 'X-XSS-Protection' ));
39+
}
40+
}

0 commit comments

Comments
 (0)