├── .gitignore ├── .perltidyrc ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── pkg ├── Cpanel │ └── Security │ │ ├── Advisor.pm │ │ └── Advisor │ │ ├── Assessors.pm │ │ └── Assessors │ │ ├── Apache.pm │ │ ├── Brute.pm │ │ ├── ClamAV.pm │ │ ├── Imunify360.pm │ │ ├── Iptables.pm │ │ ├── Jail.pm │ │ ├── Kernel.pm │ │ ├── Mysql.pm │ │ ├── PHP.pm │ │ ├── Passwords.pm │ │ ├── Permissions.pm │ │ ├── Processes.pm │ │ ├── SSH.pm │ │ ├── Scgiwrap.pm │ │ ├── Spam.pm │ │ ├── Symlinks.pm │ │ ├── Tomcat.pm │ │ ├── Trojans.pm │ │ ├── Usernames.pm │ │ └── _Self.pm ├── appconfig │ └── securityadvisor.conf ├── bin │ └── upgrade ├── cgi │ └── addon_securityadvisor.cgi ├── icon │ └── ico-security-advisor.png ├── install ├── install-dist ├── templates │ └── main.tmpl └── uninstall └── t ├── lib ├── Cpanel │ └── Security │ │ └── Advisor │ │ └── Assessors │ │ ├── MockAssessor.pm │ │ ├── MockLoadFail.pm │ │ └── MockNewFail.pm └── Test │ ├── Assessor.pm │ └── Mock │ └── SecurityAdvisor.pm ├── pkg-Cpanel-Security-Advisor-Assessors-Apache.t ├── pkg-Cpanel-Security-Advisor-Assessors-Imunify360.t ├── pkg-Cpanel-Security-Advisor-Assessors-Kernel.t ├── pkg-Cpanel-Security-Advisor-Assessors-Mysql.t ├── pkg-Cpanel-Security-Advisor-Assessors-Processes.t ├── pkg-Cpanel-Security-Advisor-Assessors-_Self.t └── pkg-Cpanel-Security-Advisor.t /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore perltidy files 2 | *.bak 3 | *.ERR 4 | # Ignore SVN 5 | *.svn* 6 | *.pyc 7 | *.so 8 | # Ignore tags files. 9 | tags 10 | TAGS 11 | # Ignore GCC generated files 12 | *.o 13 | *.gcno 14 | *.gcda 15 | gmon.out 16 | # Ignore perl tidy backup files 17 | *.tdy 18 | # Ignore patch files 19 | *.rej 20 | *.orig 21 | # ignore vim swap files. 22 | *.swp 23 | # Ignore Apple AFP files 24 | .DS_Store 25 | -------------------------------------------------------------------------------- /.perltidyrc: -------------------------------------------------------------------------------- 1 | -l=400 2 | -i=4 3 | -dt=4 4 | -it=4 5 | -bar 6 | -nsfs 7 | -nolq 8 | --break-at-old-comma-breakpoints 9 | --format-skipping 10 | --format-skipping-begin='#\s*tidyoff' 11 | --format-skipping-end='#\s*tidyon' 12 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | Please contribute using [GitHub Flow](https://guides.github.com/introduction/flow/). Create a branch, add commits, and [open a pull request](https://github.com/cpanelinc/addon_securityadvisor/compare/). 4 | 5 | # Contributing Developer Notes 6 | 7 | ## Assessor Modules 8 | 9 | The Security Advisor encapsulates all of its checks inside of the Perl modules that are located in the [Assessors directory](pkg/Cpanel/Security/Advisor/Assessors). 10 | 11 | Each module is a subclass of [Cpanel::Security::Advisor::Assessors](pkg/Cpanel/Security/Advisor/Assessors.pm), as such each module has access to the methods that can add to the notifications. 12 | 13 | ### Adding New Checks 14 | 15 | Please evaluate the list of [Assessors](pkg/Cpanel/Security/Advisor/Assessors) to see if the check you wish to add fits in an existing module. 16 | 17 | For existing modules, it is usually preferable to your new check as an isolated subroutine that can be called from the `generate_advice` subroutine. This is the "main" subroutine that is called to drive all the checks. 18 | 19 | If you must create a new module, please study the structure of existing modules. Be prepared to justify why a new module is required and why an existing module is no place for the new check. 20 | 21 | ## Advice on Advice 22 | 23 | ### All Advisory Messages Require Unique Static Keys 24 | 25 | There are four types of advice defined in `pkg/Cpanel/Security/Advisor/Assessors.pm`, they are: 26 | 27 | * `$Cpanel::Security::Advisor::Assessors::ADVISE_GOOD` 28 | * `$Cpanel::Security::Advisor::Assessors::ADVISE_INFO` 29 | * `$Cpanel::Security::Advisor::Assessors::ADVISE_WARN` 30 | * `$Cpanel::Security::Advisor::Assessors::ADVISE_BAD` 31 | 32 | They are pretty self explanatory, but it is preferred that any message type of `ADVISE_WARN` or `ADVISE_BAD` also include a `suggestion` for further explanation of the message and what actions may be taken. 33 | 34 | Example, 35 | 36 | ```perl 37 | $security_advisor_obj->add_advice( 38 | { 39 | 'key' => 'EntropyChat_is_running', #<-- required, globally unique static message key 40 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 41 | 'text' => ['Entropy Chat is running.'], 42 | 'suggestion' => [ 43 | 'Turn off Entropy Chat in the “[output,url,_1,Service Manager,_2,_3]” page.', 44 | $self->base_path('scripts/srvmng'), 45 | 'target', 46 | '_blank' 47 | ], 48 | } 49 | ); 50 | ``` 51 | 52 | ### All Advisory Messages Require Unique Static Keys 53 | 54 | The general method available to add an Advisory Message is called add_advice. You must specify the advice text and a globally unique static key that is shared by no other messages. 55 | 56 | Pull requests that contain new messages without this globally unique static key will be rejected. 57 | 58 | The convention being used for determining the static key is as follows. The key must begin with name of the assessor. For example, all keys in `Cpanel::Security::Advisor::Assessors::SSH` begin with *SSH*. What follows is a terse, but meaningful phrase using underscores rather than spaces. 59 | 60 | Here are some additional examples of helpful keys that are currently in use: 61 | 62 | * `Apache_vhosts_not_segmented` 63 | * `Brute_protection_enabled` 64 | * `Kernel_kernelcare_update_available` 65 | 66 | Once a pull request is merged into master, the static keys should never change. The keys are used to track message history, and changing them will result in the same issue that they are meant to solve; i.e., duplicate notifications for previously reported alerts. 67 | 68 | ### Preventing notification of some advice 69 | 70 | It may be desirable, in some cases, to have a piece of advice available interactively from WHM, but not send automated notifications about it. If so, you can direct this behavior in the advice creation: 71 | 72 | ```perl 73 | $security_advisor_obj->add_advice( 74 | { 75 | 'key' => 'EntropyChat_is_running', 76 | 'block_notify' => 1, # <--- any defined, nonzero value will do! 77 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 78 | 'text' => ['Entropy Chat is running.'], 79 | 'suggestion' => [ 80 | 'Turn off Entropy Chat in the “[output,url,_1,Service Manager,_2,_3]” page.', 81 | $self->base_path('scripts/srvmng'), 82 | 'target', 83 | '_blank' 84 | ], 85 | } 86 | ); 87 | ``` 88 | 89 | It won't change the behavior when Security Advisor is used in WHM, but the automated notification script will skip this advice. 90 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013, cPanel, Inc. 2 | # All rights reserved. 3 | # http://cpanel.net 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | # * Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # * Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # * Neither the name of the owner nor the names of its contributors may 13 | # be used to endorse or promote products derived from this software 14 | # without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 20 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | IS_SANDBOX=$(shell perl -e 'print "yes" if -e q{/var/cpanel/dev_sandbox}') 2 | PKG=$(shell pwd)/pkg 3 | 4 | all: 5 | @echo 'This Makefile is only used during the build process.' 6 | @echo 'Please update to cPanel & WHM 11.40 or use the pkg/install script.' 7 | 8 | define build_rules 9 | @[ $(IS_SANDBOX) = yes ] || exit 1 10 | rm -fr $(1)/Cpanel/Security/Advisor.pm $(1)/Cpanel/Security/Advisor $(1)/whostmgr/templates/securityadvisor 11 | mkdir -p $(1)/Cpanel/Security/Advisor/Assessors $(1)/whostmgr/docroot/templates/securityadvisor 12 | for i in $(PKG)/Cpanel/Security/Advisor.pm \ 13 | $(PKG)/Cpanel/Security/Advisor/Assessors.pm $(PKG)/Cpanel/Security/Advisor/Assessors/*.pm; do \ 14 | stripped=`echo $$i | sed -e 's,^$(PKG),,'`; \ 15 | rm -fr $(1)/$$stripped; $(2) $$i $(1)/$$stripped; \ 16 | done 17 | for i in $(PKG)/templates/*.tmpl; do \ 18 | stripped=`basename $$i`; \ 19 | cp -f $$i $(1)/whostmgr/docroot/templates/securityadvisor/$$stripped; \ 20 | perl -i -pe 's{/addon_plugins/}{}g' $(1)/whostmgr/docroot/templates/securityadvisor/$$stripped; \ 21 | done 22 | $(2) $(PKG)/icon/ico-security-advisor.png $(1)/whostmgr/docroot/themes/x/icons/ 23 | mkdir -m 700 -p $(1)/whostmgr/docroot/cgi/securityadvisor 24 | $(2) $(PKG)/cgi/addon_securityadvisor.cgi $(1)/whostmgr/docroot/cgi/securityadvisor/index.cgi 25 | endef 26 | 27 | sandbox: 28 | $(call build_rules,/usr/local/cpanel,ln -sf) 29 | 30 | publish: 31 | [ -n "$(DESTDIR)" ] || exit 1 32 | $(call build_rules,$(DESTDIR)/cpanel,cp -f) 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cPanel Security Advisor README 2 | 3 | The cPanel Security Advisor analyzes the configuration of a cPanel & WHM system to make recommendations that improve system security. 4 | 5 | ## Installation 6 | 7 | Installing from the GitHub repository is only needed if you want to contribute to the development of the security advisor, or you simply want the latest changes before they are distributed with cPanel & WHM. To install from here, you need to clone the repository, then run the installer. 8 | 9 | 1. /usr/local/cpanel/3rdparty/bin/git clone https://github.com/Cpanelinc/addon_securityadvisor.git 10 | 2. cd addon_securityadvisor/pkg 11 | 3. ./install 12 | 13 | The next time cPanel & WHM updates it will over write your changes. To keep the GitHub version you need to create a *postupcp* hook that re-runs the installer at the end of the update. 14 | 15 | ## Usage 16 | 17 | The Security Advisor is found within the Security Center of WHM. It requires root privileges to access. There are two ways to find the Advisor. 18 | 19 | 1. Log into WHM with root privileges 20 | 2. Click Security Center 21 | 3. Click Security Advisor 22 | 23 | OR 24 | 25 | 1. Log into WHM with root privileges 26 | 2. Search for Security Advisor 27 | 3. Click Security Advisor 28 | 29 | Accessing the Security Advisor begins the analysis of your system. 30 | 31 | ## Contributing 32 | 33 | Contributions are welcome. 34 | 35 | Please carefully read the [Contribution Document](CONTRIBUTING.md) for workflow and contributing developer information. 36 | 37 | ## License 38 | 39 | See the LICENSE file in the project root directory. 40 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor; 2 | 3 | # Copyright (c) 2020, cPanel, L.L.C. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | =pod 30 | 31 | =encoding utf-8 32 | 33 | =head1 NAME 34 | 35 | Cpanel::Security::Advisor - cPanel Security Advisor 36 | 37 | =head1 SYNOPSYS 38 | 39 | my $comet = Cpanel::Comet::Mock->new(); 40 | 41 | Cpanel::LoadModule::load_perl_module('Cpanel::Security::Advisor'); 42 | my $advisor = Cpanel::Security::Advisor->new( 'comet' => $comet, 'channel' => $CHANNEL ); 43 | 44 | my ( $merged, @result ) = Capture::Tiny::capture_merged( 45 | sub { 46 | $advisor->generate_advice(); 47 | } 48 | ); 49 | 50 | my $msgs = $comet->get_messages($CHANNEL); 51 | foreach my $msg ( @{$msgs} ) { 52 | my $msg_ref = Cpanel::AdminBin::Serializer::Load($msg); 53 | 54 | .... 55 | 56 | } 57 | 58 | =cut 59 | 60 | use strict; 61 | 62 | our $VERSION = 1.04; 63 | 64 | use Cpanel::Config::LoadCpConf (); 65 | use Cpanel::Logger (); 66 | use Cpanel::JSON (); 67 | use Cpanel::Locale (); 68 | use Cpanel::LoadModule (); 69 | use Cpanel::LoadModule::AllNames (); 70 | 71 | use Try::Tiny; 72 | 73 | our $ADVISE_GOOD = 1; 74 | our $ADVISE_INFO = 2; 75 | our $ADVISE_WARN = 4; 76 | our $ADVISE_BAD = 8; 77 | 78 | # provides string type given integer value 79 | sub _lookup_advise_type { 80 | my $int = shift; 81 | my $ADVISE_TYPES_LOOKUP = { 82 | $ADVISE_GOOD => q{ADVISE_GOOD}, 83 | $ADVISE_INFO => q{ADVISE_INFO}, 84 | $ADVISE_WARN => q{ADVISE_WARN}, 85 | $ADVISE_BAD => q{ADVISE_BAD}, 86 | }; 87 | return ( $int and $ADVISE_TYPES_LOOKUP->{$int} ) ? $ADVISE_TYPES_LOOKUP->{$int} : undef; 88 | } 89 | 90 | # provides integer value given type string 91 | sub _lookup_advise_type_by_value { 92 | my $type = shift; 93 | my $ADVISE_VALUES_LOOKUP = { 94 | q{ADVISE_GOOD} => $ADVISE_GOOD, 95 | q{ADVISE_INFO} => $ADVISE_INFO, 96 | q{ADVISE_WARN} => $ADVISE_WARN, 97 | q{ADVISE_BAD} => $ADVISE_BAD, 98 | }; 99 | return ( $type and $ADVISE_VALUES_LOOKUP->{$type} ) ? $ADVISE_VALUES_LOOKUP->{$type} : undef; 100 | } 101 | 102 | =head1 ADVISE TYPES 103 | 104 | =head2 ADVISE_GOOD 105 | 106 | =over 107 | 108 | Changes DO NOT send iContact notices 109 | 110 | =back 111 | 112 | All is well 113 | 114 | =head2 ADVISE_INFO 115 | 116 | =over 117 | 118 | Changes send iContact notices for 11.56.0.14 and below, Changes DO NOT send iContact notices for 11.56.0.15 and above 119 | 120 | =back 121 | 122 | For items that may not be be actionable soon but should know about. 123 | If there is uncertainty if the admin has control over the item or 124 | if we have less than 90% confidence that its not a false positive. 125 | 126 | =head2 ADVISE_WARN 127 | 128 | =over 129 | 130 | Changes send iContact notices 131 | 132 | =back 133 | 134 | For items that should be actioned soon. These should be 135 | 95% confidence or better that it is not a false positive. 136 | 137 | =head2 ADVISE_BAD 138 | 139 | =over 140 | 141 | Changes send iContact notices 142 | 143 | =back 144 | 145 | For items that should be actioned now. These should be 99% 146 | confidence or better that it is not a false positive. 147 | 148 | =cut 149 | 150 | sub new { 151 | my ( $class, %options ) = @_; 152 | 153 | die "No comet object provided" unless ( $options{'comet'} ); 154 | die "No comet channel provided" unless ( $options{'channel'} ); 155 | 156 | my @assessors; 157 | 158 | my $self = bless { 159 | 'assessors' => \@assessors, 160 | 'logger' => Cpanel::Logger->new(), 161 | 'cpconf' => scalar Cpanel::Config::LoadCpConf::loadcpconf(), 162 | '_version' => $VERSION, 163 | '_cache' => {}, 164 | 'comet' => $options{'comet'}, 165 | 'channel' => $options{'channel'}, 166 | 'locale' => Cpanel::Locale->get_handle(), 167 | }, $class; 168 | 169 | local @INC = ( @INC, '/var/cpanel/addons/securityadvisor/perl' ); 170 | my @modules = sort keys %{ Cpanel::LoadModule::AllNames::get_loadable_modules_in_namespace('Cpanel::Security::Advisor::Assessors') }; 171 | foreach my $module_name (@modules) { 172 | my $object; 173 | try { 174 | Cpanel::LoadModule::load_perl_module($module_name); 175 | $object = $module_name->new($self); 176 | } 177 | catch { 178 | $self->{'logger'}->warn("Failed to load $module_name: $_"); 179 | $self->_internal_message( { type => 'mod_load', state => 0, module => $module_name, message => "$_" } ); 180 | }; 181 | next unless $object; 182 | 183 | push @assessors, { name => $module_name, object => $object }; 184 | my $runtime = ( $object->can('estimated_runtime') ? $object->estimated_runtime() : 1 ); 185 | $self->_internal_message( { type => 'mod_load', state => 1, module => $module_name, runtime => $runtime } ); 186 | } 187 | 188 | return $self; 189 | } 190 | 191 | sub generate_advice { 192 | my ($self) = @_; 193 | 194 | $self->_internal_message( { type => 'scan_run', state => 0 } ); 195 | foreach my $mod ( sort { lc $a->{'name'} cmp lc $b->{'name'} } @{ $self->{'assessors'} } ) { 196 | my $module = $mod->{'name'}; 197 | my $version_ref = "$module"->can('version'); 198 | my $module_version = $version_ref ? $version_ref->() : ''; 199 | 200 | $self->_internal_message( { type => 'mod_run', state => 0, module => $mod->{name}, 'version' => $module_version } ); 201 | eval { $mod->{object}->generate_advice(); }; 202 | $self->_internal_message( { type => 'mod_run', state => ( $@ ? -1 : 1 ), module => $mod->{name}, message => "$@", 'version' => $module_version } ); 203 | } 204 | $self->_internal_message( { type => 'scan_run', state => 1 } ); 205 | $self->{'comet'}->purgeclient(); 206 | return; 207 | } 208 | 209 | sub _internal_message { 210 | my ( $self, $data ) = @_; 211 | $self->{'comet'}->add_message( 212 | $self->{'channel'}, 213 | Cpanel::JSON::Dump( 214 | { 215 | channel => $self->{'channel'}, 216 | data => $data 217 | } 218 | ), 219 | ); 220 | return; 221 | } 222 | 223 | sub add_advice { 224 | my ( $self, $advice ) = @_; 225 | 226 | # Some assessor modules call methods directly on instances of this class, 227 | # and some use wrapper methods, so try to figure out the module name 228 | # regardless of which path we took. 229 | my ( $module, $function ); 230 | foreach my $level ( 1, 3 ) { 231 | my $caller = ( caller($level) )[3]; 232 | if ( $caller =~ /(Cpanel::Security::Advisor::Assessors::.+)::([^:]+)$/ ) { 233 | ( $module, $function ) = ( $1, $2 ); 234 | last; 235 | } 236 | } 237 | 238 | $self->{'comet'}->add_message( 239 | $self->{'channel'}, 240 | Cpanel::JSON::Dump( 241 | { 242 | channel => $self->{'channel'}, 243 | data => { 244 | type => 'mod_advice', 245 | module => $module, 246 | function => $function, 247 | advice => $advice, 248 | } 249 | } 250 | ), 251 | ); 252 | return; 253 | } 254 | 255 | 1; 256 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors; 2 | 3 | # Copyright (c) 2021, cPanel, L.L.C. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use warnings; 31 | 32 | our $VERSION = 1.1; 33 | 34 | use Cpanel::SafeRun::Full (); 35 | use Cpanel::Version::Compare (); 36 | use Cpanel::Exception (); 37 | 38 | sub new { 39 | my ( $class, $security_advisor_obj ) = @_; 40 | 41 | my $self = bless { 42 | 'security_advisor_obj' => $security_advisor_obj, 43 | '_version' => $VERSION 44 | }, $class; 45 | 46 | return $self; 47 | } 48 | 49 | sub base_path { 50 | my ( $self, $path ) = @_; 51 | 52 | if ( $ENV{'REQUEST_URI'} =~ m{cgi/securityadvisor} ) { 53 | return '../../' . $path; 54 | } 55 | elsif ( $ENV{'REQUEST_URI'} =~ m{cgi/addons/securityadvisor} ) { 56 | return '../../../' . $path; 57 | } 58 | 59 | return '../' . $path; 60 | } 61 | 62 | sub add_advice { 63 | my ( $self, %opts ) = @_; 64 | 65 | return $self->{'security_advisor_obj'}->add_advice( {%opts} ); 66 | } 67 | 68 | sub add_good_advice { 69 | my ( $self, %opts ) = @_; 70 | 71 | return $self->add_advice( %opts, 'type' => $Cpanel::Security::Advisor::ADVISE_GOOD ); 72 | } 73 | 74 | sub add_info_advice { 75 | my ( $self, %opts ) = @_; 76 | 77 | return $self->add_advice( %opts, 'type' => $Cpanel::Security::Advisor::ADVISE_INFO ); 78 | } 79 | 80 | sub add_warn_advice { 81 | my ( $self, %opts ) = @_; 82 | 83 | return $self->add_advice( %opts, 'type' => $Cpanel::Security::Advisor::ADVISE_WARN ); 84 | } 85 | 86 | sub add_bad_advice { 87 | my ( $self, %opts ) = @_; 88 | 89 | return $self->add_advice( %opts, 'type' => $Cpanel::Security::Advisor::ADVISE_BAD ); 90 | } 91 | 92 | sub get_available_rpms { 93 | my ($self) = @_; 94 | 95 | my $cache = ( $self->{'security_advisor_obj'}->{'_cache'} //= {} ); 96 | 97 | return $cache->{'available_rpms'} if $cache->{'available_rpms'}; 98 | 99 | require Cpanel::FindBin; 100 | my $output = Cpanel::SafeRun::Full::run( 101 | 'program' => Cpanel::FindBin::findbin('yum'), 102 | 'args' => [qw/-d 0 list all/], 103 | 'timeout' => 90, 104 | ); 105 | 106 | if ( $output->{'status'} ) { 107 | $cache->{'available_rpms'} = { 108 | map { m{\A(\S+)\.[^.\s]+\s+(\S+)} ? ( $1 => $2 ) : () } 109 | split( m/\n/, $output->{'stdout'} ) 110 | }; 111 | } 112 | 113 | $cache->{'timed_out'} = 1 if $output->{'timeout'}; 114 | $cache->{'died'} = 1 if $output->{'exit_value'} || $output->{'died_from_signal'}; 115 | 116 | return $cache->{'available_rpms'}; 117 | } 118 | 119 | sub get_installed_rpms { 120 | my ($self) = @_; 121 | 122 | my $cache = ( $self->{'security_advisor_obj'}->{'_cache'} //= {} ); 123 | 124 | return $cache->{'installed_rpms'} if $cache->{'installed_rpms'}; 125 | 126 | require Cpanel::FindBin; 127 | my $output = Cpanel::SafeRun::Full::run( 128 | 'program' => Cpanel::FindBin::findbin('rpm'), 129 | 'args' => [ '-qa', '--queryformat', '%{NAME} %{VERSION}-%{RELEASE}\n' ], 130 | 'timeout' => 30, 131 | ); 132 | 133 | if ( $output->{'status'} ) { 134 | my %installed; 135 | for my $line ( split( "\n", $output->{'stdout'} ) ) { 136 | chomp $line; 137 | my ( $rpm, $version ) = split( qr/\s+/, $line, 2 ); 138 | if ( $installed{$rpm} ) { 139 | my ( $this_version, $this_release ) = split( m/-/, $version, 2 ); 140 | my ( $installed_version, $installed_release ) = split( m/-/, $installed{$rpm}, 2 ); 141 | 142 | next if ( Cpanel::Version::Compare::compare( $installed_version, '>', $this_version ) || ( $this_version eq $installed_version && Cpanel::Version::Compare::compare( $installed_release, '>', $this_release ) ) ); 143 | } 144 | $installed{$rpm} = $version; 145 | } 146 | $cache->{'installed_rpms'} = \%installed; 147 | } 148 | 149 | $cache->{'timed_out'} = 1 if $output->{'timeout'}; 150 | $cache->{'died'} = 1 if $output->{'exit_value'} || $output->{'died_from_signal'}; 151 | 152 | return $cache->{'installed_rpms'}; 153 | } 154 | 155 | sub get_running_kernel_type { 156 | my ($kallsyms) = Cpanel::LoadFile::loadfile('/proc/kallsyms'); 157 | 158 | my $redhat_release = Cpanel::LoadFile::loadfile('/etc/redhat-release'); 159 | my $kernel_type = 160 | ( ( $kallsyms =~ /\[(kmod)?lve\]/ ) && ( $redhat_release =~ /CloudLinux/ ) ) ? 'cloudlinux' 161 | : ( $kallsyms =~ /grsec/ ) ? 'grsec' 162 | : ( -e '/etc/redhat-release' ) ? 'other' 163 | : ''; 164 | return $kernel_type; 165 | } 166 | 167 | sub _lh { 168 | my ($self) = @_; 169 | return $self->{'security_advisor_obj'}{'locale'}; 170 | } 171 | 172 | sub get_cagefsctl_bin { 173 | my ($self) = @_; 174 | 175 | my @bins = ( 176 | '/usr/bin/cagefsctl', 177 | '/usr/sbin/cagefsctl', 178 | ); 179 | 180 | foreach my $bin (@bins) { 181 | return $bin if ( -x $bin ); 182 | } 183 | 184 | return; 185 | } 186 | 187 | sub cagefs_is_enabled { 188 | my ($self) = @_; 189 | 190 | my $cagefsctl = $self->get_cagefsctl_bin(); 191 | return 0 unless $cagefsctl; 192 | 193 | require Cpanel::SafeRun::Object; 194 | my $run = Cpanel::SafeRun::Object->new( 195 | program => $cagefsctl, 196 | args => ["--cagefs-status"] 197 | ); 198 | 199 | return ( $run->stdout() =~ /enabled/i ) ? 1 : 0; 200 | } 201 | 202 | 1; 203 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/Brute.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::Brute; 2 | 3 | # Copyright (c) 2013, cPanel, Inc. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use Cpanel::Config::Hulk (); 31 | use Cpanel::PsParser (); 32 | use Cpanel::LoadFile (); 33 | 34 | use base 'Cpanel::Security::Advisor::Assessors'; 35 | 36 | sub generate_advice { 37 | my ($self) = @_; 38 | $self->_check_for_brute_force_protection(); 39 | 40 | return 1; 41 | } 42 | 43 | sub _check_for_brute_force_protection { 44 | my ($self) = @_; 45 | 46 | my $cphulk_enabled = Cpanel::Config::Hulk::is_enabled(); 47 | 48 | my $security_advisor_obj = $self->{'security_advisor_obj'}; 49 | 50 | if ($cphulk_enabled) { 51 | $security_advisor_obj->add_advice( 52 | { 53 | 'key' => 'Brute_protection_enabled', 54 | 'type' => $Cpanel::Security::Advisor::ADVISE_GOOD, 55 | 'text' => $self->_lh->maketext('cPHulk Brute Force Protection is enabled.'), 56 | } 57 | ); 58 | 59 | } 60 | elsif ( -x "/usr/sbin/csf" ) { 61 | if ( -e "/etc/csf/csf.disable" ) { 62 | if ( -e "/usr/local/cpanel/whostmgr/docroot/cgi/configserver/csf" ) { 63 | $security_advisor_obj->add_advice( 64 | { 65 | 'key' => 'Brute_csf_installed_but_disabled_1', 66 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 67 | 'text' => $self->_lh->maketext('CSF is installed, but appears to be disabled.'), 68 | 'suggestion' => $self->_lh->maketext( 69 | 'Click “Firewall Enable“ in the “[output,url,_1,ConfigServer Security & Firewall,_2,_3]” area. Alternately, run “csf -e“ from the command line.', 70 | $self->base_path('cgi/configserver/csf.cgi'), 71 | 'target', 72 | '_blank' 73 | ), 74 | } 75 | ); 76 | } 77 | else { 78 | $security_advisor_obj->add_advice( 79 | { 80 | 'key' => 'Brute_csf_installed_but_disabled_2', 81 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 82 | 'text' => $self->_lh->maketext('CSF is installed, but appears to be disabled.'), 83 | 'suggestion' => $self->_lh->maketext('Run “csf -e“ from the command line.'), 84 | } 85 | ); 86 | } 87 | } 88 | elsif ( check_lfd_running() ) { 89 | $security_advisor_obj->add_advice( 90 | { 91 | 'key' => 'Brute_csf_installed_lfd_running', 92 | 'type' => $Cpanel::Security::Advisor::ADVISE_GOOD, 93 | 'text' => $self->_lh->maketext('CSF is installed, and LFD is running.'), 94 | } 95 | ); 96 | } 97 | else { 98 | if ( -e "/usr/local/cpanel/whostmgr/docroot/cgi/configserver/csf" ) { 99 | $security_advisor_obj->add_advice( 100 | { 101 | 'key' => 'Brute_csf_installed_lfd_not_running_1', 102 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 103 | 'text' => $self->_lh->maketext('CSF is installed, but LFD is not running.'), 104 | 'suggestion' => $self->_lh->maketext( 105 | 'Click “lfd Restart“ in the “[output,url,_1,ConfigServer Security & Firewall,_2,_3]” area. Alternately, run “csf --lfd restart“ from the command line.', 106 | $self->base_path('cgi/configserver/csf.cgi'), 107 | 'target', 108 | '_blank' 109 | ), 110 | } 111 | ); 112 | } 113 | else { 114 | $security_advisor_obj->add_advice( 115 | { 116 | 'key' => 'Brute_csf_installed_lfd_not_running_2', 117 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 118 | 'text' => $self->_lh->maketext('CSF is installed, but LFD is not running.'), 119 | 'suggestion' => $self->_lh->maketext('Run “csf --lfd restart“ from the command line.'), 120 | } 121 | ); 122 | } 123 | } 124 | } 125 | else { 126 | $security_advisor_obj->add_advice( 127 | { 128 | 'key' => 'Brute_force_protection_not_enabled', 129 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 130 | 'text' => $self->_lh->maketext('No brute force protection detected'), 131 | 'suggestion' => $self->_lh->maketext( 132 | 'Enable cPHulk Brute Force Protection in the “[output,url,_1,cPHulk Brute Force Protection,_2,_3]” area.', 133 | $self->base_path('scripts7/cphulk/config'), 134 | 'target', 135 | '_blank' 136 | 137 | ), 138 | } 139 | ); 140 | } 141 | 142 | return 1; 143 | } 144 | 145 | sub check_lfd_running { 146 | my $v_pid = Cpanel::LoadFile::load_if_exists("/var/run/lfd.pid"); 147 | if ( $v_pid && $v_pid =~ m/^[0-9]+$/ ) { 148 | chomp($v_pid); 149 | my $parsed_ps = Cpanel::PsParser::fast_parse_ps( 'want_pid' => $v_pid ); 150 | if ( $parsed_ps && $parsed_ps->[0]->{'command'} =~ m{^lfd\b} ) { 151 | return 1; 152 | } 153 | } 154 | return 0; 155 | } 156 | 157 | 1; 158 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/ClamAV.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::ClamAV; 2 | 3 | # Copyright (c) 2013, cPanel, Inc. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use warnings; 31 | use Cpanel::FindBin (); 32 | use Cpanel::SafeRun::Errors (); 33 | use Cpanel::Version (); 34 | 35 | use base 'Cpanel::Security::Advisor::Assessors'; 36 | 37 | sub generate_advice { 38 | my ($self) = @_; 39 | 40 | my $cpanel_version = Cpanel::Version::getversionnumber(); 41 | 42 | # Only show the clamav advice if running version 86 and lower. 43 | # Otherwise we will recommend ImunifyAV in the Imunify360 module. 44 | if ( Cpanel::Version::compare( $cpanel_version, '<=', '11.86' ) ) { 45 | return 0 if $self->_check_clamav(); 46 | } 47 | return 1; 48 | } 49 | 50 | sub _check_clamav { 51 | my ($self) = @_; 52 | 53 | my $security_advisor_obj = $self->{'security_advisor_obj'}; 54 | 55 | my $install_clamav = 'scripts2/manage_plugins'; 56 | 57 | $self->_find_clamav(); 58 | 59 | if ( !$self->{clamav}{clamscan}{bin} && !$self->{clamav}{freshclam}{bin} ) { 60 | $security_advisor_obj->add_advice( 61 | { 62 | 'key' => 'ClamAV_not_installed', 63 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 64 | 'text' => $self->_lh->maketext('ClamAV is not installed.'), 65 | 'suggestion' => $self->_lh->maketext( 66 | 'Install ClamAV within "[output,url,_1,Manage Plugins,_2,_3]".', 67 | $self->base_path($install_clamav), 'target', '_blank', 68 | ), 69 | } 70 | ); 71 | } 72 | elsif ( !$self->{clamav}{clamscan}{bin} ) { 73 | $security_advisor_obj->add_advice( 74 | { 75 | 'key' => 'ClamAV_binary_not_installed', 76 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 77 | 'text' => $self->_lh->maketext('ClamAV clamscan binary is not installed.'), 78 | 'suggestion' => $self->_lh->maketext( 79 | 'Install ClamAV within "[output,url,_1,Manage Plugins,_2,_3]".', 80 | $self->base_path($install_clamav), 'target', '_blank', 81 | ), 82 | } 83 | ); 84 | } 85 | elsif ( !$self->{clamav}{freshclam}{bin} ) { 86 | $security_advisor_obj->add_advice( 87 | { 88 | 'key' => 'ClamAV_freshclam_not_installed', 89 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 90 | 'text' => $self->_lh->maketext('ClamAV freshclam binary is not installed.'), 91 | 'suggestion' => $self->_lh->maketext( 92 | 'Install ClamAV within "[output,url,_1,Manage Plugins,_2,_3]".', 93 | $self->base_path($install_clamav), 'target', '_blank', 94 | ), 95 | } 96 | ); 97 | } 98 | else { 99 | $self->_get_clam_version(); 100 | 101 | my @bad_clams; 102 | 103 | push( @bad_clams, 'clamscan' ) if !defined $self->{clamav}{clamscan}{version}; 104 | push( @bad_clams, 'freshclam' ) if !defined $self->{clamav}{freshclam}{version}; 105 | 106 | if (@bad_clams) { 107 | 108 | $security_advisor_obj->add_advice( 109 | { 110 | 'key' => 'ClamAV_failed_to_get_version', 111 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 112 | 'text' => $self->_lh->maketext( 'Failed to get version for [list_and,_1].', \@bad_clams ), 113 | 'suggestion' => $self->_lh->maketext( 114 | 'clamscan version: [_1]
freshclam version: [_2]

Reinstall ClamAV within "[output,url,_3,Manage Plugins,_4,_5]".', $self->{clamav}{clamscan}{version_str}, $self->{clamav}{freshclam}{version_str}, $self->base_path($install_clamav), 'target', '_blank', 115 | ), 116 | } 117 | ); 118 | 119 | return $self; 120 | } 121 | 122 | if ( $self->{clamav}{clamscan}{version} ne $self->{clamav}{freshclam}{version} ) { 123 | $security_advisor_obj->add_advice( 124 | { 125 | 'key' => 'ClamAV_freshclam_and_clamscan_binaries_different_versions', 126 | 'type' => $Cpanel::Security::Advisor::ADVISE_WARN, 127 | 'text' => $self->_lh->maketext('ClamAV freshclam and clamscan binaries are different versions.'), 128 | 'suggestion' => $self->_lh->maketext( 129 | 'clamscan version: [_1]
freshclam version: [_2]

Reinstall ClamAV within "[output,url,_3,Manage Plugins,_4,_5]".', $self->{clamav}{clamscan}{version_str}, $self->{clamav}{freshclam}{version_str}, $self->base_path($install_clamav), 'target', '_blank', 130 | ), 131 | } 132 | ); 133 | } 134 | } 135 | return $self; 136 | } 137 | 138 | sub _find_clamav { 139 | my ($self) = @_; 140 | 141 | my @paths = qw{ /usr/local/cpanel/3rdparty/bin /usr/bin /usr/local/bin /bin /sbin /usr/sbin /usr/local/sbin }; 142 | 143 | $self->{clamav}{clamscan}{bin} = Cpanel::FindBin::findbin( 'clamscan', 'path' => @paths ); 144 | $self->{clamav}{freshclam}{bin} = Cpanel::FindBin::findbin( 'freshclam', 'path' => @paths ); 145 | 146 | return $self; 147 | } 148 | 149 | sub _get_clam_version { 150 | my ($self) = @_; 151 | 152 | foreach my $clam ( 'clamscan', 'freshclam' ) { 153 | chomp( my $version = Cpanel::SafeRun::Errors::saferunallerrors( $self->{clamav}{$clam}{bin}, '-V' ) ); 154 | $self->{clamav}{$clam}{version_str} = $version || 'Failed to obtain version!'; 155 | if ( $version =~ /^ClamAV (\d+\.\d+\.\d+)/m ) { 156 | $self->{clamav}{$clam}{version} = $1; 157 | } 158 | } 159 | 160 | return $self; 161 | } 162 | 163 | 1; 164 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/Iptables.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::Iptables; 2 | 3 | # Copyright (c) 2013, cPanel, Inc. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use base 'Cpanel::Security::Advisor::Assessors'; 31 | use Cpanel::SafeRun::Simple; 32 | 33 | sub generate_advice { 34 | my ($self) = @_; 35 | $self->_is_iptables_active(); 36 | 37 | return 1; 38 | } 39 | 40 | sub _is_iptables_active { 41 | 42 | my ($self) = @_; 43 | 44 | my $security_advisor_obj = $self->{'security_advisor_obj'}; 45 | 46 | if ( -x '/etc/init.d/iptables' ) { 47 | my $status_check = `/etc/init.d/iptables status`; 48 | 49 | # need a better way to check this 50 | if ( $status_check =~ m/not running/i ) { 51 | $security_advisor_obj->add_advice( 52 | { 53 | 'key' => 'Iptables_firewall_not_running', 54 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 55 | 'text' => $self->_lh->maketext('Firewall is not running'), 56 | 'suggestion' => $self->_lh->maketext('This might be a simple matter of executing "/etc/init.d/iptables start"'), 57 | }, 58 | ); 59 | } 60 | } 61 | 62 | return 1; 63 | } 64 | 65 | 1; 66 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/Jail.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::Jail; 2 | 3 | # Copyright (c) 2021, cPanel, L.L.C. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use Cpanel::Config::LoadCpConf (); 31 | use Cpanel::PwCache (); 32 | use Cpanel::Config::Users (); 33 | use Cpanel::Version (); 34 | 35 | use base 'Cpanel::Security::Advisor::Assessors'; 36 | 37 | sub generate_advice { 38 | my ($self) = @_; 39 | $self->_check_for_unjailed_users(); 40 | 41 | return 1; 42 | } 43 | 44 | sub _check_for_unjailed_users { 45 | my ($self) = @_; 46 | 47 | my $security_advisor_obj = $self->{'security_advisor_obj'}; 48 | 49 | if ( !$self->cagefs_is_enabled() ) { 50 | if ( -e '/var/cpanel/conf/jail/flags/mount_usr_bin_suid' ) { 51 | $security_advisor_obj->add_advice( 52 | { 53 | 'key' => 'Jail_mounted_user_bin_suid', 54 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 55 | 'text' => $self->_lh->maketext('Jailshell is mounting /usr/bin suid, which allows escaping the jail via crontab.'), 56 | 'suggestion' => $self->_lh->maketext( 57 | 'Disable “Jailed /usr/bin mounted suid" in the “[output,url,_1,Tweak Settings,_2,_3]” area', 58 | $self->base_path('scripts2/tweaksettings?find=jailmountusrbinsuid'), 59 | 'target', 60 | '_blank' 61 | ), 62 | } 63 | ); 64 | } 65 | 66 | Cpanel::PwCache::init_passwdless_pwcache(); 67 | my %cpusers = map { $_ => undef } Cpanel::Config::Users::getcpusers(); 68 | my %wheel_users_hash = map { $_ => 1 } split( ' ', ( getgrnam('wheel') )[3] ); 69 | delete $wheel_users_hash{'root'}; # We don't care about root being in the wheel group 70 | 71 | # Versions older than 11.60 will need to use the old PwCache location 72 | my $curr_version = $Cpanel::Version::MAJORVERSION; 73 | my $pwcache_ref; 74 | 75 | if ( Cpanel::Version::compare( $curr_version, '>=', '11.60' ) ) { 76 | require Cpanel::PwCache::Build; 77 | $pwcache_ref = Cpanel::PwCache::Build::fetch_pwcache(); 78 | } 79 | else { 80 | $pwcache_ref = Cpanel::PwCache::fetch_pwcache(); 81 | } 82 | 83 | my @users = map { $_->[0] } grep { exists $cpusers{ $_->[0] } && $_->[8] && $_->[8] !~ m{(?:false|nologin|(?:no|jail)shell)} } @$pwcache_ref; #aka users without jail or noshell 84 | my @users_without_jail; 85 | my @wheel_users; 86 | 87 | foreach my $user (@users) { 88 | if ( $wheel_users_hash{$user} ) { 89 | push( @wheel_users, $user ); 90 | } 91 | else { 92 | push( @users_without_jail, $user ); 93 | } 94 | } 95 | 96 | @users_without_jail = sort @users_without_jail; # Always notify in the same order 97 | if ( scalar @users_without_jail > 100 ) { 98 | splice( @users_without_jail, 100 ); 99 | push @users_without_jail, '..truncated..'; 100 | } 101 | 102 | if (@wheel_users) { 103 | $security_advisor_obj->add_advice( 104 | { 105 | 'key' => 'Jail_wheel_users_exist', 106 | 'type' => $Cpanel::Security::Advisor::ADVISE_INFO, 107 | 'text' => $self->_lh->maketext('Users with wheel group access:'), 108 | 'suggestion' => $self->_lh->maketext( '[list_and,_1].', \@wheel_users ) . '

' . $self->_lh->maketext( 109 | 'Users in the “[asis,wheel]” group may run “[asis,su]”. Consider removing these users from the “[asis,wheel]” group in the “[output,url,_1,Manage Wheel Group Users,_2,_3]” area if they do not need to be in the “[asis,wheel]” group.', 110 | $self->base_path('scripts/modwheel'), 111 | 'target', 112 | '_blank' 113 | ), 114 | } 115 | ); 116 | } 117 | 118 | if (@users_without_jail) { 119 | $security_advisor_obj->add_advice( 120 | { 121 | 'key' => 'Jail_users_running_outside_of_jail', 122 | 'type' => $Cpanel::Security::Advisor::ADVISE_WARN, 123 | 'text' => $self->_lh->maketext('Users running outside of the jail:'), 124 | 'suggestion' => $self->_lh->maketext( '[list_and,_1].', \@users_without_jail ) . '

' . $self->_lh->maketext( 125 | 'Change these users to jailshell or noshell in the “[output,url,_1,Manage Shell Access,_2,_3]” area.', 126 | $self->base_path('scripts2/manageshells'), 127 | 'target', 128 | '_blank' 129 | 130 | ), 131 | } 132 | ); 133 | } 134 | } 135 | 136 | return 1; 137 | } 138 | 139 | 1; 140 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/Mysql.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::Mysql; 2 | 3 | # Copyright (c) 2019, cPanel, L.L.C. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use Cpanel::Hostname (); 31 | use Cpanel::IP::Loopback (); 32 | use Cpanel::IP::Parse (); 33 | use Cpanel::IPv6::Has (); 34 | use Cpanel::MysqlUtils (); 35 | use Cpanel::MysqlUtils::MyCnf::Full (); 36 | use Cpanel::SafeRun::Errors (); 37 | use Cpanel::LoadFile (); 38 | eval { local $SIG{__DIE__}; require Cpanel::MysqlUtils::Connect; }; 39 | 40 | use base 'Cpanel::Security::Advisor::Assessors'; 41 | 42 | sub generate_advice { 43 | my ($self) = @_; 44 | 45 | eval { Cpanel::MysqlUtils::Connect::connect(); } if $INC{'Cpanel/MysqlUtils/Connect.pm'}; 46 | 47 | if ( !$self->_sqlcmd('SELECT 1;') ) { 48 | $self->add_bad_advice( 49 | 'key' => 'Mysql_can_not_connect_to_mysql', 50 | 'text' => $self->_lh->maketext('Cannot connect to MySQL server.'), 51 | 'suggestion' => $self->_lh->maketext( 52 | 'Enable the [output,url,_1,MySQL database service,_2].', 53 | $self->base_path('scripts/srvmng'), 54 | { 'target' => '_blank' }, 55 | ), 56 | 57 | ); 58 | return; 59 | } 60 | 61 | $self->_check_for_db_test(); 62 | $self->_check_for_anonymous_users(); 63 | $self->_check_for_public_bind_address(); 64 | 65 | return 1; 66 | } 67 | 68 | sub _sqlcmd { 69 | my ( $self, $cmd ) = @_; 70 | return Cpanel::MysqlUtils::sqlcmd( $cmd, { quiet => 1 } ); 71 | } 72 | 73 | sub _check_for_db_test { 74 | 75 | my $self = shift; 76 | 77 | my $exists = $self->_sqlcmd(qq{show databases like 'test'}); 78 | 79 | if ( !$exists ) { 80 | $self->add_good_advice( 81 | 'key' => 'Mysql_test_database_does_not_exist', 82 | 'text' => $self->_lh->maketext("[asis,MySQL] test database does not exist.") 83 | ); 84 | } 85 | else { 86 | $self->add_bad_advice( 87 | 'key' => 'Mysql_test_database_exists', 88 | 'text' => $self->_lh->maketext("[asis,MySQL] test database exists."), 89 | 'suggestion' => $self->_lh->maketext( 90 | 'Numerous attacks exploit the [asis,MySQL] test database. To remove it, run “[_1]”.', 91 | "mysql -e 'drop database test'" 92 | ), 93 | ); 94 | 95 | } 96 | 97 | return 1; 98 | } 99 | 100 | sub _check_for_anonymous_users { 101 | my $self = shift; 102 | 103 | my $ok = 1; 104 | my $ano = $self->_sqlcmd(qq{select 1 from mysql.user where user="" limit 1}); 105 | if ($ano) { 106 | $ok = 0; 107 | } 108 | 109 | for my $h ( 'localhost', Cpanel::Hostname::gethostname ) { 110 | eval { 111 | my $grant = $self->_sqlcmd(qq{SHOW GRANTS FOR ''\@'$h'}); 112 | $ok = 0 if $grant; 113 | }; 114 | } 115 | 116 | if ($ok) { 117 | $self->add_good_advice( 118 | 'key' => 'Mysql_no_anonymous_users', 119 | 'text' => $self->_lh->maketext("[asis,MySQL] check for anonymous users") 120 | ); 121 | } 122 | else { 123 | $self->add_bad_advice( 124 | 'key' => 'Mysql_found_anonymous_users', 125 | 'text' => $self->_lh->maketext("You have some anonymous [asis,MySQL] users"), 126 | 'suggestion' => $self->_lh->maketext( 'Remove [asis,MySQL] anonymous [asis,MySQL] users: [_1]', "mysql -e \"DELETE FROM mysql.user WHERE User=''; FLUSH PRIVILEGES;\"" ) 127 | ); 128 | } 129 | 130 | return 1; 131 | } 132 | 133 | sub _check_for_public_bind_address { 134 | my $self = shift; 135 | 136 | my $mycnf = Cpanel::MysqlUtils::MyCnf::Full::etc_my_cnf(); 137 | my $bind_address = $mycnf->{'mysqld'}->{'bind-address'}; 138 | my $port = $mycnf->{'mysqld'}->{'port'} || '3306'; 139 | 140 | my @deny_rules = grep { /--dport \Q$port\E/ && /-j (DROP|REJECT)/ } split /\n/, Cpanel::SafeRun::Errors::saferunnoerror( '/sbin/iptables', '--list-rules' ); 141 | my @deny_rules_6 = grep { /--dport \Q$port\E/ && /-j (DROP|REJECT)/ } split /\n/, Cpanel::SafeRun::Errors::saferunnoerror( '/sbin/ip6tables', '--list-rules' ); 142 | 143 | # From: http://dev.mysql.com/doc/refman/5.5/en/server-options.html 144 | # The server treats different types of addresses as follows: 145 | # 146 | # If the address is *, the server accepts TCP/IP connections on all server 147 | # host IPv6 and IPv4 interfaces if the server host supports IPv6, or accepts 148 | # TCP/IP connections on all IPv4 addresses otherwise. Use this address to 149 | # permit both IPv4 and IPv6 connections on all server interfaces. This value 150 | # is permitted (and is the default) as of MySQL 5.6.6. 151 | # 152 | # If the address is 0.0.0.0, the server accepts TCP/IP connections on all 153 | # server host IPv4 interfaces. This is the default before MySQL 5.6.6. 154 | # 155 | # If the address is ::, the server accepts TCP/IP connections on all server 156 | # host IPv4 and IPv6 interfaces. 157 | # 158 | # If the address is an IPv4-mapped address, the server accepts TCP/IP 159 | # connections for that address, in either IPv4 or IPv6 format. For example, 160 | # if the server is bound to ::ffff:127.0.0.1, clients can connect using 161 | # --host=127.0.0.1 or --host=::ffff:127.0.0.1. 162 | # 163 | # If the address is a “regular” IPv4 or IPv6 address (such as 127.0.0.1 or 164 | # ::1), the server accepts TCP/IP connections only for that IPv4 or IPv6 165 | # address. 166 | 167 | if ( defined($bind_address) ) { 168 | my $version = ( Cpanel::IP::Parse::parse($bind_address) )[0]; 169 | 170 | if ( Cpanel::IP::Loopback::is_loopback($bind_address) ) { 171 | $self->add_good_advice( 172 | 'key' => 'Mysql_listening_only_to_local_address', 173 | 'text' => $self->_lh->maketext("MySQL is listening only on a local address.") 174 | ); 175 | } 176 | elsif ( ( ( $version == 4 ) && @deny_rules && ( ( $bind_address =~ /ffff/i ) ? @deny_rules_6 : 1 ) ) || ( ( $version == 6 ) && @deny_rules_6 ) || ( csf_port_closed($port) ) ) { 177 | $self->add_good_advice( 178 | 'key' => 'Mysql_port_blocked_by_firewall_1', 179 | 'text' => $self->_lh->maketext("The MySQL port is blocked by the firewall, effectively allowing only local connections.") 180 | ); 181 | } 182 | else { 183 | $self->add_bad_advice( 184 | 'key' => 'Mysql_listening_on_public_address', 185 | 'text' => $self->_lh->maketext( "The MySQL service is currently configured to listen on a public address: (bind-address=[_1])", $bind_address ), 186 | 'suggestion' => $self->_lh->maketext( 187 | 'Configure bind-address=127.0.0.1 in /etc/my.cnf, or close port [_1] in the server’s firewall.', 188 | $port 189 | ), 190 | ); 191 | } 192 | } 193 | else { 194 | if ( ( @deny_rules && ( !Cpanel::IPv6::Has::system_has_ipv6() || @deny_rules_6 ) ) || ( csf_port_closed($port) ) ) { 195 | $self->add_good_advice( 196 | 'key' => 'Mysql_port_blocked_by_firewall_2', 197 | 'text' => $self->_lh->maketext("The MySQL port is blocked by the firewall, effectively allowing only local connections.") 198 | ); 199 | } 200 | else { 201 | $self->add_bad_advice( 202 | 'key' => 'Mysql_listening_on_all_interfaces', 203 | 'text' => $self->_lh->maketext('The MySQL service is currently configured to listen on all interfaces: (bind-address=*)'), 204 | 'suggestion' => $self->_lh->maketext( 205 | 'Configure bind-address=127.0.0.1 in /etc/my.cnf, or close port [_1] in the server’s firewall.', 206 | $port 207 | ), 208 | ); 209 | } 210 | } 211 | 212 | return 1; 213 | } 214 | 215 | sub config_key_contains_port { 216 | my ( $file, $key, $port ) = @_; 217 | 218 | my $csf_conf = Cpanel::LoadFile::load_if_exists($file); 219 | return if !$csf_conf; 220 | 221 | foreach my $line ( split m/\n/, $csf_conf ) { 222 | if ( $line =~ m/^\s*\Q$key\E\s*=\s*(['"])([^'"]*)\1/a ) { 223 | my $port_list = $2; 224 | foreach my $entry ( split m/,/, $port_list ) { 225 | my ( $first, $last ) = split m/:/, $entry; 226 | if ($last) { 227 | return 1 if $first <= $port && $port <= $last; 228 | } 229 | else { 230 | return 1 if $port == $first; 231 | } 232 | } 233 | } 234 | } 235 | 236 | return 0; 237 | } 238 | 239 | sub csf_port_closed { 240 | my ($port) = @_; 241 | my $contains = config_key_contains_port( '/etc/csf/csf.conf', 'TCP_IN', $port ); 242 | return if !defined $contains; 243 | return !$contains; 244 | } 245 | 246 | 1; 247 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/PHP.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::PHP; 2 | 3 | # cpanel - pkg/Cpanel/Security/Advisor/Assessors/PHP.pm 4 | # Copyright 2019 cPanel, L.L.C. 5 | # All rights Reserved. 6 | # copyright@cpanel.net http://cpanel.net 7 | 8 | # 9 | # Redistribution and use in source and binary forms, with or without 10 | # modification, are permitted provided that the following conditions are met: 11 | # * Redistributions of source code must retain the above copyright 12 | # notice, this list of conditions and the following disclaimer. 13 | # * Redistributions in binary form must reproduce the above copyright 14 | # notice, this list of conditions and the following disclaimer in the 15 | # documentation and/or other materials provided with the distribution. 16 | # * Neither the name of the owner nor the names of its contributors may 17 | # be used to endorse or promote products derived from this software 18 | # without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 24 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | use strict; 32 | use warnings; 33 | use base 'Cpanel::Security::Advisor::Assessors'; 34 | use Cpanel::Result (); 35 | 36 | my $php_ver_regex = '^ea-php(\d{2,3})$'; 37 | 38 | sub generate_advice { 39 | my ($self) = @_; 40 | $self->_check_for_php_eol(); 41 | return 1; 42 | } 43 | 44 | sub _check_for_php_eol { 45 | my $self = shift; 46 | require Cpanel::API::EA4; 47 | require Cpanel::ProgLang; 48 | 49 | my $installed_php_versions = Cpanel::ProgLang->new( type => 'php' )->get_installed_packages(); 50 | 51 | my $result = Cpanel::Result->new(); 52 | Cpanel::API::EA4::get_recommendations( undef, $result ); 53 | my $reco_data = $result->{'data'} if $result; 54 | 55 | my @reco_keys_to_consider = (); 56 | foreach my $installed_version (@$installed_php_versions) { 57 | if ( grep { $_ eq $installed_version } keys %$reco_data ) { 58 | push @reco_keys_to_consider, $installed_version; 59 | } 60 | } 61 | 62 | my @eol_php_versions = (); 63 | my $eol_reco_data; 64 | foreach my $key ( sort @reco_keys_to_consider ) { 65 | my @recos = @{ $reco_data->{$key} }; 66 | foreach my $reco (@recos) { 67 | if ( grep { $_ eq 'eol' } @{ $reco->{'filter'} } ) { 68 | 69 | # Recommendation data is same for all EOL PHP versions. Storing only one such instance 70 | # here to use later in the advice. 71 | $eol_reco_data = $reco if ( !$eol_reco_data ); 72 | push @eol_php_versions, _get_readable_php_version_format($key); 73 | } 74 | } 75 | } 76 | 77 | # Return if there is no EOL PHPs. 78 | return if scalar @eol_php_versions == 0; 79 | 80 | my $security_advisor_obj = $self->{'security_advisor_obj'}; 81 | 82 | $security_advisor_obj->add_advice( 83 | { 84 | 'key' => 'Php_versions_going_eol', 85 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 86 | 'text' => $self->_lh->maketext( '[list_and,_1] reached [output,acronym,EOL,End of Life][comment,title]', \@eol_php_versions ), 87 | 'suggestion' => _make_unordered_list( map { $_->{'text'} } @{ $eol_reco_data->{'options'} } ) . $self->_lh->maketext( 88 | 'We recommend that you use the [output,url,_1,MultiPHP Manager,_4,_5] interface to upgrade your domains to a supported version. Then, uninstall [numerate,_2,this version,these versions] in the [output,url,_3,EasyApache 4,_4,_5] interface.', 89 | $self->base_path('scripts7/multiphp-manager'), 90 | scalar @eol_php_versions, 91 | $self->base_path('scripts7/EasyApache4'), 92 | 'target', 93 | '_blank' 94 | ) 95 | . ' ' 96 | . $self->_lh->maketext( 'For more information, read [output,url,_1,PHP EOL Documentation,target,_blank].', 'https://www.php.net/supported-versions.php' ), 97 | } 98 | ); 99 | 100 | return 1; 101 | } 102 | 103 | sub _get_readable_php_version_format { 104 | my ($php_version) = @_; 105 | my $readable_php_version; 106 | if ( $php_version =~ /$php_ver_regex/ ) { 107 | my $second_part = $1; 108 | $second_part =~ s/(\d)$/\.$1/; 109 | $readable_php_version = "PHP $second_part"; 110 | } 111 | return $readable_php_version; 112 | } 113 | 114 | sub _make_unordered_list { 115 | my (@items) = @_; 116 | 117 | my $output = ''; 122 | 123 | return $output; 124 | } 125 | 126 | 1; 127 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/Passwords.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::Passwords; 2 | 3 | # Copyright (c) 2013, cPanel, Inc. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | 31 | use base 'Cpanel::Security::Advisor::Assessors'; 32 | 33 | sub generate_advice { 34 | my ($self) = @_; 35 | $self->_check_for_low_pwstrength; 36 | 37 | return 1; 38 | } 39 | 40 | sub _check_for_low_pwstrength { 41 | my ($self) = @_; 42 | 43 | my $security_advisor_obj = $self->{'security_advisor_obj'}; 44 | 45 | if ( !$security_advisor_obj->{'cpconf'}->{'minpwstrength'} || $security_advisor_obj->{'cpconf'}->{'minpwstrength'} < 25 ) { 46 | $security_advisor_obj->add_advice( 47 | { 48 | 'key' => 'Passwords_weak_permitted', 49 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 50 | 'text' => $self->_lh->maketext('Trivially weak passwords are permitted.'), 51 | 'suggestion' => $self->_lh->maketext( 52 | 'Configure Password Strength requirements in the “[output,url,_1,Password Strength Configuration,_2,_3]” area', 53 | $self->base_path('scripts/minpwstrength'), 54 | 'target', 55 | '_blank' 56 | ), 57 | } 58 | ); 59 | 60 | } 61 | elsif ( $security_advisor_obj->{'cpconf'}->{'minpwstrength'} < 50 ) { 62 | $security_advisor_obj->add_advice( 63 | { 64 | 'key' => 'Passwords_strength_requirements_are_low', 65 | 'type' => $Cpanel::Security::Advisor::ADVISE_WARN, 66 | 'text' => $self->_lh->maketext('Password strength requirements are low.'), 67 | 'suggestion' => $self->_lh->maketext( 68 | 'Configure a Default Password Strength of at least 50 in the “[output,url,_1,Password Strength Configuration,_2,_3]” area', 69 | $self->base_path('scripts/minpwstrength'), 70 | 'target', 71 | '_blank' 72 | ), 73 | } 74 | ); 75 | 76 | } 77 | elsif ( $security_advisor_obj->{'cpconf'}->{'minpwstrength'} < 65 ) { 78 | $security_advisor_obj->add_advice( 79 | { 80 | 'key' => 'Passwords_strength_requirements_are_moderate', 81 | 'type' => $Cpanel::Security::Advisor::ADVISE_INFO, 82 | 'text' => $self->_lh->maketext('Password strength requirements are moderate.'), 83 | 'suggestion' => $self->_lh->maketext( 84 | 'Configure a Default Password Strength of at least 65 in the “[output,url,_1,Password Strength Configuration,_2,_3]” area', 85 | $self->base_path('scripts/minpwstrength'), 86 | 'target', 87 | '_blank' 88 | ), 89 | } 90 | ); 91 | 92 | } 93 | else { 94 | $security_advisor_obj->add_advice( 95 | { 96 | 'key' => 'Passwords_strengths_requirements_are_strong', 97 | 'type' => $Cpanel::Security::Advisor::ADVISE_GOOD, 98 | 'text' => $self->_lh->maketext('Password strength requirements are strong.'), 99 | } 100 | ); 101 | } 102 | 103 | return 1; 104 | } 105 | 106 | 1; 107 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/Permissions.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::Permissions; 2 | 3 | # Copyright (c) 2016, cPanel, Inc. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | 31 | use base 'Cpanel::Security::Advisor::Assessors'; 32 | 33 | sub generate_advice { 34 | my ($self) = @_; 35 | $self->_check_for_unsafe_permissions(); 36 | 37 | return 1; 38 | } 39 | 40 | sub _check_for_unsafe_permissions { 41 | my ($self) = @_; 42 | 43 | my %test_files = ( 44 | '/etc/shadow' => { 'perms' => [ 0200, 0600 ], 'uid' => 0, 'gid' => 0 }, 45 | '/etc/passwd' => { 'perms' => [0644], 'uid' => 0, 'gid' => 0 } 46 | ); 47 | 48 | for my $file ( keys %test_files ) { 49 | my $expected_attributes = $test_files{$file}; 50 | my ( $current_mode, $uid, $gid ) = ( stat($file) )[ 2, 4, 5 ]; 51 | my $perms_ok = 0; 52 | foreach my $allowed_perms ( @{ $expected_attributes->{'perms'} } ) { 53 | if ( ( $allowed_perms & 07777 ) == ( $current_mode & 07777 ) ) { 54 | $perms_ok = 1; 55 | last; 56 | } 57 | } 58 | if ( !$perms_ok ) { 59 | my $expected_mode = join( ' ', map { sprintf( '%04o', $_ ) } @{ $expected_attributes->{'perms'} } ); 60 | my $actual_mode = sprintf( "%04o", $current_mode & 07777 ); 61 | $self->add_warn_advice( 62 | 'key' => q{Permissions_are_non_default}, 63 | 'text' => $self->_lh->maketext( "[_1] has non default permissions. Expected: [_2], Actual: [_3].", $file, $expected_mode, $actual_mode ), 64 | 'suggestion' => $self->_lh->maketext( "Review the permissions on [_1] to ensure they are safe", $file ), 65 | ); 66 | } 67 | 68 | if ( $uid != $expected_attributes->{'uid'} or $gid != $expected_attributes->{'gid'} ) { 69 | $self->add_warn_advice( 70 | 'key' => q{Permissions_has_non_root_users}, 71 | 'text' => $self->_lh->maketext( "[_1] has non root user and/or group", $file ), 72 | 'suggestion' => $self->_lh->maketext( "Review the ownership permissions on [_1]", $file ), 73 | ); 74 | } 75 | } 76 | 77 | return 1; 78 | } 79 | 80 | 1; 81 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/Processes.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::Processes; 2 | 3 | # Copyright (c) 2017, cPanel, Inc. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use warnings; 31 | use base 'Cpanel::Security::Advisor::Assessors'; 32 | 33 | use Cpanel::Version (); 34 | 35 | my $has_modern_cpanel_os = eval { require Cpanel::OS && ( $Cpanel::OS::VERSION // 0 ) >= 2 } 36 | or require Cpanel::Sys::OS::Check; 37 | 38 | sub version { 39 | return '1.01'; 40 | } 41 | 42 | sub generate_advice { 43 | my ($self) = @_; 44 | 45 | if ( Cpanel::Version::compare( Cpanel::Version::getversionnumber(), '<', '11.65' ) ) { 46 | require Cpanel::FileUtils::Path; 47 | require Cpanel::SafeRun::Object; 48 | require Cpanel::Sys::OS; 49 | $self->_check_for_outdated_processes_on_a_cpanel_whm_system_at_v64_or_earlier; 50 | } 51 | else { 52 | require Cpanel::Exception; 53 | require Cpanel::ProcessCheck::Outdated; 54 | $self->_check_for_outdated_processes; 55 | } 56 | 57 | return 1; 58 | } 59 | 60 | sub _check_for_outdated_processes { 61 | my ($self) = @_; 62 | 63 | my $reboot = eval { Cpanel::ProcessCheck::Outdated::reboot_suggested() }; 64 | if ( my $err = $@ ) { 65 | if ( ref $err && $err->isa('Cpanel::Exception::Service::BinaryNotFound') ) { 66 | $self->add_info_advice( 67 | key => 'Processes_unable_to_check_running_executables', 68 | text => $self->_lh->maketext('Unable to check whether running executables are up-to-date.'), 69 | suggestion => $self->_lh->maketext( 70 | 'Install the ‘[_1]’ command to check if processes are up-to-date.', 71 | $err->get('service'), 72 | ), 73 | ); 74 | return; # Cannot check any other cases, so abort. 75 | } 76 | elsif ( !ref $err || !$err->isa('Cpanel::Exception::Unsupported') ) { 77 | $self->add_warn_advice( 78 | key => 'Processes_error_while_checking_reboot', 79 | text => $self->_lh->maketext( 'Failed to determine if a reboot is necessary: [_1]', Cpanel::Exception::get_string_no_id($err) ), 80 | ); 81 | } 82 | } 83 | 84 | if ($reboot) { 85 | $self->add_bad_advice( 86 | key => 'Processes_detected_running_from_outdated_executables', 87 | text => $self->_lh->maketext('The system’s core libraries or services have been updated.'), 88 | suggestion => $self->_lh->maketext( 89 | '[output,url,_1,Reboot the server,_2,_3] to ensure the system benefits from these updates.', 90 | $self->base_path('scripts/dialog?dialog=reboot'), 91 | 'target', 92 | '_blank', 93 | ), 94 | ); 95 | return; # No need to check further. 96 | } 97 | 98 | my @services = eval { Cpanel::ProcessCheck::Outdated::outdated_services() }; 99 | if ( my $err = $@ ) { 100 | if ( !ref $err || !$err->isa('Cpanel::Exception::Unsupported') ) { 101 | $self->add_warn_advice( 102 | key => 'Processes_error_while_checking_running_services', 103 | text => $self->_lh->maketext( 'Failed to check whether active services are up-to-date: [_1]', Cpanel::Exception::get_string_no_id($err) ), 104 | ); 105 | } 106 | } 107 | 108 | if (@services) { 109 | my $restart_cmd = 'systemctl restart ' . join( q{ }, @services ); 110 | my $systemd = 111 | $has_modern_cpanel_os 112 | ? Cpanel::OS::is_systemd() 113 | : Cpanel::Sys::OS::Check::has_systemd(); 114 | 115 | if ( !$systemd ) { 116 | $restart_cmd = 'service'; 117 | @services = map { s/\.service$//r } @services; 118 | } 119 | 120 | $self->add_bad_advice( 121 | key => 'Processes_detected_running_outdated_services', 122 | text => $self->_lh->maketext( 123 | 'Detected [quant,_1,service,services] that [numerate,_1,is,are] running outdated executables: [join, ,_2]', 124 | scalar @services, 125 | \@services, 126 | ), 127 | suggestion => _make_unordered_list( 128 | $self->_lh->maketext('You must take one of the following actions to ensure the system is up-to-date:'), 129 | $self->_lh->maketext( 130 | 'Restart the listed [numerate,_1,service,services] using “[_2]”; then click “[_3]” to check non-service processes.', 131 | scalar @services, 132 | $restart_cmd, 133 | 'Scan Again', # Not translated in pkg/templates/main.tmpl 134 | ), 135 | $self->_lh->maketext( 136 | '[output,url,_1,Reboot the server,_2,_3].', 137 | $self->base_path('scripts/dialog?dialog=reboot'), 138 | 'target', 139 | '_blank', 140 | ), 141 | ), 142 | ); 143 | return; # No need to check further. 144 | } 145 | 146 | my @PIDs = eval { Cpanel::ProcessCheck::Outdated::outdated_processes() }; 147 | if ( my $err = $@ ) { 148 | if ( !ref $err || !$err->isa('Cpanel::Exception::Unsupported') ) { 149 | $self->add_warn_advice( 150 | key => 'Processes_error_while_checking_running_executables', 151 | text => $self->_lh->maketext( 'Failed to check whether running executables are up-to-date: [_1]', Cpanel::Exception::get_string_no_id($err) ), 152 | ); 153 | } 154 | return; # We can't check anything, so don't report anything. 155 | } 156 | 157 | if (@PIDs) { 158 | my $suggestion; 159 | if ( grep { $_ eq '1' } @PIDs ) { # If initd or systemd needs update, just suggest reboot. 160 | $suggestion = $self->_lh->maketext( 161 | '[output,url,_1,Reboot the server,_2,_3] to ensure the system benefits from these updates.', 162 | $self->base_path('scripts/dialog?dialog=reboot'), 163 | 'target', 164 | '_blank', 165 | ); 166 | } 167 | else { 168 | $suggestion = _make_unordered_list( 169 | $self->_lh->maketext('You must take one of the following actions to ensure the system is up-to-date:'), 170 | $self->_lh->maketext( 171 | 'Restart the listed [numerate,_1,process,processes].', 172 | scalar @PIDs, 173 | ), 174 | $self->_lh->maketext( 175 | '[output,url,_1,Reboot the server,_2,_3].', 176 | $self->base_path('scripts/dialog?dialog=reboot'), 177 | 'target', 178 | '_blank', 179 | ) 180 | ); 181 | } 182 | 183 | $self->add_bad_advice( 184 | key => 'Processes_detected_running_outdated_executables', 185 | text => $self->_lh->maketext( 186 | 'Detected [quant,_1,process,processes] that [numerate,_1,is,are] running outdated executables: [join, ,_2]', 187 | scalar @PIDs, 188 | \@PIDs, 189 | ), 190 | suggestion => $suggestion, 191 | ); 192 | return; # Error reported. 193 | } 194 | 195 | $self->add_good_advice( 196 | key => 'Processes_none_with_outdated_executables', 197 | text => $self->_lh->maketext('The system did not detect processes with outdated binaries.') 198 | ); 199 | 200 | return 1; 201 | } 202 | 203 | # Do this to work around bad perltidy concatenation rules. 204 | sub _make_unordered_list { 205 | my ( $title, @items ) = @_; 206 | 207 | my $output = $title; 208 | $output .= ''; 213 | 214 | return $output; 215 | } 216 | 217 | sub _check_for_outdated_processes_on_a_cpanel_whm_system_at_v64_or_earlier { 218 | my ($self) = @_; 219 | 220 | # Prior to CentOS 6, the yum-utils package did not come with /usr/bin/needs-restarting 221 | return if Cpanel::Sys::OS::getreleaseversion() < 6; 222 | 223 | # needs-restarting won't work without smaps support (Disabled in grsec kernels). 224 | return if !-e qq{/proc/$$/smaps}; 225 | 226 | # Find the needs-restarting executable, if available. 227 | my $package_install_cmd = 'yum install yum-utils'; 228 | my $command = 'needs-restarting'; 229 | my $exec = Cpanel::FileUtils::Path::findinpath($command); 230 | 231 | if ( !$exec ) { 232 | $self->add_info_advice( 233 | 'key' => 'Processes_unable_to_check_running_executables', 234 | text => $self->_lh->maketext('Unable to check whether running executables are up-to-date.'), 235 | suggestion => $self->_lh->maketext( 'Install the ‘[_1]’ command by running ‘[_2]’ on the command line to get notifications when executables are updated but the existing processes are not restarted.', $command, $package_install_cmd ), 236 | ); 237 | } 238 | else { 239 | my $proc = Cpanel::SafeRun::Object->new( program => $exec ); 240 | 241 | if ( $proc->stdout() ) { 242 | $self->add_bad_advice( 243 | 'key' => 'Processes_detected_running_from_outdated_executables', 244 | text => $self->_lh->maketext('Detected processes that are running outdated binary executables.'), 245 | suggestion => $self->_lh->maketext( 246 | 'Reboot the system in the “[output,url,_1,Graceful Server Reboot,_2,_3]” area. Alternatively, [asis,SSH] into this server and run ‘[_4]’, then manually restart each of the listed processes.', 247 | $self->base_path('scripts/dialog?dialog=reboot'), 248 | 'target', 249 | '_blank', 250 | $exec, 251 | ), 252 | ); 253 | } 254 | elsif ( $proc->CHILD_ERROR() ) { 255 | $self->add_warn_advice( 256 | 'key' => 'Processes_error_while_checking_running_executables_1', 257 | text => $self->_lh->maketext( 'An error occurred while attempting to check whether running executables are up-to-date: [_1]', $proc->autopsy() ), 258 | ); 259 | } 260 | elsif ( $proc->stderr() ) { 261 | $self->add_warn_advice( 262 | 'key' => 'Processes_error_while_checking_running_executables_2', 263 | text => $self->_lh->maketext( 'An error occurred while attempting to check whether running executables are up-to-date: [_1]', $proc->stderr() ), 264 | ); 265 | } 266 | else { 267 | $self->add_good_advice( 268 | key => 'Processes_none_with_outdated_executables', 269 | text => $self->_lh->maketext('No processes with outdated binaries detected.') 270 | ); 271 | } 272 | } 273 | 274 | return 1; 275 | } 276 | 277 | 1; 278 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/SSH.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::SSH; 2 | 3 | # Copyright (c) 2021, cPanel, L.L.C. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use Whostmgr::Services::SSH::Config (); 31 | use Cpanel::PackMan (); 32 | 33 | use base 'Cpanel::Security::Advisor::Assessors'; 34 | 35 | sub version { 36 | return '1.02'; 37 | } 38 | 39 | sub generate_advice { 40 | my ($self) = @_; 41 | $self->_check_for_ssh_settings; 42 | $self->_check_for_ssh_version; 43 | 44 | return 1; 45 | } 46 | 47 | sub _check_for_ssh_settings { 48 | my ($self) = @_; 49 | 50 | my $scfg = Whostmgr::Services::SSH::Config->new(); 51 | my $sshd_config = $scfg->get_config(); 52 | if ( $scfg->get_config('PasswordAuthentication') =~ m/yes/i || $scfg->get_config('ChallengeResponseAuthentication') =~ m/yes/i ) { 53 | $self->add_bad_advice( 54 | 'key' => 'SSH_password_authentication_enabled', 55 | 'text' => $self->_lh->maketext('SSH password authentication is enabled.'), 56 | 'suggestion' => $self->_lh->maketext( 57 | 'Disable SSH password authentication in the “[output,url,_1,SSH Password Authorization Tweak,_2,_3]” area', 58 | $self->base_path('scripts2/tweaksshauth'), 59 | 'target', 60 | '_blank' 61 | ), 62 | ); 63 | } 64 | else { 65 | $self->add_good_advice( 66 | 'key' => 'SSH_password_authentication_disabled', 67 | 'text' => $self->_lh->maketext('SSH password authentication is disabled.'), 68 | ); 69 | 70 | } 71 | 72 | if ( !$scfg->get_config('PermitRootLogin') || $scfg->get_config('PermitRootLogin') =~ m/yes/i ) { 73 | $self->add_bad_advice( 74 | 'key' => 'SSH_direct_root_login_permitted', 75 | 'text' => $self->_lh->maketext('SSH direct root logins are permitted.'), 76 | 'suggestion' => $self->_lh->maketext( 77 | 'Manually edit /etc/ssh/sshd_config and change PermitRootLogin to “without-password” or “no”, then restart SSH in the “[output,url,_1,Restart SSH,_2,_3]” area', 78 | $self->base_path('scripts/ressshd'), 79 | 'target', 80 | '_blank' 81 | ), 82 | ); 83 | } 84 | else { 85 | $self->add_good_advice( 86 | 'key' => 'SSH_direct_root_logins_disabled', 87 | 'text' => $self->_lh->maketext('SSH direct root logins are disabled.'), 88 | ); 89 | 90 | } 91 | 92 | return 1; 93 | 94 | } 95 | 96 | sub _check_for_ssh_version { 97 | my ($self) = @_; 98 | 99 | my $pkm = Cpanel::PackMan->instance; 100 | my $pkginfo = $pkm->pkg_hr('openssh-server'); 101 | 102 | my $current_sshversion = $pkginfo->{'version_installed'}; 103 | my $latest_sshversion = $pkginfo->{'version_latest'}; 104 | 105 | if ( length $current_sshversion && length $latest_sshversion ) { 106 | if ( $current_sshversion lt $latest_sshversion ) { 107 | $self->add_bad_advice( 108 | 'key' => 'SSH_version_outdated', 109 | 'text' => $self->_lh->maketext('Current SSH version is out of date.'), 110 | 'suggestion' => $self->_lh->maketext( 111 | 'Update current system software in the “[output,url,_1,Update System Software,_2,_3]” area', 112 | $self->base_path('scripts/dialog?dialog=updatesrvsoftware'), 113 | 'target', 114 | '_blank' 115 | ), 116 | ); 117 | } 118 | else { 119 | $self->add_good_advice( 120 | 'key' => 'SSH_is_current', 121 | 'text' => $self->_lh->maketext( 'Current SSH version is up to date: [_1]', $current_sshversion ) 122 | ); 123 | } 124 | } 125 | else { 126 | $self->add_warn_advice( 127 | 'key' => 'SSH_can_not_determine_version', 128 | 'text' => $self->_lh->maketext('Unable to determine SSH version'), 129 | 'suggestion' => $self->_lh->maketext('Ensure that yum and rpm are working on your system.') 130 | ); 131 | } 132 | 133 | return 1; 134 | 135 | } 136 | 137 | 1; 138 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/Scgiwrap.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::Scgiwrap; 2 | 3 | # Copyright (c) 2015, cPanel, Inc. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use base 'Cpanel::Security::Advisor::Assessors'; 31 | use Cpanel::Config::Httpd (); 32 | 33 | sub generate_advice { 34 | my ($self) = @_; 35 | $self->_check_scgiwrap; 36 | 37 | return 1; 38 | } 39 | 40 | sub _binary_has_setuid { 41 | my ($binary) = @_; 42 | return ( ( stat $binary )[2] || 0 ) & 04000; 43 | } 44 | 45 | sub _check_scgiwrap { 46 | my ($self) = @_; 47 | 48 | my $security_advisor_obj = $self->{'security_advisor_obj'}; 49 | 50 | # In ea3 these are always the same, in ea4 they are determined by the RPM in question. 51 | my $httpd = "/usr/local/apache/bin/httpd"; 52 | my $suexec = "/usr/local/apache/bin/suexec"; 53 | 54 | #check for sticky bit on file to see if it is enabled or not. 55 | my $suenabled = _binary_has_setuid($suexec); 56 | 57 | if ( defined &Cpanel::Config::Httpd::is_ea4 ) { 58 | if ( Cpanel::Config::Httpd::is_ea4() ) { 59 | require Cpanel::ConfigFiles::Apache; 60 | my $apacheconf = Cpanel::ConfigFiles::Apache->new(); 61 | $suexec = $apacheconf->bin_suexec(); 62 | $httpd = $apacheconf->bin_httpd(); 63 | if ( -f $suexec ) { 64 | 65 | # patches welcome for more a robust way to do this besides matching getcap output! 66 | my $gc = `getcap $suexec`; # the RPM in ea4 uses capabilities for setuid, not setuid bit 67 | $suenabled = $gc =~ m/cap_setgid/ && $gc =~ m/cap_setuid/; 68 | 69 | # CloudLinux's EA 4 RPM uses setuid. 70 | $suenabled ||= _binary_has_setuid($suexec); 71 | } 72 | } 73 | } 74 | 75 | # DEPRECATED_1158 76 | my $scgiwrap = '/usr/local/cpanel/cgi-sys/scgiwrap'; 77 | $scgiwrap .= '_deprecated' if $Cpanel::Version::MAJORVERSION > 11.57; 78 | 79 | #check for sticky bit on file to see if it is enabled or not. 80 | my $scgienabled = ( ( stat $scgiwrap )[2] || 0 ) & 04000; 81 | 82 | my ($ruid) = ( grep { /ruid2_module/ } split( /\n/, Cpanel::SafeRun::Simple::saferun( $httpd, '-M' ) ) ); 83 | 84 | if ( $suenabled && !$scgienabled ) { 85 | $security_advisor_obj->add_advice( 86 | { 87 | 'key' => 'Scgiwrap_SCGI_is_disabled', 88 | 'type' => $Cpanel::Security::Advisor::ADVISE_GOOD, 89 | 'text' => $self->_lh->maketext('SCGI is disabled, currently using the recommended suEXEC.'), 90 | } 91 | ); 92 | } 93 | elsif ( $suenabled && $scgienabled ) { 94 | if ( !$ruid ) { 95 | $security_advisor_obj->add_advice( 96 | { 97 | 'key' => 'Scgiwrap_SCGI_AND_suEXEC_are_enabled', 98 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 99 | 'text' => $self->_lh->maketext('Both SCGI and suEXEC are enabled.'), 100 | 'suggestion' => $self->_lh->maketext( 101 | 'On the “[output,url,_1,Configure PHP and suEXEC,_2,_3]” page toggle “Apache suEXEC” off then back on to disable SCGI.', 102 | $self->base_path('scripts2/phpandsuexecconf'), 103 | 'target', 104 | '_blank' 105 | ), 106 | } 107 | ); 108 | } 109 | else { 110 | $security_advisor_obj->add_advice( 111 | { 112 | 'key' => 'Scgiwrap_SCGI_suEXEC_and_mod_ruid2_are_enabled', 113 | 'type' => $Cpanel::Security::Advisor::ADVISE_GOOD, 114 | 'text' => $self->_lh->maketext('SCGI, suEXEC, and mod_ruid2 are enabled.'), 115 | } 116 | ); 117 | } 118 | } 119 | elsif ( !$suenabled || -f "$suexec.disable" ) { 120 | if ( !$ruid ) { 121 | $security_advisor_obj->add_advice( 122 | { 123 | 'key' => 'Scgiwrap_suEXEC_is_disabled', 124 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 125 | 'text' => $self->_lh->maketext('suEXEC is disabled.'), 126 | 'suggestion' => $self->_lh->maketext( 127 | 'Enable suEXEC on the “[output,url,_1,Configure PHP and suEXEC,_2,_3]” page.', 128 | $self->base_path('scripts2/phpandsuexecconf'), 129 | 'target', 130 | '_blank' 131 | ), 132 | } 133 | ); 134 | } 135 | else { 136 | $security_advisor_obj->add_advice( 137 | { 138 | 'key' => 'Scgiwrap_suEXEC_is_disabled_mod_ruid2_is_installed', 139 | 'type' => $Cpanel::Security::Advisor::ADVISE_GOOD, 140 | 'text' => $self->_lh->maketext('suEXEC is disabled; however mod_ruid2 is installed.'), 141 | } 142 | ); 143 | } 144 | } 145 | else { 146 | $security_advisor_obj->add_advice( 147 | { 148 | 'key' => 'Scgiwrap_SCGI_is_enabled', 149 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 150 | 'text' => $self->_lh->maketext('SCGI is enabled.'), 151 | 'suggestion' => $self->_lh->maketext( 152 | 'Turn off SCGI and enable the more secure suEXEC in the “[output,url,_1,Configure PHP and suEXEC,_2,_3]” page.', 153 | $self->base_path('scripts2/phpandsuexecconf'), 154 | 'target', 155 | '_blank' 156 | ), 157 | } 158 | ); 159 | 160 | } 161 | 162 | return 1; 163 | } 164 | 1; 165 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/Spam.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::Spam; 2 | 3 | # Copyright (c) 2013, cPanel, Inc. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use Cpanel::LoadFile (); 31 | 32 | use base 'Cpanel::Security::Advisor::Assessors'; 33 | 34 | sub generate_advice { 35 | my ($self) = @_; 36 | $self->_check_for_nobody_tracking(); 37 | 38 | return 1; 39 | } 40 | 41 | sub _check_for_nobody_tracking { 42 | my ($self) = @_; 43 | 44 | my $security_advisor_obj = $self->{'security_advisor_obj'}; 45 | 46 | if ( $security_advisor_obj->{'cpconf'}->{'nobodyspam'} ) { 47 | $security_advisor_obj->add_advice( 48 | { 49 | 'key' => 'Spam_user_nobody_can_not_permitted_to_send_email', 50 | 'type' => $Cpanel::Security::Advisor::ADVISE_GOOD, 51 | 'text' => $self->_lh->maketext('The pseudo-user “nobody” is not permitted to send email.'), 52 | } 53 | ); 54 | } 55 | else { 56 | $security_advisor_obj->add_advice( 57 | { 58 | 'key' => 'Spam_user_nobody_can_send_email', 59 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 60 | 'text' => $self->_lh->maketext('The pseudo-user “nobody” is permitted to send email.'), 61 | 'suggestion' => $self->_lh->maketext( 62 | 'Enable “Prevent "nobody" from sending mail” in the “[output,url,_1,Tweak Settings,_2,_3]” area', 63 | $self->base_path('scripts2/tweaksettings?find=nobodyspam'), 64 | 'target', 65 | '_blank' 66 | ), 67 | } 68 | ); 69 | } 70 | 71 | if ( -e '/var/cpanel/smtpgidonlytweak' ) { 72 | $security_advisor_obj->add_advice( 73 | { 74 | 'key' => 'Spam_outbound_smtp_restricted', 75 | 'type' => $Cpanel::Security::Advisor::ADVISE_GOOD, 76 | 'text' => $self->_lh->maketext('Outbound SMTP connections are restricted.'), 77 | } 78 | ); 79 | 80 | } 81 | elsif ( _csf_has_option( 'SMTP_BLOCK', '1' ) ) { 82 | $security_advisor_obj->add_advice( 83 | { 84 | 'key' => 'Spam_smtp_block_enabled', 85 | 'type' => $Cpanel::Security::Advisor::ADVISE_GOOD, 86 | 'text' => $self->_lh->maketext('CSF has SMTP_BLOCK enabled.'), 87 | } 88 | ); 89 | 90 | } 91 | else { 92 | $security_advisor_obj->add_advice( 93 | { 94 | 'key' => 'Spam_smtp_unrestricted', 95 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 96 | 'text' => $self->_lh->maketext('Outbound SMTP connections are unrestricted.'), 97 | 'suggestion' => $self->_lh->maketext( 98 | 'Enable SMTP Restrictions in the “[output,url,_1,SMTP Restrictions,_2,_3]” area', 99 | $self->base_path('scripts2/smtpmailgidonly'), 100 | 'target', 101 | '_blank' 102 | ), 103 | 104 | } 105 | ); 106 | } 107 | 108 | if ( -e '/var/cpanel/config/email/query_apache_for_nobody_senders' ) { 109 | $security_advisor_obj->add_advice( 110 | { 111 | 'key' => 'Spam_apache_queried_for_sender', 112 | 'type' => $Cpanel::Security::Advisor::ADVISE_GOOD, 113 | 'text' => $self->_lh->maketext('Apache is being queried to determine the actual sender when mail originates from the “nobody” pseudo-user.'), 114 | } 115 | ); 116 | } 117 | else { 118 | $security_advisor_obj->add_advice( 119 | { 120 | 'key' => 'Spam_apache_not_queried_for_sender', 121 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 122 | 'text' => $self->_lh->maketext('Apache is not being queried to determine the actual sender when mail originates from the “nobody” pseudo-user.'), 123 | 'suggestion' => $self->_lh->maketext( 124 | 'Enable “Query Apache server status to determine the sender of email sent from processes running as nobody” in the “[output,url,_1,Exim Configuration Manager,_2,_3]” area\'s “Basic Editor”', 125 | $self->base_path('scripts2/displayeximconfforedit'), 126 | 'target', 127 | '_blank' 128 | ), 129 | } 130 | ); 131 | 132 | } 133 | 134 | return 1; 135 | } 136 | 137 | sub _csf_has_option { 138 | my ( $option, $value ) = @_; 139 | if ( -e '/etc/csf/csf.conf' ) { 140 | my $csf_conf = Cpanel::LoadFile::loadfile('/etc/csf/csf.conf'); 141 | return 1 if $csf_conf =~ m/\n[ \t]*\Q$option\E[ \t]*=[ \t]*['"]\Q$value\E/s; 142 | } 143 | return 0; 144 | } 145 | 146 | 1; 147 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/Symlinks.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::Symlinks; 2 | 3 | # Copyright (c) 2016, cPanel, Inc. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use warnings; 31 | 32 | use Cpanel::Sys::Uname (); 33 | 34 | use base 'Cpanel::Security::Advisor::Assessors'; 35 | 36 | sub generate_advice { 37 | my ($self) = @_; 38 | 39 | if ( $self->has_cpanel_hardened_kernel() ) { 40 | $self->add_warn_advice( 41 | 'key' => 'Symlinks_protection_no_longer_support_hardened_kernel', 42 | 'text' => $self->_lh->maketext('Unsupported cPanel hardened kernel detected.'), 43 | 44 | 'suggestion' => $self->_lh->maketext( 45 | "[asis,cPanel] no longer supports the hardened kernel. We recommend that you use [asis,KernelCare's] free symlink protection. In order to enable [asis,KernelCare], you must replace the hardened kernel with a standard kernel. For instructions, please read the document on [output,url,_1,How to Manually Remove the cPanel-Provided Hardened Kernel,_2,_3].", 46 | 'https://go.cpanel.net/uninstallhardenedkernel', 'target', '_blank' 47 | ), 48 | ); 49 | 50 | } 51 | return 1; 52 | } 53 | 54 | sub has_cpanel_hardened_kernel { 55 | my $self = shift; 56 | my $kernel_uname = ( Cpanel::Sys::Uname::get_uname_cached() )[2]; 57 | my $ret; 58 | if ( $kernel_uname =~ m/(?:cpanel|cp)6\.x86_64/ ) { 59 | $ret = 1; 60 | } 61 | return $ret; 62 | } 63 | 64 | 1; 65 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/Tomcat.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::Tomcat; 2 | 3 | # Copyright (c) 2016, cPanel, Inc. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use base 'Cpanel::Security::Advisor::Assessors'; 31 | 32 | sub generate_advice { 33 | my ($self) = @_; 34 | $self->_is_tomcat5_installed(); 35 | 36 | return 1; 37 | } 38 | 39 | sub _is_tomcat5_installed { 40 | my ($self) = @_; 41 | my $security_advisor_obj = $self->{'security_advisor_obj'}; 42 | 43 | if ( -l '/usr/local/jakarta/tomcat' ) { 44 | $security_advisor_obj->add_advice( 45 | { 46 | 'key' => q{Tomcat_installed_5_5_version_is_EOL}, 47 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 48 | 'text' => $self->_lh->maketext('Tomcat 5.5 is installed, but is EOL.'), 49 | 'suggestion' => $self->_lh->maketext( 50 | 'Rebuild “[output,url,_1,EasyApache,_2,_3]” without Tomcat 5.5.x selected (or select the newest version of Tomcat), and manually remove the old Tomcat files.', 51 | $self->base_path('cgi/easyapache.pl?action=_pre_cpanel_sync_screen'), 'target', '_blank', 52 | ), 53 | } 54 | ); 55 | } 56 | 57 | return 1; 58 | } 59 | 60 | 1; 61 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/Usernames.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::Usernames; 2 | 3 | # Copyright (c) 2018, cPanel, Inc. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use warnings; 30 | use strict; 31 | 32 | use Cpanel::Config::LoadUserOwners (); 33 | use Cpanel::Config::LoadUserDomains (); 34 | use Cpanel::ArrayFunc::Uniq (); 35 | use Cpanel::Validate::Username::Core (); 36 | 37 | use base 'Cpanel::Security::Advisor::Assessors'; 38 | 39 | my $GO_URL = "https://go.cpanel.net/usernames"; 40 | 41 | my $update_user_domains_cmd = '/usr/local/cpanel/scripts/updateuserdomains --force'; 42 | my $rename_user_cmd = 'whmapi1 modifyacct user=old_username newuser=new_username'; 43 | 44 | sub generate_advice { 45 | my ($self) = @_; 46 | $self->_check_for_invalid_users; 47 | 48 | return 1; 49 | } 50 | 51 | sub _check_for_invalid_users { 52 | my ($self) = @_; 53 | 54 | my @users; 55 | 56 | # gather the users in /etc/trueuserowners file. 57 | my %trueuserowners; 58 | Cpanel::Config::LoadUserOwners::loadtrueuserowners( \%trueuserowners, 1, 1 ); 59 | my @true_users = ( keys %trueuserowners ); 60 | 61 | # gather users that exist in /var/cpanel/userdata directory 62 | my $user_data_dir = '/var/cpanel/userdata'; 63 | opendir( my $userdata_dh, $user_data_dir ); 64 | my @userdata_users = grep { !/\A\.{1,2}\z|\Anobody\z/ && -e "$user_data_dir/$_/main" } readdir $userdata_dh; 65 | 66 | # find misconfigured users that have userdata but don't exist in the owners file, and vice versa. 67 | my %userdata_hash = map { $_ => 1 } @userdata_users; 68 | my @true_owner_no_userdata = grep { !$userdata_hash{$_} } @true_users; 69 | 70 | # gather the domain owners 71 | my $userdomains = Cpanel::Config::LoadUserDomains::loaduserdomains(); 72 | my @domain_users = ( keys %{$userdomains} ); 73 | 74 | # find domains owners who do not have userdata or do not exist in the trueuserowners file. 75 | my @has_domain_no_userdata = grep { !$userdata_hash{$_} } @domain_users; 76 | my @has_domain_no_true_owner = grep { !$trueuserowners{$_} } @domain_users; 77 | 78 | # get a list of unique users based on the previous user lists. 79 | my @uniq_users = Cpanel::ArrayFunc::Uniq::uniq( @true_users, @userdata_users, @domain_users ); 80 | 81 | # check for user names that do not pass validation, or are reserved. 82 | my @reserved; 83 | my @reserved_by_alias; 84 | my @invalid; 85 | my @aliases = Cpanel::Validate::Username::Core::aliases(); 86 | 87 | foreach my $username (@uniq_users) { 88 | if ( Cpanel::Validate::Username::Core::reserved_username_check($username) ) { 89 | if ( grep { /\A$username\z/ } @aliases ) { 90 | push( @reserved_by_alias, $username ); 91 | } 92 | else { 93 | push( @reserved, $username ); 94 | } 95 | } 96 | elsif ( !Cpanel::Validate::Username::Core::is_valid($username) ) { 97 | push( @invalid, $username ); 98 | } 99 | } 100 | 101 | @reserved = sort_and_truncate_list(@reserved); 102 | @reserved_by_alias = sort_and_truncate_list(@reserved_by_alias); 103 | @invalid = sort_and_truncate_list(@invalid); 104 | 105 | # populate all misconfigured users into a single list. 106 | my @misconfigured = Cpanel::ArrayFunc::Uniq::uniq( @true_owner_no_userdata, @has_domain_no_userdata, @has_domain_no_true_owner ); 107 | @misconfigured = sort_and_truncate_list(@misconfigured); 108 | 109 | # display the advice 110 | if (@reserved) { 111 | $self->add_bad_advice( 112 | 'key' => 'Usernames_reserved', 113 | 'text' => $self->_lh->maketext( 'The following reserved usernames were found in use by cPanel users: [list_and,_1].', \@reserved ), 114 | 'suggestion' => $self->_lh->maketext( 115 | 'These usernames need to be renamed. Run “[_1]” from command line to rename these accounts. For further information about reserved usernames, please visit “[output,url,_2,Invalid and Misconfigured Usernames,_3,_4]”.', 116 | $rename_user_cmd, 117 | $GO_URL, 118 | "target", 119 | "_blank" 120 | ) 121 | ); 122 | } 123 | 124 | if (@reserved_by_alias) { 125 | $self->add_bad_advice( 126 | 'key' => 'Usernames_reserved_by_alias', 127 | 'text' => $self->_lh->maketext( 'The following cPanel usernames were found to have an email alias to a reserved username: [list_and,_1].', \@reserved_by_alias ), 128 | 'suggestion' => $self->_lh->maketext( 129 | 'These usernames need to be renamed or the problem aliases removed. Run “[_1]” from command line to rename these accounts. For further information about reserved usernames due to email aliases, please visit “[output,url,_2,Invalid and Misconfigured Usernames,_3,_4]”.', 130 | $rename_user_cmd, 131 | $GO_URL, 132 | "target", 133 | "_blank" 134 | ) 135 | ); 136 | } 137 | 138 | if (@invalid) { 139 | $self->add_bad_advice( 140 | 'key' => 'Usernames_invalid', 141 | 'text' => $self->_lh->maketext( 'The following cPanel usernames were found to be invalid: [list_and,_1].', \@invalid ), 142 | 'suggestion' => $self->_lh->maketext( 143 | 'These usernames do not match cPanel’s username validation rules and will need to be renamed. Run “[_1]” from command line to rename these accounts. To find more information about username validation rules please visit “[output,url,_2,Invalid and Misconfigured Usernames,_3,_4]”.', 144 | $rename_user_cmd, 145 | $GO_URL, 146 | "target", 147 | "_blank" 148 | ) 149 | ); 150 | } 151 | 152 | if (@misconfigured) { 153 | $self->add_bad_advice( 154 | 'key' => 'Usernames_misconfigured', 155 | 'text' => $self->_lh->maketext( 'The following cPanel usernames were found to be misconfigured: [list_and,_1].', \@misconfigured ), 156 | 'suggestion' => $self->_lh->maketext( 157 | 'These usernames are in an incomplete and misconfigured state. Run “[_1]” from command line to resolve these problems. For further information please visit “[output,url,_2,Invalid and Misconfigured Usernames,_3,_4]”.', 158 | $update_user_domains_cmd, 159 | $GO_URL, 160 | "target", 161 | "_blank" 162 | ) 163 | ); 164 | } 165 | 166 | return 1; 167 | } 168 | 169 | sub sort_and_truncate_list { 170 | my @data = sort @_; 171 | if ( scalar @data > 50 ) { 172 | splice( @data, 50 ); 173 | push @data, '..truncated..'; 174 | } 175 | return @data; 176 | } 177 | 1; 178 | -------------------------------------------------------------------------------- /pkg/Cpanel/Security/Advisor/Assessors/_Self.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::_Self; 2 | 3 | # Copyright (c) 2021, cPanel, L.L.C. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use warnings; 31 | use base 'Cpanel::Security::Advisor::Assessors'; 32 | 33 | use Cpanel::RPM::Versions::File (); 34 | 35 | # The purpose of this assessor module is to report conditions which may render 36 | # the provided advice untrustworthy or invalid. Currently, this is limited to 37 | # determining whether the RPM database is acting as expected, since several 38 | # other assessors rely on good RPM data. 39 | 40 | # Logic behind this number: A barebones CentOS 6 container has 129(?) RPM packages. 41 | # Round down to one significant figure. 42 | use constant OS_RPM_COUNT_WARN_THRESHOLD => 100; 43 | 44 | sub version { return '1.01'; } 45 | 46 | sub generate_advice { 47 | my ($self) = @_; 48 | 49 | $self->_check_rpm() if $self->_distro_uses_rpm(); 50 | 51 | return 1; 52 | } 53 | 54 | sub _check_rpm { 55 | my ($self) = @_; 56 | 57 | # Both primes the cache and ensures that the test is run. 58 | my $installed_rpms = $self->get_installed_rpms(); 59 | 60 | my $cache = $self->{'security_advisor_obj'}->{'_cache'}; 61 | if ( exists $cache->{'timed_out'} && $cache->{'timed_out'} ) { 62 | $self->add_bad_advice( 63 | 'key' => 'RPM_timed_out', 64 | 'text' => $self->_lh->maketext('Security Advisor timed out while reading the RPM database of packages.'), 65 | 'suggestion' => $self->_lh->maketext( "Security Advisor may include inaccurate results until it can fully read the RPM database. To resolve this, reduce the load on your system and then rebuild the RPM database with the following interface: [output,url,_1,Rebuild RPM Database,_2,_3].", $self->base_path('scripts/dialog?dialog=rebuildrpmdb'), 'target', '_blank' ), 66 | 'block_notify' => 1, 67 | ); 68 | } 69 | elsif ( exists $cache->{'died'} && $cache->{'died'} ) { 70 | $self->add_bad_advice( 71 | 'key' => 'RPM_broken', 72 | 'text' => $self->_lh->maketext('Security Advisor detected RPM database corruption.'), 73 | 'suggestion' => $self->_lh->maketext( "Security Advisor may include inaccurate results until it can cleanly read the RPM database. To resolve this, rebuild the RPM database with the following interface: [output,url,_1,Rebuild RPM Database,_2,_3].", $self->base_path('scripts/dialog?dialog=rebuildrpmdb'), 'target', '_blank' ), 74 | 'block_notify' => 1, 75 | ); 76 | } 77 | elsif ( ref $installed_rpms eq 'HASH' && scalar keys %$installed_rpms <= scalar( keys %{ Cpanel::RPM::Versions::File->new()->list_rpms_in_state('installed') } ) + OS_RPM_COUNT_WARN_THRESHOLD ) { 78 | $self->add_warn_advice( 79 | 'key' => 'RPM_too_few', 80 | 'text' => $self->_lh->maketext('The RPM database is smaller than expected.'), 81 | 'suggestion' => $self->_lh->maketext("Security Advisor may include inaccurate results if the RPM database of packages is incomplete. To resolve this, check the cPanel update logs for RPM issues."), 82 | 'block_notify' => 1, 83 | ); 84 | } 85 | 86 | return; 87 | } 88 | 89 | sub _distro_uses_rpm { 90 | my ($self) = @_; 91 | 92 | # Optimistically try to query Cpanel::OS: 93 | my $answer = eval { 94 | require Cpanel::OS; 95 | Cpanel::OS::is_rpm_based(); 96 | }; 97 | return $answer unless $@; 98 | 99 | # cPanel is too old for that to work. Since the rpm program can't be relied 100 | # upon, query for the existence of /etc/redhat-release: 101 | return -e '/etc/redhat-release'; 102 | } 103 | 104 | 1; 105 | -------------------------------------------------------------------------------- /pkg/appconfig/securityadvisor.conf: -------------------------------------------------------------------------------- 1 | # name 2 | name=securityadvisor 3 | 4 | # Service that will serve this app 5 | service=whostmgr 6 | 7 | # Physical path: /usr/local/cpanel/3rdparty/Foo.php 8 | # Literal URL path: $server:$port/$cpsession/3rdparty/Foo.php 9 | url=/cgi/addons/securityadvisor/ 10 | url2=/cgi/addon_securityadvisor.cgi 11 | 12 | # System user to run process as 13 | user=root 14 | 15 | # Required acls 16 | acls=all 17 | 18 | # Display name as show in the service ui 19 | displayname=Security Advisor Tool 20 | 21 | # Url to show in the service ui (relative to install path for whm this is cgi/) 22 | entryurl=addons/securityadvisor/index.cgi 23 | 24 | upgradecall=/var/cpanel/addons/securityadvisor/bin/upgrade 25 | 26 | icon=ico-security-advisor.png 27 | -------------------------------------------------------------------------------- /pkg/bin/upgrade: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | # Copyright (c) 2013, cPanel, Inc. 3 | # All rights reserved. 4 | # http://cpanel.net 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # * Neither the name of the owner nor the names of its contributors may 14 | # be used to endorse or promote products derived from this software 15 | # without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 21 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | package addon::securityadvisor::upgrade; 29 | 30 | use strict; 31 | use Cpanel::SafeRun::Simple (); 32 | 33 | run(@ARGV) unless caller; 34 | 35 | sub run { 36 | my ( $previousversion, $currentversion ) = @_; 37 | 38 | if ( -x '/usr/local/cpanel/bin/is_registered_with_appconfig' && Cpanel::SafeRun::Simple::saferun( '/usr/local/cpanel/bin/is_registered_with_appconfig', 'whostmgr', 'securityadvisor' ) ) { 39 | 40 | if ( !-e "/usr/local/cpanel/whostmgr/docroot/cgi/addons/securityadvisor/index.cgi" && -e "/usr/local/cpanel/whostmgr/docroot/cgi/addon_securityadvisor.cgi" ) { 41 | mkdir( "/usr/local/cpanel/whostmgr/docroot/cgi/addons/securityadvisor", 0700 ); 42 | rename( 43 | "/usr/local/cpanel/whostmgr/docroot/cgi/addon_securityadvisor.cgi", 44 | "/usr/local/cpanel/whostmgr/docroot/cgi/addons/securityadvisor/index.cgi" 45 | ); 46 | } 47 | } 48 | 49 | return; 50 | } 51 | -------------------------------------------------------------------------------- /pkg/cgi/addon_securityadvisor.cgi: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | eval 'if [ -x /usr/local/cpanel/3rdparty/bin/perl ]; then exec /usr/local/cpanel/3rdparty/bin/perl -x -- $0 ${1+"$@"}; else exec /usr/bin/perl -x $0 ${1+"$@"}; fi;' ## no critic qw(ProhibitStringyEval RequireUseStrict) -*-mode:perl-*- 3 | if 0; 4 | 5 | #!/usr/bin/perl 6 | #WHMADDON:addonupdates:Security Advisor Tool 7 | #ACLS:all 8 | 9 | # Copyright (c) 2013, cPanel, Inc. 10 | # All rights reserved. 11 | # http://cpanel.net 12 | # 13 | # Redistribution and use in source and binary forms, with or without 14 | # modification, are permitted provided that the following conditions are met: 15 | # * Redistributions of source code must retain the above copyright 16 | # notice, this list of conditions and the following disclaimer. 17 | # * Redistributions in binary form must reproduce the above copyright 18 | # notice, this list of conditions and the following disclaimer in the 19 | # documentation and/or other materials provided with the distribution. 20 | # * Neither the name of the owner nor the names of its contributors may 21 | # be used to endorse or promote products derived from this software 22 | # without specific prior written permission. 23 | # 24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 25 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 26 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 28 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 31 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | 35 | package cgi::addon_securityadvisor; 36 | 37 | use strict; 38 | 39 | BEGIN { 40 | unshift @INC, '/var/cpanel/addons/securityadvisor/perl', '/usr/local/cpanel'; 41 | } 42 | 43 | use Whostmgr::ACLS (); 44 | use Whostmgr::HTMLInterface (); 45 | use Cpanel::Form (); 46 | use Cpanel::Template (); 47 | use Cpanel::Comet (); 48 | use Cpanel::Rlimit (); 49 | use Cpanel::Encoder::URI (); 50 | use POSIX (); 51 | 52 | # from /var/cpanel/addons/securityadvisor/perl 53 | use Cpanel::Security::Advisor (); 54 | 55 | run(@ARGV) unless caller(); 56 | 57 | sub run { 58 | _check_acls(); 59 | my $form = Cpanel::Form::parseform(); 60 | if ( $form->{'start_scan'} ) { 61 | _start_scan( $form->{'channel'} ); 62 | exit; 63 | } 64 | else { 65 | _headers("text/html"); 66 | 67 | my $template_file = 68 | -e '/var/cpanel/addons/securityadvisor/templates/main.tmpl' 69 | ? '/var/cpanel/addons/securityadvisor/templates/main.tmpl' 70 | : '/usr/local/cpanel/whostmgr/docroot/templates/securityadvisor/main.tmpl'; 71 | 72 | Cpanel::Template::process_template( 73 | 'whostmgr', 74 | { 75 | 'template_file' => $template_file, 76 | 'security_advisor_version' => $Cpanel::Security::Advisor::VERSION, 77 | }, 78 | ); 79 | } 80 | 81 | return 1; 82 | } 83 | 84 | sub _check_acls { 85 | Whostmgr::ACLS::init_acls(); 86 | 87 | if ( !Whostmgr::ACLS::hasroot() ) { 88 | _headers('text/html'); 89 | Whostmgr::HTMLInterface::defheader('cPanel Security Advisor'); 90 | print <<'EOM'; 91 |
92 |
93 |

Permission denied

94 | 95 | 96 | EOM 97 | exit; 98 | } 99 | } 100 | 101 | sub _headers { 102 | my $content_type = shift; 103 | 104 | print "Content-type: ${content_type}; charset=utf-8\r\n\r\n"; 105 | 106 | return 1; 107 | } 108 | 109 | # Start a new scan writing to the comet channel specified 110 | sub _start_scan { 111 | Cpanel::Rlimit::set_rlimit_to_infinity(); # we need to run yum :) 112 | 113 | my $channel = shift; 114 | _headers('text/json'); 115 | 116 | if ( !$channel ) { 117 | print qq({"status":0,"message":"No scan channel was specified."}\n); 118 | return; 119 | } 120 | if ( $channel !~ m{\A[/A-Za-z_0-9]+\z} ) { 121 | print qq({"status":0,"message":"Invalid channel name."}\n); 122 | return; 123 | } 124 | 125 | my $comet = Cpanel::Comet->new(); 126 | if ( !$comet->subscribe($channel) ) { 127 | print qq({"status":0,"message":"Failed to subscribe to channel."}\n); 128 | return; 129 | } 130 | 131 | my $pid = fork(); 132 | if ( !defined $pid ) { 133 | print qq({"status":0,"message":"Failed to fork scanning subprocess."}\n); 134 | return; 135 | } 136 | elsif ($pid) { 137 | print qq({"status":1,"message":"Scan started."}\n); 138 | return; 139 | } 140 | else { 141 | POSIX::setsid(); 142 | open STDOUT, ">&STDERR" or die "Could not redirect STDOUT to STDERR"; 143 | open STDIN, "<", "/dev/null" or die "Could not attach STDIN to /dev/null"; 144 | my $advisor = Cpanel::Security::Advisor->new( 'comet' => $comet, 'channel' => $channel ); 145 | 146 | $advisor->generate_advice(); 147 | exit; 148 | } 149 | } 150 | 151 | -------------------------------------------------------------------------------- /pkg/icon/ico-security-advisor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CpanelInc/addon_securityadvisor/ee83000c390935d70a8593b45b7ba1063a8fd083/pkg/icon/ico-security-advisor.png -------------------------------------------------------------------------------- /pkg/install: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | # Copyright (c) 2013, cPanel, Inc. 3 | # All rights reserved. 4 | # http://cpanel.net 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # * Neither the name of the owner nor the names of its contributors may 14 | # be used to endorse or promote products derived from this software 15 | # without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 21 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | mkdir( "/usr/local/cpanel/whostmgr/docroot/cgi/addons", 0755 ); 29 | mkdir( "/usr/local/cpanel/whostmgr/docroot/cgi/addons/securityadvisor", 0700 ); 30 | 31 | mkdir( "/var/cpanel/apps", 0755 ); 32 | mkdir( "/var/cpanel/addons", 0755 ); 33 | mkdir( "/var/cpanel/addons/securityadvisor", 0700 ); 34 | mkdir( '/var/cpanel/addons/securityadvisor/perl', 0700 ); 35 | 36 | system '/usr/bin/rsync', '-rlptD', 'templates', '/var/cpanel/addons/securityadvisor/'; 37 | system '/usr/bin/rsync', '-rlptD', 'bin', '/var/cpanel/addons/securityadvisor/'; 38 | system '/usr/bin/rsync', '-rlptD', 'Cpanel', '/var/cpanel/addons/securityadvisor/perl/'; 39 | 40 | if ( -x '/usr/local/cpanel/bin/register_appconfig' ) { 41 | install( "-o", "root", "-g", "wheel", "-m" . "0700", "cgi/addon_securityadvisor.cgi", "/usr/local/cpanel/whostmgr/docroot/cgi/addons/securityadvisor/index.cgi" ); 42 | unlink("/usr/local/cpanel/whostmgr/docroot/cgi/addon_securityadvisor.cgi") if -e "/usr/local/cpanel/whostmgr/docroot/cgi/addon_securityadvisor.cgi"; 43 | system '/usr/local/cpanel/bin/register_appconfig', "appconfig/securityadvisor.conf"; 44 | } 45 | else { 46 | install( "-o", "root", "-g", "wheel", "-m" . "0700", "cgi/addon_securityadvisor.cgi", "/usr/local/cpanel/whostmgr/docroot/cgi/addon_securityadvisor.cgi" ); 47 | install( "-o", "root", "-g", "wheel", "-m" . "0600", "appconfig/securityadvisor.conf", "/var/cpanel/apps/securityadvisor.conf" ); 48 | } 49 | 50 | mkdir( "/usr/local/cpanel/whostmgr/docroot/addon_plugins", 0755 ) if !-e "/usr/local/cpanel/whostmgr/docroot/addon_plugins"; 51 | install( "-o", "root", "-g", "wheel", "-m" . "0600", "icon/ico-security-advisor.png", "/usr/local/cpanel/whostmgr/docroot/addon_plugins/ico-security-advisor.png" ); 52 | 53 | print "cPanel Security Advisor installed into WHM.\n"; 54 | 55 | exit(0); 56 | 57 | sub install { 58 | system( "/usr/bin/install", @_ ); 59 | } 60 | 61 | -------------------------------------------------------------------------------- /pkg/install-dist: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | # Copyright (c) 2013, cPanel, Inc. 3 | # All rights reserved. 4 | # http://cpanel.net 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # * Neither the name of the owner nor the names of its contributors may 14 | # be used to endorse or promote products derived from this software 15 | # without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 21 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | use Cwd (); 29 | use Cpanel::SafeFind (); 30 | use Cpanel::LoadFile (); 31 | use Cpanel::FileUtils::Write (); 32 | 33 | if ( !-e '/var/cpanel/dev_sandbox' ) { 34 | print "This installer script is only intended to run on internal cPanel systems.\n"; 35 | print "Please use the standard install script (install) instead of $0\n"; 36 | exit(1); 37 | } 38 | 39 | my $git_bin = '/usr/local/cpanel/3rdparty/bin/git'; 40 | 41 | my $pwd = Cwd::cwd(); 42 | 43 | chdir("/usr/local/cpanel") or die "Could not chdir to /usr/local/cpanel: $!"; 44 | 45 | # Remove old modules 46 | system $git_bin, 'rm', '-rf', 'Cpanel/Security/Advisor'; 47 | system $git_bin, 'rm', '-f', 'Cpanel/Security/Advisor.pm'; 48 | 49 | # Remove old templates 50 | system $git_bin, 'rm', '-rf', '/usr/local/cpanel/whostmgr/docroot/templates/securityadvisor'; 51 | 52 | # Remove old cgi 53 | system $git_bin, 'rm', '-rf', '/usr/local/cpanel/whostmgr/docroot/cgi/securityadvisor/index.cgi'; 54 | 55 | # Remove old icon 56 | system $git_bin, 'rm', '-rf', '/usr/local/cpanel/whostmgr/docroot/themes/x/icons/ico-security-advisor.png'; 57 | 58 | chdir($pwd) or die "Could not return to original directory: $pwd: $!"; 59 | 60 | # Install the new version 61 | system '/usr/bin/rsync', '-rlptD', 'templates/', '/usr/local/cpanel/whostmgr/docroot/templates/securityadvisor/'; 62 | system '/usr/bin/rsync', '-rlptD', 'Cpanel/Security/Advisor/', '/usr/local/cpanel/Cpanel/Security/Advisor/'; 63 | install( 64 | "-o", "root", "-g", "wheel", "-m" . "0644", 65 | 'Cpanel/Security/Advisor.pm', 66 | '/usr/local/cpanel/Cpanel/Security/Advisor.pm' 67 | ); 68 | install( 69 | "-o", 70 | "root", 71 | "-g", 72 | "wheel", 73 | "-m" . "0600", 74 | "icon/ico-security-advisor.png", 75 | "/usr/local/cpanel/whostmgr/docroot/themes/x/icons/ico-security-advisor.png" 76 | ); 77 | 78 | mkdir( "/usr/local/cpanel/whostmgr/docroot/cgi/securityadvisor", 0700 ); 79 | 80 | install( 81 | "-o", "root", "-g", "wheel", 82 | "-m" . "0700", 83 | "cgi/addon_securityadvisor.cgi", 84 | "/usr/local/cpanel/whostmgr/docroot/cgi/securityadvisor/index.cgi" 85 | ); 86 | 87 | chdir("/usr/local/cpanel") or die "Could not chdir to /usr/local/cpanel: $!"; 88 | 89 | _update_inline_licenses(); 90 | 91 | _update_template("/usr/local/cpanel/whostmgr/docroot/templates/securityadvisor/main.tmpl"); 92 | 93 | system $git_bin, "add", "Cpanel/Security/Advisor", "Cpanel/Security/Advisor.pm", "whostmgr/docroot/themes/x/icons/ico-security-advisor.png", 'whostmgr/docroot/templates/securityadvisor', 'whostmgr/docroot/cgi/securityadvisor/index.cgi'; 94 | 95 | #system $git_bin, "commit", "-m", "Update cPanel Security Advisor\ncase XXXXX: Update cPanel Security Advisor\n"; 96 | 97 | chdir($pwd) or die "Could not return to original directory: $pwd: $!"; 98 | 99 | print "cPanel Security Advisor installed into WHM (distro mode).\n"; 100 | 101 | exit(0); 102 | 103 | sub install { 104 | system( "/usr/bin/install", @_ ); 105 | } 106 | 107 | sub _update_inline_licenses { 108 | Cpanel::SafeFind::find( 109 | { 110 | 'wanted' => sub { 111 | if ( $_ =~ m{\.pm$} && $_ =~ m{Advisor} ) { 112 | _update_to_product_license($File::Find::name); 113 | } 114 | 115 | }, 116 | 'no_chdir' => 1 117 | }, 118 | "/usr/local/cpanel/Cpanel/Security", 119 | ); 120 | 121 | _update_to_product_license("/usr/local/cpanel/whostmgr/docroot/cgi/securityadvisor/index.cgi"); 122 | 123 | } 124 | 125 | sub _update_to_product_license { 126 | my ($file) = @_; 127 | 128 | my $shortfile = $file; 129 | $shortfile =~ s{^/usr/local/cpanel/}{}g; 130 | 131 | print "[$shortfile]\n"; 132 | 133 | my $license = <<'EOM'; 134 | 135 | # cpanel - %FILE% Copyright(c) 2013 cPanel, Inc. 136 | # All rights Reserved. 137 | # copyright@cpanel.net http://cpanel.net 138 | # This code is subject to the cPanel license. Unauthorized copying is prohibited 139 | EOM 140 | $license =~ s/\%FILE\%/$shortfile/g; 141 | 142 | my $text = Cpanel::LoadFile::loadfile($file); 143 | 144 | $text =~ s/\n#[ \t]+Copyright.*?DAMAGE\./$license/s; 145 | 146 | if ( $file =~ m/\.cgi$/ ) { 147 | Cpanel::FileUtils::Write::writefile( $file, $text, 0700 ); 148 | system '/usr/local/cpanel//3rdparty/perl/514/bin/perltidy', '-x', '-b', $file; 149 | } 150 | else { 151 | Cpanel::FileUtils::Write::writefile( $file, $text, 0644 ); 152 | system '/usr/local/cpanel//3rdparty/perl/514/bin/perltidy', '-b', $file; 153 | } 154 | 155 | unlink("$file.bak"); 156 | } 157 | 158 | sub _update_template { 159 | my ($file) = @_; 160 | 161 | my $shortfile = $file; 162 | $shortfile =~ s{^/usr/local/cpanel/}{}g; 163 | 164 | print "[$shortfile]\n"; 165 | 166 | my $text = Cpanel::LoadFile::loadfile($file); 167 | 168 | $text =~ s{/addon_plugins/}{}g; 169 | 170 | Cpanel::FileUtils::Write::writefile( $file, $text, 0644 ); 171 | } 172 | -------------------------------------------------------------------------------- /pkg/templates/main.tmpl: -------------------------------------------------------------------------------- 1 | [% USE Whostmgr -%] 2 | [% USE JSON %] 3 | 4 | [% SET wrapper_path = '_defwrapper.tmpl', 5 | wrapper_theme = ''; 6 | %] 7 | 8 | [% IF Whostmgr.RELEASE_VERSION >= 63; 9 | SET wrapper_path = 'master_templates/master.tmpl' 10 | wrapper_theme = 'bootstrap'; 11 | END; %] 12 | 13 | [% WRAPPER $wrapper_path 14 | header = 'cPanel Security Advisor' 15 | icon = '/addon_plugins/ico-security-advisor.png' 16 | theme = wrapper_theme 17 | scripts = [ 18 | Whostmgr.find_file_url('libraries/handlebars/handlebars.min.js'), 19 | Whostmgr.find_file_url('sharedjs/cometd_optimized.js'), 20 | Whostmgr.find_file_url('sharedjs/yui/yui.cometd_optimized.js'), 21 | ]; 22 | -%] 23 | 24 | [% PROCESS '_ajaxapp_styles.tmpl' -%] 25 | [% PROCESS '_ajaxapp_header.tmpl' -%] 26 | 44 | 45 | 46 | 53 | 54 | 55 | 56 | 57 | 58 | [% locale.maketext("[output,strong,Version:] [_1]", security_advisor_version) %] 59 | 60 | 61 | 62 |
63 |

64 | [% locale.maketext('Important') %] 65 |

66 |
67 |

68 | [% locale.maketext('Recommendations') %] 69 |

70 |
71 |

72 | [% locale.maketext('Information') %] 73 |

74 |
75 |

76 | [% locale.maketext('Verified') %] 77 |

78 |
79 |
80 | 93 | 116 | 129 | 142 | 143 | 328 | 329 | [% PROCESS '_ajaxapp_footer.tmpl' -%] 330 | [% END #wrapper -%] 331 | -------------------------------------------------------------------------------- /pkg/uninstall: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | # Copyright (c) 2013, cPanel, Inc. 3 | # All rights reserved. 4 | # http://cpanel.net 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # * Neither the name of the owner nor the names of its contributors may 14 | # be used to endorse or promote products derived from this software 15 | # without specific prior written permission. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 21 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | use Cpanel::Version (); 29 | 30 | unlink("/usr/local/cpanel/whostmgr/docroot/cgi/addon_securityadvisor.cgi"); 31 | unlink("/usr/local/cpanel/whostmgr/docroot/cgi/securityadvisor/index.cgi"); 32 | unlink("/usr/local/cpanel/whostmgr/docroot/addon_plugins/ico-security-advisor.png"); 33 | unlink("/var/cpanel/apps/securityadvisor.conf"); 34 | 35 | system "/bin/rm", "-rf", "--", "/var/cpanel/addons/securityadvisor/"; 36 | system "/bin/rm", "-rf", "--", "/usr/local/cpanel/whostmgr/docroot/cgi/addons/securityadvisor"; 37 | 38 | if ( -x '/usr/local/cpanel/bin/unregister_appconfig' ) { 39 | system '/usr/local/cpanel/bin/unregister_appconfig', "appconfig/securityadvisor.conf"; 40 | } 41 | 42 | print "cPanel Security Advisor uninstalled from WHM.\n"; 43 | 44 | exit(0); 45 | -------------------------------------------------------------------------------- /t/lib/Cpanel/Security/Advisor/Assessors/MockAssessor.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::MockAssessor; 2 | 3 | # Copyright (c) 2020, cPanel, L.L.C. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use warnings; 31 | use base 'Cpanel::Security::Advisor::Assessors'; 32 | 33 | 34 | sub version { 35 | return '9.99'; 36 | } 37 | 38 | sub generate_advice { 39 | my ($self) = @_; 40 | $self->add_good_advice( 41 | 'key' => 'example_good_advice', 42 | 'text' => 'This is good.', 43 | 'suggestion' => 'A suggestion.' 44 | ); 45 | $self->add_info_advice( 46 | 'key' => 'example_info_advice', 47 | 'text' => 'This is info.', 48 | 'suggestion' => 'A suggestion.' 49 | ); 50 | $self->add_warn_advice( 51 | 'key' => 'example_warn_advice', 52 | 'text' => 'This is a warning.', 53 | 'suggestion' => 'A suggestion.' 54 | ); 55 | $self->add_bad_advice( 56 | 'key' => 'example_bad_advice', 57 | 'text' => 'This is bad.', 58 | 'suggestion' => 'A suggestion.' 59 | ); 60 | return 1; 61 | } 62 | 63 | 1; 64 | -------------------------------------------------------------------------------- /t/lib/Cpanel/Security/Advisor/Assessors/MockLoadFail.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::MockLoadFail; 2 | 3 | # Copyright (c) 2020, cPanel, L.L.C. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use warnings; 31 | use base 'Cpanel::Security::Advisor::Assessors'; 32 | 33 | die 'No assessor for you!'; 34 | 35 | 1; 36 | -------------------------------------------------------------------------------- /t/lib/Cpanel/Security/Advisor/Assessors/MockNewFail.pm: -------------------------------------------------------------------------------- 1 | package Cpanel::Security::Advisor::Assessors::MockNewFail; 2 | 3 | # Copyright (c) 2020, cPanel, L.L.C. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use warnings; 31 | use base 'Cpanel::Security::Advisor::Assessors'; 32 | 33 | sub new { 34 | die 'No new for you!'; 35 | } 36 | 37 | 1; 38 | -------------------------------------------------------------------------------- /t/lib/Test/Assessor.pm: -------------------------------------------------------------------------------- 1 | package Test::Assessor; 2 | 3 | # Copyright (c) 2017, cPanel, Inc. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use warnings; 31 | 32 | use Cpanel::Locale (); 33 | use Cpanel::Security::Advisor (); # For ADVISE_GOOD, et. al. 34 | 35 | sub new { 36 | my ( $class, %options ) = @_; 37 | 38 | my $module_name = "Cpanel::Security::Advisor::Assessors::$options{assessor}"; 39 | 40 | my $self = bless { 41 | locale => Cpanel::Locale->get_handle(), 42 | advice => [], 43 | }, $class; 44 | 45 | eval "require $module_name" or die $@; ##no critic (ProhibitStringyEval) -- require $module_name; doesn't work for some reason. 46 | my $assessor = "$module_name"->new($self); 47 | $self->{assessor} = $assessor; 48 | 49 | return $self; 50 | } 51 | 52 | sub generate_advice { 53 | my ($self) = @_; 54 | return $self->{assessor}->generate_advice(); 55 | } 56 | 57 | sub add_advice { 58 | my ( $self, $advice ) = @_; 59 | 60 | # Some assessor modules call methods directly on instances of this class, 61 | # and some use wrapper methods, so try to figure out the module name 62 | # regardless of which path we took. 63 | my ( $module, $function ); 64 | foreach my $level ( 1, 3 ) { 65 | my $caller = ( caller($level) )[3]; 66 | if ( $caller =~ /(Cpanel::Security::Advisor::Assessors::.+)::([^:]+)$/ ) { 67 | ( $module, $function ) = ( $1, $2 ); 68 | last; 69 | } 70 | } 71 | 72 | push @{ $self->{advice} }, { 73 | module => $module, 74 | function => $function, 75 | advice => $advice, 76 | }; 77 | 78 | return; 79 | } 80 | 81 | sub get_advice { 82 | my ($self) = @_; 83 | return $self->{advice}; 84 | } 85 | 86 | sub clear_advice { 87 | my ($self) = @_; 88 | $self->{advice} = []; 89 | return; 90 | } 91 | 92 | 1; 93 | -------------------------------------------------------------------------------- /t/lib/Test/Mock/SecurityAdvisor.pm: -------------------------------------------------------------------------------- 1 | package Test::Mock::SecurityAdvisor; 2 | 3 | # Copyright (c) 2020, cPanel, L.L.C. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use warnings; 31 | 32 | use Test::MockModule qw/strict/; 33 | use Test::MockFile; 34 | 35 | use Cpanel::Comet::Mock (); 36 | use Cpanel::JSON (); 37 | use Cpanel::Security::Advisor (); 38 | 39 | use constant { 40 | COMET_CHANNEL => 'securityadvisor', 41 | }; 42 | 43 | sub new { 44 | return bless { 45 | '_comet' => Cpanel::Comet::Mock->new(), 46 | }, 47 | __PACKAGE__; 48 | } 49 | 50 | sub new_advisor_object { 51 | my ($self) = @_; 52 | return Cpanel::Security::Advisor->new( 'comet' => $self->{'_comet'}, 'channel' => COMET_CHANNEL() ); 53 | } 54 | 55 | sub mock_all { 56 | my ($self) = @_; 57 | 58 | $self->set_assessor_module('Cpanel::Security::Advisor::Assessors::MockAssessor'); 59 | 60 | $self->mock_func( 61 | 'Cpanel::Logger', 62 | 'warn' => sub { 63 | 64 | # For testing CPANEL-33980. Clobber $@ a.k.a. $EVAL_ERROR because that's what Cpanel::Logger does. 65 | eval { die 'Not what you expected, grasshopper?' }; 66 | return 1; 67 | } 68 | ); 69 | 70 | $self->mock_func( 71 | 'Cpanel::LoadModule', 72 | 'load_perl_module' => sub { 73 | my $module = shift; 74 | if ( index( $module, 'Cpanel::Security::Advisor::Assessors' ) >= 0 && index( $module, 'Mock' ) == -1 ) { 75 | die "Attempting to load an unmocked assessor module: $module"; 76 | } 77 | 78 | # Otherwise, make a real attempt to load the module. 79 | return $self->get_mock_module('Cpanel::LoadModule')->original('load_perl_module')->($module); 80 | } 81 | ); 82 | 83 | return; 84 | } 85 | 86 | sub set_assessor_module { 87 | my ( $self, $module ) = @_; 88 | $self->mock_func( 89 | 'Cpanel::LoadModule::AllNames', 90 | 'get_loadable_modules_in_namespace' => sub { 91 | return { $module => 'testing' }; 92 | } 93 | ); 94 | return; 95 | } 96 | 97 | sub get_advisor_messages { 98 | my ($self) = @_; 99 | my @decoded; 100 | for my $msg ( @{ $self->{'_comet'}->get_messages( COMET_CHANNEL() ) } ) { 101 | push @decoded, Cpanel::JSON::Load($msg); 102 | } 103 | return \@decoded; 104 | } 105 | 106 | sub get_mock_module { 107 | my ( $self, $module ) = @_; 108 | die "No mock for $module exists" unless exists $self->{'_module'}->{$module}; 109 | return $self->{'_module'}->{$module}; 110 | } 111 | 112 | sub get_func_calls { 113 | my ( $self, $module, $func ) = @_; 114 | die "No mock for ${module}::${func} exists" unless exists $self->{'_module'}->{$module} && $self->{'_module'}->{$module}->is_mocked($func); 115 | return $self->{'_calls'}->{$module}->{$func}; 116 | } 117 | 118 | sub mock_func { 119 | my ( $self, $module, $func, $impl ) = @_; 120 | if ( !exists $self->{'_module'}->{$module} ) { 121 | $self->{'_module'}->{$module} = Test::MockModule->new($module); 122 | } 123 | @{ $self->{'_calls'}->{$module}->{$func} } = (); 124 | $self->{'_module'}->{$module}->redefine( 125 | $func => sub { 126 | push @{ $self->{'_calls'}->{$module}->{$func} }, [@_]; 127 | if ( ref $impl eq 'CODE' ) { 128 | return $impl->(@_); 129 | } 130 | return $impl; 131 | } 132 | ); 133 | return $self->{'_module'}->{$module}; 134 | } 135 | 136 | sub mock_file { 137 | my ( $self, $dir, $file, $contents ) = @_; 138 | my $fullpath = $dir . q{/} . $file; 139 | 140 | if ( exists $self->{'_file'}->{$dir} ) { 141 | $self->{'_file'}->{$dir}->contents( [ sort( @{ $self->{'_file'}->{$dir}->contents() }, $file ) ] ); 142 | } 143 | else { 144 | $self->{'_file'}->{$dir} = Test::MockFile->dir( $dir, [$file] ); 145 | } 146 | 147 | $self->{'_file'}->{$fullpath} = Test::MockFile->file( $fullpath, $contents ); 148 | 149 | return; 150 | } 151 | 152 | 1; 153 | -------------------------------------------------------------------------------- /t/pkg-Cpanel-Security-Advisor-Assessors-Apache.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright (c) 2018, cPanel, L.L.C. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use warnings; 31 | 32 | use FindBin; 33 | use lib "$FindBin::Bin/lib", "$FindBin::Bin/../pkg"; 34 | 35 | use Test::More; 36 | use Cpanel::Version (); 37 | use Cpanel::Version::Tiny (); 38 | 39 | plan skip_all => 'Requires cPanel & WHM v66 or later' if Cpanel::Version::compare( Cpanel::Version::getversionnumber(), '<', '11.65' ); 40 | plan tests => 4; 41 | 42 | use Cpanel::Security::Advisor::Assessors::Apache (); 43 | 44 | my %doesnot_doea3 = ( 45 | 75 => 0, 46 | 76 => 0, 47 | 77 => 1, 48 | 78 => 1, 49 | ); 50 | 51 | for my $v (qw(75 76 77 78)) { 52 | local $Cpanel::Version::Tiny::major_version = $v; 53 | 54 | my @calls; 55 | no warnings "redefine"; 56 | local *Cpanel::Security::Advisor::Assessors::Apache::_check_for_easyapache3_eol = sub { push @calls, '_check_for_easyapache3_eol' }; 57 | local *Cpanel::Security::Advisor::Assessors::Apache::_check_for_apache_chroot = sub { push @calls, '_check_for_apache_chroot' }; 58 | local *Cpanel::Security::Advisor::Assessors::Apache::_check_for_easyapache_build = sub { push @calls, '_check_for_easyapache_build' }; 59 | local *Cpanel::Security::Advisor::Assessors::Apache::_check_for_eol_apache = sub { push @calls, '_check_for_eol_apache' }; 60 | local *Cpanel::Security::Advisor::Assessors::Apache::_check_for_symlink_protection = sub { push @calls, '_check_for_symlink_protection' }; 61 | 62 | Cpanel::Security::Advisor::Assessors::Apache->generate_advice(); 63 | 64 | if ( $doesnot_doea3{$v} ) { 65 | is_deeply \@calls, [ '_check_for_apache_chroot', '_check_for_symlink_protection' ], "v$v does not do ea3 specific checks"; 66 | } 67 | else { 68 | is_deeply \@calls, [ '_check_for_easyapache3_eol', '_check_for_apache_chroot', '_check_for_easyapache_build', '_check_for_eol_apache', '_check_for_symlink_protection' ], "v$v does do ea3 specific checks"; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /t/pkg-Cpanel-Security-Advisor-Assessors-Imunify360.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2018, cPanel, L.L.C. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use warnings; 31 | 32 | use FindBin; 33 | use lib "$FindBin::Bin/lib", "$FindBin::Bin/../pkg"; 34 | 35 | use Cpanel::Version (); 36 | use Test::Assessor (); 37 | use Test::Deep; 38 | use Test::More; 39 | use Test::NoWarnings (); 40 | use Test::MockModule; 41 | use HTTP::Response; 42 | 43 | use Cpanel::Security::Advisor::Assessors::Imunify360 (); 44 | 45 | local $ENV{"REQUEST_URI"} = ""; 46 | 47 | plan skip_all => 'Requires cPanel & WHM v80 or later' if Cpanel::Version::compare( Cpanel::Version::getversionnumber(), '<', '11.79' ); 48 | 49 | Test::NoWarnings->import(); # defer import until this line for skip_all compatibility 50 | 51 | my $mocked_version_module = Test::MockModule->new('Cpanel::Version'); 52 | my $imunify = Test::MockModule->new('Whostmgr::Imunify360'); 53 | my $mocked_HTTP = Test::MockModule->new('Cpanel::HTTP::Client'); 54 | $mocked_HTTP->redefine( 55 | get => sub { 56 | my @args = @_; 57 | die explain [ 'Unmocked HTTP request:', \@args ]; 58 | } 59 | ); 60 | my $mocked_cpstore = Test::MockModule->new('Cpanel::cPStore'); 61 | 62 | $mocked_cpstore->redefine( 63 | get => sub { 64 | [ 65 | { item_id => '373', short_name => 'Imunify360' }, # incomplete but sufficient for the tests 66 | { item_id => '447', short_name => 'ImunifyAV+' }, 67 | ] 68 | }, 69 | ); 70 | 71 | my $response_imunify_disabled = Cpanel::HTTP::Client::Response->new( 72 | { 73 | success => 1, 74 | status => 200, 75 | content => ' 76 | { 77 | "disabled": 1, 78 | "url": "", 79 | "email": "" 80 | }', 81 | } 82 | ); 83 | $response_imunify_disabled->header( 'Content-Type', 'application/json' ); 84 | 85 | my $response_imunify_enabled = Cpanel::HTTP::Client::Response->new( 86 | { 87 | success => 1, 88 | status => 200, 89 | content => ' 90 | { 91 | "disabled": 0, 92 | "url": "", 93 | "email": "" 94 | }', 95 | } 96 | ); 97 | $response_imunify_enabled->header( 'Content-Type', 'application/json' ); 98 | 99 | plan tests => 9 + 1; 100 | 101 | subtest 'When Imunify360 is disabled' => sub { 102 | plan tests => 1; 103 | 104 | $mocked_version_module->redefine( getversionnumber => sub { '11.80' } ); 105 | $mocked_HTTP->redefine( get => $response_imunify_disabled ); 106 | my $advice = get_advice(); 107 | 108 | is_deeply( $advice, [], "Should not return the Imunify360 advice" ) or diag explain $advice; 109 | }; 110 | 111 | subtest 'When Imunify360 is enabled' => sub { 112 | plan tests => 1; 113 | 114 | $mocked_version_module->redefine( getversionnumber => sub { '11.80' } ); 115 | $mocked_HTTP->redefine( get => $response_imunify_enabled ); 116 | my $advice = get_advice(); 117 | 118 | cmp_deeply( $advice, [ superhashof( { advice => ignore() } ) ], "Should return the Imunify360 advice" ) 119 | or diag explain $advice; 120 | }; 121 | 122 | $imunify->redefine( is_imunify360_licensed => sub { 0 } ); 123 | $imunify->redefine( is_imunify360_installed => sub { 0 } ); 124 | 125 | subtest 'When Imunify360 is not installed or licensed' => sub { 126 | plan tests => 1; 127 | 128 | $imunify->redefine( is_imunify360_licensed => sub { 0 } ); 129 | $imunify->redefine( is_imunify360_installed => sub { 0 } ); 130 | 131 | my $advice = get_advice(); 132 | my $expected = { 133 | 'advice' => { 134 | 'key' => 'Imunify360_purchase', 135 | 'block_notify' => ignore(), 136 | 'suggestion' => ignore(), 137 | 'text' => ignore(), 138 | 'type' => ignore(), 139 | }, 140 | }; 141 | 142 | cmp_deeply( $advice->[0], superhashof($expected), "It should advice buying an Imunify360 license" ) or diag explain $advice; 143 | }; 144 | 145 | subtest 'When Imunify360 is installed' => sub { 146 | plan tests => 1; 147 | 148 | $imunify->redefine( is_imunify360_installed => sub { 1 } ); 149 | 150 | my $advice = get_advice(); 151 | my $expected = { 152 | 'advice' => superhashof( 153 | { 154 | 'key' => 'Imunify360_present', 155 | } 156 | ), 157 | }; 158 | 159 | cmp_deeply( $advice->[0], superhashof($expected), "It should say that the server is protected" ) or diag explain $advice; 160 | }; 161 | 162 | subtest 'When the custom URL is present' => sub { 163 | plan tests => 1; 164 | SKIP: { 165 | if ( Cpanel::Version::compare( Cpanel::Version::getversionnumber(), '<', '11.81' ) ) { 166 | skip 'Only on 82+', 1; 167 | } 168 | 169 | $imunify->redefine( get_custom_url => sub { 'https://example.com' } ); 170 | $imunify->redefine( is_imunify360_licensed => sub { 0 } ); 171 | $imunify->redefine( is_imunify360_installed => sub { 0 } ); 172 | 173 | my $advice = get_advice(); 174 | 175 | like( $advice->[0]->{advice}->{suggestion}, qr{https://example.com}, "It should change the link href" ) 176 | or diag explain $advice; 177 | } 178 | }; 179 | 180 | subtest 'When the custom URL is NOT present' => sub { 181 | plan tests => 1; 182 | 183 | SKIP: { 184 | if ( Cpanel::Version::compare( Cpanel::Version::getversionnumber(), '<', '11.81' ) ) { 185 | skip 'Only on 82+', 1; 186 | } 187 | $imunify->redefine( get_custom_url => sub { '' } ); 188 | $imunify->redefine( is_imunify360_licensed => sub { 0 } ); 189 | $imunify->redefine( is_imunify360_installed => sub { 0 } ); 190 | 191 | my $advice = get_advice(); 192 | 193 | like( $advice->[0]->{advice}->{suggestion}, qr{scripts12/purchase_imunify360_init}, "It should link to the init script" ) 194 | or diag explain $advice; 195 | } 196 | }; 197 | 198 | $mocked_version_module->redefine( getversionnumber => sub { '11.88' } ); 199 | my $imunify_avp = Test::MockModule->new('Whostmgr::Store::Product::ImunifyAVPlus'); 200 | $imunify_avp->redefine( should_offer => sub { 1 } ); 201 | $imunify_avp->redefine( is_product_installed => sub { 0 } ); 202 | $imunify_avp->redefine( is_product_licensed => sub { 0 } ); 203 | 204 | $mocked_HTTP->redefine( get => $response_imunify_disabled ); 205 | 206 | subtest 'ImunifyAV+ advice when Imunify360 is disabled' => sub { 207 | plan tests => 1; 208 | 209 | my $advice = get_advice(); 210 | my $expected = { 211 | 'advice' => superhashof( 212 | { 213 | 'key' => 'ImunifyAV+_info', 214 | } 215 | ), 216 | }; 217 | 218 | cmp_deeply( 219 | $advice->[0], 220 | superhashof($expected), 'ImunifyAV+ advice is offered when Imunify360 is disabled' 221 | ) or diag explain $advice; 222 | }; 223 | 224 | $mocked_HTTP->redefine( get => $response_imunify_enabled ); 225 | $imunify->redefine( is_imunify360_installed => sub { 1 } ); 226 | 227 | subtest 'ImunifyAV+ advice when Imunify360 is installed' => sub { 228 | plan tests => 1; 229 | 230 | my $advice = get_advice(); 231 | ok( 232 | !( grep { /ImunifyAV/i } map { $_->{advice}->{key} } @{$advice} ), 233 | 'ImunifyAV+ advice is not offered when Imunify360 is installed' 234 | ) or diag explain $advice; 235 | }; 236 | 237 | subtest 'When RPM is broken' => sub { 238 | plan tests => 1; 239 | $imunify->redefine( is_imunify360_installed => sub { die "Unable to find the rpm binary" } ); 240 | $imunify_avp->redefine( is_product_licensed => sub { 0 } ); 241 | my $advice = get_advice(); 242 | 243 | is_deeply $advice, 244 | [ 245 | { 246 | 'advice' => { 247 | 'block_notify' => 1, 248 | 'key' => 'Immunify360_rpm_failure', 249 | 'suggestion' => 'Ensure that yum and rpm are working on your system.', 250 | 'text' => 'Unable to determine if Imunify360 is installed', 251 | 'type' => 8 252 | }, 253 | 'function' => 'generate_advice', 254 | 'module' => 'Cpanel::Security::Advisor::Assessors::Imunify360' 255 | } 256 | ]; 257 | }; 258 | 259 | sub get_advice { 260 | my $object = Test::Assessor->new( assessor => 'Imunify360' ); 261 | $object->generate_advice(); 262 | my $advice = $object->get_advice(); 263 | 264 | return $advice; 265 | } 266 | -------------------------------------------------------------------------------- /t/pkg-Cpanel-Security-Advisor-Assessors-Processes.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright (c) 2017, cPanel, Inc. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use warnings; 31 | 32 | use FindBin; 33 | use lib "$FindBin::Bin/lib", "$FindBin::Bin/../../pkg"; 34 | 35 | use Test::More; 36 | use Test::Deep; 37 | use Test::MockModule; 38 | use IO::Pty; 39 | 40 | use Test::Assessor (); 41 | 42 | use Cpanel::Exception (); 43 | use Cpanel::Version (); 44 | 45 | plan skip_all => 'Requires cPanel & WHM v66 or later' if Cpanel::Version::compare( Cpanel::Version::getversionnumber(), '<', '11.65' ); 46 | plan tests => 6; 47 | 48 | # Suppress warnings related to checking this path, and create relative paths that are realistic 49 | local $ENV{REQUEST_URI} = '/cpsessXXXXXXX/cgi/securityadvisor/index.cgi'; 50 | 51 | # Avoid maketext failures related to html vs. text context when non-interactive 52 | my $pty = IO::Pty->new(); 53 | local *STDIN = $pty->slave; 54 | 55 | subtest 'Missing executable' => sub { 56 | plan tests => 1; 57 | 58 | my $outdated = Test::MockModule->new('Cpanel::ProcessCheck::Outdated'); 59 | my $err = sub { die Cpanel::Exception::create( 'Service::BinaryNotFound', [ service => 'needs-restarting' ] ) }; 60 | $outdated->mock( 61 | reboot_suggested => $err, 62 | outdated_services => $err, 63 | outdated_processes => $err, 64 | ); 65 | 66 | my $expected = { 67 | key => 'Processes_unable_to_check_running_executables', 68 | text => 'Unable to check whether running executables are up-to-date.', 69 | suggestion => 'Install the ‘needs-restarting’ command to check if processes are up-to-date.', 70 | type => $Cpanel::Security::Advisor::ADVISE_INFO, 71 | }; 72 | cmp_assessor( 'Processes', [$expected], 'Error displayed' ); 73 | }; 74 | 75 | subtest 'Handle unexpected error' => sub { 76 | plan tests => 6; 77 | 78 | my $outdated = Test::MockModule->new('Cpanel::ProcessCheck::Outdated'); 79 | my $err = sub { die "Simple string\n" }; 80 | $outdated->mock( 81 | reboot_suggested => sub { }, 82 | outdated_services => sub { }, 83 | outdated_processes => $err, 84 | ); 85 | 86 | my @expected; 87 | unshift @expected, { 88 | key => 'Processes_error_while_checking_running_executables', 89 | text => "Failed to check whether running executables are up-to-date: Simple string\n", 90 | type => $Cpanel::Security::Advisor::ADVISE_WARN, 91 | }; 92 | cmp_assessor( 'Processes', \@expected, 'Warning displayed for plain error on outdated processes' ); 93 | 94 | $outdated->mock( outdated_services => $err ); 95 | unshift @expected, { 96 | key => 'Processes_error_while_checking_running_services', 97 | text => "Failed to check whether active services are up-to-date: Simple string\n", 98 | type => $Cpanel::Security::Advisor::ADVISE_WARN, 99 | }; 100 | cmp_assessor( 'Processes', \@expected, 'Warning displayed for plain error on outdated services' ); 101 | 102 | $outdated->mock( reboot_suggested => $err ); 103 | unshift @expected, { 104 | key => 'Processes_error_while_checking_reboot', 105 | text => "Failed to determine if a reboot is necessary: Simple string\n", 106 | type => $Cpanel::Security::Advisor::ADVISE_WARN, 107 | }; 108 | cmp_assessor( 'Processes', \@expected, 'Warning displayed for plain error on reboot suggestion' ); 109 | 110 | $err = sub { die Cpanel::Exception->create('Cpanel::Exception object') }; 111 | $outdated->mock( 112 | reboot_suggested => sub { }, 113 | outdated_services => sub { }, 114 | outdated_processes => $err, 115 | ); 116 | 117 | @expected = (); 118 | unshift @expected, { 119 | key => 'Processes_error_while_checking_running_executables', 120 | text => 'Failed to check whether running executables are up-to-date: Cpanel::Exception object', 121 | type => $Cpanel::Security::Advisor::ADVISE_WARN, 122 | }; 123 | cmp_assessor( 'Processes', \@expected, 'Warning displayed for blessed error on outdated processes' ); 124 | 125 | $outdated->mock( outdated_services => $err ); 126 | unshift @expected, { 127 | key => 'Processes_error_while_checking_running_services', 128 | text => 'Failed to check whether active services are up-to-date: Cpanel::Exception object', 129 | type => $Cpanel::Security::Advisor::ADVISE_WARN, 130 | }; 131 | cmp_assessor( 'Processes', \@expected, 'Warning displayed for blessed error on outdated services' ); 132 | 133 | $outdated->mock( reboot_suggested => $err ); 134 | unshift @expected, { 135 | key => 'Processes_error_while_checking_reboot', 136 | text => 'Failed to determine if a reboot is necessary: Cpanel::Exception object', 137 | type => $Cpanel::Security::Advisor::ADVISE_WARN, 138 | }; 139 | cmp_assessor( 'Processes', \@expected, 'Warning displayed for blessed error on reboot suggestion' ); 140 | }; 141 | 142 | subtest 'Handle unsupported systems' => sub { 143 | plan tests => 2; 144 | 145 | my $outdated = Test::MockModule->new('Cpanel::ProcessCheck::Outdated'); 146 | my $err = sub { die Cpanel::Exception::create( 'Unsupported', 'The kernel does not support [asis,smaps].' ) }; 147 | $outdated->mock( 148 | reboot_suggested => $err, 149 | outdated_services => $err, 150 | outdated_processes => $err, 151 | ); 152 | 153 | cmp_assessor( 'Processes', [], 'No recommendations given' ); 154 | 155 | $err = sub { die Cpanel::Exception::create( 'Unsupported', 'Not supported by “[_1]” before [asis,CentOS 7].', ['needs-restarting'] ) }; 156 | $outdated->mock( 157 | reboot_suggested => $err, 158 | outdated_services => $err, 159 | outdated_processes => sub { }, 160 | ); 161 | 162 | my $expected = { 163 | key => 'Processes_none_with_outdated_executables', 164 | text => 'The system did not detect processes with outdated binaries.', 165 | type => $Cpanel::Security::Advisor::ADVISE_GOOD, 166 | }; 167 | cmp_assessor( 'Processes', [$expected], 'Good status displayed' ); 168 | }; 169 | 170 | subtest 'Recommend reboot' => sub { 171 | plan tests => 3; 172 | 173 | my $outdated = Test::MockModule->new('Cpanel::ProcessCheck::Outdated'); 174 | $outdated->mock( 175 | reboot_suggested => sub { { systemd => '219-30.el7_3.7' } }, 176 | outdated_services => sub { }, 177 | outdated_processes => sub { }, 178 | ); 179 | 180 | my $expected = { 181 | key => 'Processes_detected_running_from_outdated_executables', 182 | text => 'The system’s core libraries or services have been updated.', 183 | suggestion => 'Reboot the server (../../scripts/dialog?dialog=reboot) to ensure the system benefits from these updates.', 184 | type => $Cpanel::Security::Advisor::ADVISE_BAD, 185 | }; 186 | cmp_assessor( 'Processes', [$expected], 'Core libraries updated' ); 187 | 188 | $outdated->mock( 189 | outdated_services => sub { qw(exim.service cpanellogd.service) }, 190 | outdated_processes => sub { ( 2, 703, 5840 ) }, 191 | ); 192 | 193 | # $expected unchanged 194 | cmp_assessor( 'Processes', [$expected], 'Core libraries updated - other things too' ); 195 | 196 | $outdated->mock( 197 | reboot_suggested => sub { }, 198 | outdated_services => sub { }, 199 | outdated_processes => sub { (1) }, 200 | ); 201 | 202 | $expected = { 203 | key => 'Processes_detected_running_outdated_executables', 204 | text => 'Detected 1 process that is running outdated executables: 1', 205 | suggestion => 'Reboot the server (../../scripts/dialog?dialog=reboot) to ensure the system benefits from these updates.', 206 | type => $Cpanel::Security::Advisor::ADVISE_BAD, 207 | }; 208 | cmp_assessor( 'Processes', [$expected], 'PID 1 updated' ); 209 | }; 210 | 211 | subtest 'Recommend process restart' => sub { 212 | plan tests => 3; 213 | 214 | my $outdated = Test::MockModule->new('Cpanel::ProcessCheck::Outdated'); 215 | $outdated->mock( 216 | reboot_suggested => sub { }, 217 | outdated_services => sub { ( 'foo.service', 'bar.service' ) }, 218 | outdated_processes => sub { }, 219 | ); 220 | 221 | my $expected = { 222 | key => 'Processes_detected_running_outdated_services', 223 | text => 'Detected 2 services that are running outdated executables: foo.service bar.service', 224 | suggestion => 'You must take one of the following actions to ensure the system is up-to-date:', 225 | type => $Cpanel::Security::Advisor::ADVISE_BAD, 226 | }; 227 | cmp_assessor( 'Processes', [$expected], 'One process outdated' ); 228 | 229 | $outdated->mock( 230 | reboot_suggested => sub { }, 231 | outdated_services => sub { }, 232 | outdated_processes => sub { (42) }, 233 | ); 234 | 235 | $expected = { 236 | key => 'Processes_detected_running_outdated_executables', 237 | text => 'Detected 1 process that is running outdated executables: 42', 238 | suggestion => 'You must take one of the following actions to ensure the system is up-to-date:', 239 | type => $Cpanel::Security::Advisor::ADVISE_BAD, 240 | }; 241 | cmp_assessor( 'Processes', [$expected], 'One process outdated' ); 242 | 243 | $outdated->mock( 244 | outdated_processes => sub { ( 42, 1337, 2017, 9374, 31337 ) }, 245 | ); 246 | $expected = { 247 | key => 'Processes_detected_running_outdated_executables', 248 | text => 'Detected 5 processes that are running outdated executables: 42 1337 2017 9374 31337', 249 | suggestion => 'You must take one of the following actions to ensure the system is up-to-date:', 250 | type => $Cpanel::Security::Advisor::ADVISE_BAD, 251 | }; 252 | cmp_assessor( 'Processes', [$expected], 'Multiple processes outdated' ); 253 | }; 254 | 255 | subtest 'Status good' => sub { 256 | plan tests => 1; 257 | 258 | my $outdated = Test::MockModule->new('Cpanel::ProcessCheck::Outdated'); 259 | $outdated->mock( 260 | reboot_suggested => sub { }, 261 | outdated_services => sub { }, 262 | outdated_processes => sub { }, 263 | ); 264 | 265 | my $expected = { 266 | key => 'Processes_none_with_outdated_executables', 267 | text => 'The system did not detect processes with outdated binaries.', 268 | type => $Cpanel::Security::Advisor::ADVISE_GOOD, 269 | }; 270 | cmp_assessor( 'Processes', [$expected], 'Good status displayed' ); 271 | }; 272 | 273 | sub cmp_assessor { 274 | my ( $assessor, $expected_advice, $msg ) = @_; 275 | 276 | local $Test::Builder::Level = $Test::Builder::Level + 1; 277 | 278 | my $object = Test::Assessor->new( assessor => $assessor ); 279 | $object->generate_advice(); 280 | 281 | my $got = $object->get_advice(); 282 | $object->clear_advice(); 283 | 284 | my @expected = map { { module => "Cpanel::Security::Advisor::Assessors::$assessor", function => ignore(), advice => $_ } } @$expected_advice; 285 | 286 | my $ret = cmp_deeply( $got, \@expected, $msg ); 287 | diag explain $got if !$ret; 288 | 289 | return $ret; 290 | } 291 | -------------------------------------------------------------------------------- /t/pkg-Cpanel-Security-Advisor-Assessors-_Self.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright (c) 2020, cPanel, L.L.C. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use warnings; 31 | 32 | use FindBin; 33 | use lib "$FindBin::Bin/lib", "$FindBin::Bin/../pkg"; 34 | 35 | use Test::More; 36 | use Test::Deep; 37 | use Test::MockModule 'strict'; 38 | 39 | use Test::Assessor (); 40 | 41 | use Cpanel::Exception (); 42 | use Cpanel::Locale (); 43 | use Cpanel::Version (); 44 | 45 | use Cpanel::Security::Advisor (); 46 | use Cpanel::Security::Advisor::Assessors (); 47 | use Cpanel::Security::Advisor::Assessors::_Self (); 48 | 49 | plan skip_all => 'Requires cPanel & WHM v66 or later' if Cpanel::Version::compare( Cpanel::Version::getversionnumber(), '<', '11.65' ); 50 | plan tests => 4; 51 | 52 | # Suppress warning from base_path(): 53 | $ENV{'REQUEST_URI'} = ''; 54 | 55 | can_ok( 'Cpanel::Security::Advisor::Assessors::_Self', qw(version generate_advice) ); 56 | 57 | subtest 'RPM times out' => sub { 58 | plan tests => 1; 59 | 60 | # Mock for `rpm -qa` for when the program times out. 61 | my $mock_saferun = Test::MockModule->new('Cpanel::SafeRun::Full'); 62 | $mock_saferun->redefine( 63 | run => sub { 64 | return { 65 | 'did_dump_core' => 0, 66 | 'died_from_signal' => 15, 67 | 'exit_value' => 0, 68 | 'message' => 'Executed /usr/bin/rpm -qa --queryformat %{NAME} %{VERSION}-%{RELEASE}\\n', 69 | 'status' => 1, 70 | 'stderr' => '', 71 | 'stdout' => '', 72 | 'timeout' => 1, 73 | }; 74 | } 75 | ); 76 | my $expected = { 77 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 78 | 'key' => 'RPM_timed_out', 79 | 'text' => Cpanel::Locale::lh()->maketext('Security Advisor timed out while reading the RPM database of packages.'), 80 | 'suggestion' => 81 | Cpanel::Locale::lh()->maketext( "Security Advisor may include inaccurate results until it can fully read the RPM database. To resolve this, reduce the load on your system and then rebuild the RPM database with the following interface: [output,url,_1,Rebuild RPM Database,_2,_3].", Cpanel::Security::Advisor::Assessors->base_path('scripts/dialog?dialog=rebuildrpmdb'), 'target', '_blank' ), 82 | 'block_notify' => 1, 83 | }; 84 | cmp_assessor( '_Self', [$expected], 'Error displayed' ); 85 | }; 86 | 87 | subtest 'RPM crashes' => sub { 88 | plan tests => 1; 89 | 90 | # Mock for `rpm -qa` for when the program dies abnormally. 91 | my $mock_saferun = Test::MockModule->new('Cpanel::SafeRun::Full'); 92 | $mock_saferun->redefine( 93 | run => sub { 94 | return { 95 | 'did_dump_core' => 0, 96 | 'died_from_signal' => 6, 97 | 'exit_value' => 0, 98 | 'message' => 'Executed /usr/bin/rpm -qa --queryformat %{NAME} %{VERSION}-%{RELEASE}\\n', 99 | 'status' => 1, 100 | 'stderr' => '', 101 | 'stdout' => '', 102 | 'timeout' => undef, 103 | }; 104 | } 105 | ); 106 | my $expected = { 107 | 'type' => $Cpanel::Security::Advisor::ADVISE_BAD, 108 | 'key' => 'RPM_broken', 109 | 'text' => Cpanel::Locale::lh()->maketext('Security Advisor detected RPM database corruption.'), 110 | 'suggestion' => Cpanel::Locale::lh()->maketext( "Security Advisor may include inaccurate results until it can cleanly read the RPM database. To resolve this, rebuild the RPM database with the following interface: [output,url,_1,Rebuild RPM Database,_2,_3].", Cpanel::Security::Advisor::Assessors->base_path('scripts/dialog?dialog=rebuildrpmdb'), 'target', '_blank' ), 111 | 'block_notify' => 1, 112 | }; 113 | cmp_assessor( '_Self', [$expected], 'Error displayed' ); 114 | }; 115 | 116 | subtest 'RPM is incomplete' => sub { 117 | plan tests => 1; 118 | 119 | my $mock_saferun = Test::MockModule->new('Cpanel::SafeRun::Full'); 120 | $mock_saferun->redefine( 121 | run => sub { 122 | return { 123 | 'did_dump_core' => 0, 124 | 'died_from_signal' => 0, 125 | 'exit_value' => 0, 126 | 'message' => 'Executed /usr/bin/rpm -qa --queryformat %{NAME} %{VERSION}-%{RELEASE}\\n', 127 | 'status' => 1, 128 | 'stderr' => '', 129 | 'stdout' => "pkg-1.0-1.el7.x86_64\n" x 500, 130 | 'timeout' => undef, 131 | }; 132 | } 133 | ); 134 | my $expected = { 135 | 'type' => $Cpanel::Security::Advisor::ADVISE_WARN, 136 | 'key' => 'RPM_too_few', 137 | 'text' => Cpanel::Locale::lh()->maketext('The RPM database is smaller than expected.'), 138 | 'suggestion' => Cpanel::Locale::lh()->maketext("Security Advisor may include inaccurate results if the RPM database of packages is incomplete. To resolve this, check the cPanel update logs for RPM issues."), 139 | 'block_notify' => 1, 140 | }; 141 | cmp_assessor( '_Self', [$expected], 'Error displayed' ); 142 | }; 143 | 144 | sub cmp_assessor { 145 | my ( $assessor, $expected_advice, $msg ) = @_; 146 | 147 | local $Test::Builder::Level = $Test::Builder::Level + 1; 148 | 149 | my $object = Test::Assessor->new( assessor => $assessor ); 150 | $object->generate_advice(); 151 | 152 | my $got = $object->get_advice(); 153 | $object->clear_advice(); 154 | 155 | my @expected = map { { module => "Cpanel::Security::Advisor::Assessors::$assessor", function => ignore(), advice => $_ } } @$expected_advice; 156 | 157 | my $ret = cmp_deeply( $got, \@expected, $msg ); 158 | diag explain $got if !$ret; 159 | 160 | return $ret; 161 | } 162 | -------------------------------------------------------------------------------- /t/pkg-Cpanel-Security-Advisor.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright (c) 2020, cPanel, L.L.C. 4 | # All rights reserved. 5 | # http://cpanel.net 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # * Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # * Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # * Neither the name of the owner nor the names of its contributors may 15 | # be used to endorse or promote products derived from this software 16 | # without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | # DISCLAIMED. IN NO EVENT SHALL cPanel, L.L.C. BE LIABLE FOR ANY 22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | use strict; 30 | use warnings; 31 | 32 | use FindBin; 33 | use lib "$FindBin::Bin/lib", "$FindBin::Bin/../pkg"; 34 | 35 | use Test::More; 36 | use Test::Deep; 37 | use Test::Exception; 38 | 39 | use Test::Mock::SecurityAdvisor; 40 | 41 | use Cpanel::Version (); 42 | 43 | use Cpanel::Security::Advisor (); 44 | 45 | plan skip_all => 'Requires cPanel & WHM v86 or later' if Cpanel::Version::compare( Cpanel::Version::getversionnumber(), '<', '11.85' ); 46 | plan tests => 4; 47 | 48 | can_ok( 'Cpanel::Security::Advisor', qw(new generate_advice add_advice) ); 49 | 50 | subtest 'happy path' => sub { 51 | plan tests => 3; 52 | 53 | my $mock = Test::Mock::SecurityAdvisor->new(); 54 | $mock->mock_all(); 55 | 56 | my $advisor = $mock->new_advisor_object(); 57 | lives_ok { $advisor->generate_advice() } 'generate_advice() lives in the happy path.'; 58 | 59 | my $advisor_messages = $mock->get_advisor_messages(); 60 | 61 | my @expected_msgs = map { 62 | { 63 | 'channel' => 'securityadvisor', 64 | 'data' => $_, 65 | } 66 | } ( 67 | { 68 | 'module' => 'Cpanel::Security::Advisor::Assessors::MockAssessor', 69 | 'runtime' => 1, 70 | 'state' => 1, 71 | 'type' => 'mod_load' 72 | }, 73 | { 74 | 'state' => 0, 75 | 'type' => 'scan_run' 76 | }, 77 | { 78 | 'module' => 'Cpanel::Security::Advisor::Assessors::MockAssessor', 79 | 'state' => 0, 80 | 'type' => 'mod_run', 81 | 'version' => '9.99' 82 | }, 83 | { 84 | 'advice' => { 85 | 'key' => 'example_good_advice', 86 | 'suggestion' => 'A suggestion.', 87 | 'text' => 'This is good.', 88 | 'type' => 1 89 | }, 90 | 'function' => 'generate_advice', 91 | 'module' => 'Cpanel::Security::Advisor::Assessors::MockAssessor', 92 | 'type' => 'mod_advice' 93 | }, 94 | { 95 | 'advice' => { 96 | 'key' => 'example_info_advice', 97 | 'suggestion' => 'A suggestion.', 98 | 'text' => 'This is info.', 99 | 'type' => 2 100 | }, 101 | 'function' => 'generate_advice', 102 | 'module' => 'Cpanel::Security::Advisor::Assessors::MockAssessor', 103 | 'type' => 'mod_advice' 104 | }, 105 | { 106 | 'advice' => { 107 | 'key' => 'example_warn_advice', 108 | 'suggestion' => 'A suggestion.', 109 | 'text' => 'This is a warning.', 110 | 'type' => 4 111 | }, 112 | 'function' => 'generate_advice', 113 | 'module' => 'Cpanel::Security::Advisor::Assessors::MockAssessor', 114 | 'type' => 'mod_advice' 115 | }, 116 | { 117 | 'advice' => { 118 | 'key' => 'example_bad_advice', 119 | 'suggestion' => 'A suggestion.', 120 | 'text' => 'This is bad.', 121 | 'type' => 8 122 | }, 123 | 'function' => 'generate_advice', 124 | 'module' => 'Cpanel::Security::Advisor::Assessors::MockAssessor', 125 | 'type' => 'mod_advice' 126 | }, 127 | { 128 | 'message' => '', 129 | 'module' => 'Cpanel::Security::Advisor::Assessors::MockAssessor', 130 | 'state' => 1, 131 | 'type' => 'mod_run', 132 | 'version' => '9.99' 133 | }, 134 | { 135 | 'state' => 1, 136 | 'type' => 'scan_run' 137 | } 138 | ); 139 | 140 | cmp_deeply( $advisor_messages, \@expected_msgs, 'Got expected messages in the happy path.' ) or diag explain $advisor_messages; 141 | 142 | my $logged_warnings = $mock->get_func_calls( 'Cpanel::Logger', 'warn' ); 143 | 144 | cmp_deeply( $logged_warnings, [], 'No logged warnings in the happy path.' ) or diag explain $logged_warnings; 145 | 146 | return; 147 | }; 148 | 149 | subtest 'handle module load exception' => sub { 150 | plan tests => 4; 151 | 152 | my $mock = Test::Mock::SecurityAdvisor->new(); 153 | $mock->mock_all(); 154 | 155 | $mock->set_assessor_module('Cpanel::Security::Advisor::Assessors::MockLoadFail'); 156 | 157 | my $advisor; 158 | lives_ok { $advisor = $mock->new_advisor_object() } 'new() lives when there is a module load exception.'; 159 | lives_ok { $advisor->generate_advice() } 'generate_advice() lives when there is a module load exception.'; 160 | 161 | my @expected_msgs = map { 162 | { 163 | 'channel' => 'securityadvisor', 164 | 'data' => $_, 165 | } 166 | } ( 167 | { 168 | 'message' => re('The system failed to load the module'), 169 | 'module' => 'Cpanel::Security::Advisor::Assessors::MockLoadFail', 170 | 'state' => 0, 171 | 'type' => 'mod_load' 172 | }, 173 | { 174 | 'state' => 0, 175 | 'type' => 'scan_run' 176 | }, 177 | { 178 | 'state' => 1, 179 | 'type' => 'scan_run' 180 | } 181 | ); 182 | 183 | my $advisor_messages = $mock->get_advisor_messages(); 184 | cmp_deeply( $advisor_messages, \@expected_msgs, 'Got expected messages when there is a module new exception.' ) or diag explain $advisor_messages; 185 | 186 | my $logged_warnings = $mock->get_func_calls( 'Cpanel::Logger', 'warn' ); 187 | cmp_deeply( 188 | $logged_warnings, 189 | [ 190 | [ isa('Cpanel::Logger'), re('The system failed to load the module') ], 191 | ], 192 | 'Got expected logged warnings when there is a module load exception.' 193 | ) or diag explain $logged_warnings; 194 | 195 | return; 196 | }; 197 | 198 | subtest 'handle assessor->new() exception' => sub { 199 | plan tests => 4; 200 | 201 | my $mock = Test::Mock::SecurityAdvisor->new(); 202 | $mock->mock_all(); 203 | 204 | $mock->set_assessor_module('Cpanel::Security::Advisor::Assessors::MockNewFail'); 205 | 206 | my $advisor; 207 | lives_ok { $advisor = $mock->new_advisor_object() } 'new() lives when there is an assessor->new() exception.'; 208 | lives_ok { $advisor->generate_advice() } 'generate_advice() lives when there is an assessor->new() exception.'; 209 | 210 | my @expected_msgs = map { 211 | { 212 | 'channel' => 'securityadvisor', 213 | 'data' => $_, 214 | } 215 | } ( 216 | { 217 | 'message' => re('No new for you!'), 218 | 'module' => 'Cpanel::Security::Advisor::Assessors::MockNewFail', 219 | 'state' => 0, 220 | 'type' => 'mod_load' 221 | }, 222 | { 223 | 'state' => 0, 224 | 'type' => 'scan_run' 225 | }, 226 | { 227 | 'state' => 1, 228 | 'type' => 'scan_run' 229 | } 230 | ); 231 | 232 | my $advisor_messages = $mock->get_advisor_messages(); 233 | cmp_deeply( $advisor_messages, \@expected_msgs, 'Got expected messages when there is an assessor->new() exception.' ) or diag explain $advisor_messages; 234 | 235 | my $logged_warnings = $mock->get_func_calls( 'Cpanel::Logger', 'warn' ); 236 | cmp_deeply( 237 | $logged_warnings, 238 | [ 239 | [ isa('Cpanel::Logger'), re('No new for you!') ], 240 | ], 241 | 'Got expected logged warnings when there is an assessor->new() exception.' 242 | ) or diag explain $logged_warnings; 243 | 244 | return; 245 | }; 246 | --------------------------------------------------------------------------------