banalize 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/PARSER.md +43 -0
- data/README.md +168 -0
- data/bin/banalize +120 -0
- data/lib/banalize.rb +64 -0
- data/lib/banalize/errors.rb +43 -0
- data/lib/banalize/exception.rb +19 -0
- data/lib/banalize/files.rb +74 -0
- data/lib/banalize/parser.rb +46 -0
- data/lib/banalize/parser/numbered.rb +102 -0
- data/lib/banalize/policy.rb +95 -0
- data/lib/banalize/policy/severity.rb +35 -0
- data/lib/banalize/registry.rb +219 -0
- data/lib/banalize/runner.rb +101 -0
- data/lib/commands/describe.rb +58 -0
- data/lib/commands/dir.rb +39 -0
- data/lib/commands/file.rb +22 -0
- data/lib/commands/list.rb +25 -0
- data/lib/core_extensions/string.rb +15 -0
- data/lib/helpers/beautify.rb +43 -0
- data/lib/policies/consistent_indents.rb +34 -0
- data/lib/policies/define_path.rb +53 -0
- data/lib/policies/exit_on_error.rb +46 -0
- data/lib/policies/exit_on_unset_variable.rb +58 -0
- data/lib/policies/indentation_style.rb +40 -0
- data/lib/policies/max_line_length.rb +43 -0
- data/lib/policies/minus_n_syntax_check +26 -0
- data/lib/policies/shebang_format.rb +15 -0
- data/lib/policies/trailing_spaces.rb +16 -0
- data/version.txt +1 -0
- metadata +136 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
module Banalize
|
2
|
+
|
3
|
+
##
|
4
|
+
# Run policy check and return result of it.
|
5
|
+
#
|
6
|
+
# @param [String] bash Bash file for analisys
|
7
|
+
#
|
8
|
+
# @param [String, Hash] search Name of a policy to check against or
|
9
|
+
# hash having :severity and/or :policy keys
|
10
|
+
#
|
11
|
+
# @see Policy.search
|
12
|
+
#
|
13
|
+
# @return [Array of Hashes] each element of array is { :name => result }
|
14
|
+
#
|
15
|
+
def self.run bash, search
|
16
|
+
|
17
|
+
run_list = Policy.search search
|
18
|
+
|
19
|
+
if run_list.empty?
|
20
|
+
raise Banalize::Runner::Error, "No policy satisfying criteria: #{search.inspect}"
|
21
|
+
end
|
22
|
+
|
23
|
+
res = { }
|
24
|
+
run_list.each do |policy|
|
25
|
+
res[policy[:policy]] = Banalize::Runner.new(bash, policy).result
|
26
|
+
end
|
27
|
+
res
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Executing policy checks against bash file(s). Class instance is
|
32
|
+
# single bach file to check and single policy. Result of the check
|
33
|
+
# is returned in `@result` instance variable (and attr_accessor).
|
34
|
+
#
|
35
|
+
# Instance attributes
|
36
|
+
# ----------------------
|
37
|
+
# - `@bash` - PATH to bash file
|
38
|
+
#
|
39
|
+
# - `@policy` - [Hash] Policy configuration hash, key :klass contains
|
40
|
+
# name of the class for Ruby policy. If Hash does not have
|
41
|
+
# :klass key, it's not a Ruby, execute it as shell policy.
|
42
|
+
#
|
43
|
+
# - `@result` - Currenty returns only Boolean (true - check OK/false
|
44
|
+
# check fail). This can change later.
|
45
|
+
class Runner
|
46
|
+
|
47
|
+
##
|
48
|
+
# Create new instance of policy check runner and execute
|
49
|
+
# it. Result of the check is returned in @result attribute.
|
50
|
+
#
|
51
|
+
# @param [String] bash
|
52
|
+
# @param [Hash] policy Policy configuration hash.
|
53
|
+
#
|
54
|
+
# @see Runner
|
55
|
+
#
|
56
|
+
def initialize bash,policy
|
57
|
+
@bash, @policy, @result = bash, policy, nil
|
58
|
+
|
59
|
+
if @policy.has_key? :klass
|
60
|
+
ruby
|
61
|
+
else
|
62
|
+
shell
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
attr_accessor :policy, :bash, :result
|
67
|
+
|
68
|
+
##
|
69
|
+
# Execute ruby check
|
70
|
+
#
|
71
|
+
def ruby
|
72
|
+
object = policy[:klass].constantize.new(bash)
|
73
|
+
res = object.run
|
74
|
+
@result = {
|
75
|
+
:status => res ? true : false,
|
76
|
+
:messages => Errors.to_s(object.errors.messages)
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Execute shell check
|
82
|
+
#
|
83
|
+
def shell
|
84
|
+
err = %x{ #{policy[:path]} #{bash} }
|
85
|
+
@result = {
|
86
|
+
:status => ($?.exitstatus == 0),
|
87
|
+
:messages => err
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
# systemu is very slow
|
92
|
+
# def shell_
|
93
|
+
# stat, out, err = systemu "#{policy[:path]} #{bash} "
|
94
|
+
# @result = {
|
95
|
+
# :status => ($?.exitstatus == 0),
|
96
|
+
# :messages => err
|
97
|
+
# }
|
98
|
+
# end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
desc 'Give a description of terms used here'
|
2
|
+
arg_name 'Describe arguments to analyze here'
|
3
|
+
command [:describe,:desc] do |c|
|
4
|
+
|
5
|
+
|
6
|
+
c.desc 'Print help for the specified policy'
|
7
|
+
c.command [:policy, :pol, :p] do |p|
|
8
|
+
p.desc 'Policy name'
|
9
|
+
p.arg_name 'policy_name'
|
10
|
+
|
11
|
+
p.action do |global_options, options, args|
|
12
|
+
raise "Need a policy name to see description" if args.empty?
|
13
|
+
p = args.first.to_sym
|
14
|
+
print "\n#{p}: #{Banalize::Policy.synopsis(p)}\n\n"
|
15
|
+
print Banalize::Policy.description(p) + "\n\n"
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
##
|
22
|
+
# Policies
|
23
|
+
#
|
24
|
+
c.desc 'List and describe available policy groups'
|
25
|
+
c.command [:styles, :style] do |p|
|
26
|
+
p.action do
|
27
|
+
puts <<-EOP
|
28
|
+
Theme Description
|
29
|
+
----- -----------
|
30
|
+
core All policies
|
31
|
+
bugs Policies that that prevent or reveal bugs
|
32
|
+
maintenance Policies that affect the long-term health of the code
|
33
|
+
cosmetic Policies that only have a superficial effect
|
34
|
+
complexity Policies that specifically relate to code complexity
|
35
|
+
security Policies that relate to security issues
|
36
|
+
tests Policies that are specific to test programs
|
37
|
+
|
38
|
+
EOP
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
##
|
45
|
+
# Severity
|
46
|
+
#
|
47
|
+
c.desc 'Print out names of severity levels and their numeric value'
|
48
|
+
c.command [:severity, :sev] do |a|
|
49
|
+
a.action do
|
50
|
+
puts "Name Value"
|
51
|
+
puts "------ ------"
|
52
|
+
print Banalize::Policy::Severity.to_s
|
53
|
+
|
54
|
+
puts "\n\nDefault severity: " << Banalize::Policy::Severity.default.to_s
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/lib/commands/dir.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
desc 'Banalize file(s) from single or multiple directories. Can use wildcards and mix files/directories.'
|
2
|
+
|
3
|
+
arg_name 'dir', :multiple
|
4
|
+
command [:directory, :dir] do |c|
|
5
|
+
|
6
|
+
c.desc "Show all results, by default only failures shown (only for long format)"
|
7
|
+
c.switch [:a,:all]
|
8
|
+
|
9
|
+
c.desc "Short dotted output format"
|
10
|
+
c.switch [:s, :short, :dots]
|
11
|
+
|
12
|
+
c.desc "Recursive scan directories for files"
|
13
|
+
c.switch [:recursive, :recur, :r]
|
14
|
+
|
15
|
+
c.switch [:allow_files, :f], :desc => "Allow use of file paths together with directory paths"
|
16
|
+
|
17
|
+
c.desc "Wildcard for file lists"
|
18
|
+
c.default_value "*"
|
19
|
+
c.flag [:wildcard, :w]
|
20
|
+
|
21
|
+
c.desc "With 'no-' do not show errors, only name of failed check"
|
22
|
+
c.default_value true
|
23
|
+
c.switch [:errors, :err, :e]
|
24
|
+
|
25
|
+
c.action do |global, options, args|
|
26
|
+
files = []
|
27
|
+
args.each { |dir|
|
28
|
+
dir = File.expand_path dir
|
29
|
+
if options[:allow_files] && File.file?(dir)
|
30
|
+
files << dir
|
31
|
+
else
|
32
|
+
files += Dir.glob("#{dir}/#{ options[:r] ? '**/' : ''}#{options[:wildcard]}").select { |x| File.file? x}
|
33
|
+
end
|
34
|
+
}
|
35
|
+
files.each { |file| $res[file] = Banalize.run(file, $search) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
desc 'Run banalize on a single file or multiple files'
|
2
|
+
|
3
|
+
arg_name 'filename', :multiple
|
4
|
+
command [:file, :fl] do |c|
|
5
|
+
|
6
|
+
c.desc "Show all results, by default only failures shown (only for long format)"
|
7
|
+
c.switch [:a,:all]
|
8
|
+
|
9
|
+
c.desc "Short dotted output format"
|
10
|
+
c.switch [:s, :short, :dots]
|
11
|
+
|
12
|
+
c.desc "With 'no-' do not show errors, only name of failed check"
|
13
|
+
c.default_value true
|
14
|
+
c.switch [:errors, :err, :e]
|
15
|
+
|
16
|
+
|
17
|
+
c.action do |global, options, args|
|
18
|
+
args.each { |file| $res[file] = Banalize.run(file, $search) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
desc 'List available policies'
|
2
|
+
|
3
|
+
command [:list, :ls] do |c|
|
4
|
+
|
5
|
+
c.desc 'Only names of policies without description'
|
6
|
+
c.switch [:short, :s]
|
7
|
+
c.default_value true
|
8
|
+
|
9
|
+
c.action do |global, options, args|
|
10
|
+
|
11
|
+
|
12
|
+
printf "\n%40s %s\n\n", "Policy name".color(:bold), "Synopsis, style, severity".color(:bold)
|
13
|
+
|
14
|
+
$policies.each do |x|
|
15
|
+
if options[:short]
|
16
|
+
printf "%40s : %s, %s\n", x[:policy].to_s.color(:yellow), x[:style], x[:severity]
|
17
|
+
else
|
18
|
+
printf "%40s : %s [%s, %s]\n", x[:policy].to_s.color(:yellow), x[:synopsis], x[:style], x[:severity]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
require 'pp'
|
22
|
+
# pp $policies
|
23
|
+
# pp Banalize::Files.policies
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
##
|
2
|
+
# Extend class String with color codes
|
3
|
+
class String
|
4
|
+
def color type
|
5
|
+
return self unless $color
|
6
|
+
color_code = case type
|
7
|
+
when :bold then 1
|
8
|
+
when :white, :file then 37
|
9
|
+
when :error, :red then 31
|
10
|
+
when :green then 32
|
11
|
+
when :policy, :yellow then 33
|
12
|
+
end
|
13
|
+
"\e[#{color_code}m#{self}\e[0m"
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Banalize
|
2
|
+
##
|
3
|
+
# Make ouput text indented and apply colors to it if this is required
|
4
|
+
#
|
5
|
+
# @param [Hash] hash check result hash
|
6
|
+
# @param [String] indent Default indent 4 spaces for each level
|
7
|
+
#
|
8
|
+
def self.beautify hash, indent=' '
|
9
|
+
out = ''
|
10
|
+
l1 = "\n"+indent
|
11
|
+
l2 = "\n" << indent*2
|
12
|
+
l3 = "\n" << indent*2 << "## "
|
13
|
+
|
14
|
+
hash.each do |file,results|
|
15
|
+
out << "\n\n#{file.color(:file)}"
|
16
|
+
|
17
|
+
results.each do |f_or_s, checks|
|
18
|
+
|
19
|
+
|
20
|
+
out << l1 << f_or_s << l1 << '-'*10
|
21
|
+
|
22
|
+
checks.each do |policy, string|
|
23
|
+
out << l1 << policy.to_s.color(f_or_s == 'Fail' ? :red : :yellow)
|
24
|
+
if string
|
25
|
+
lines = case string
|
26
|
+
when String
|
27
|
+
string.split("\n")
|
28
|
+
when Array
|
29
|
+
string
|
30
|
+
end
|
31
|
+
lines.each do |line|
|
32
|
+
line.gsub!(/(line \d+:)(.*)/, '\1' +"#{l3} "+ '\2' )
|
33
|
+
out << l3 << line
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
out
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
banalizer File.basename(__FILE__, '.rb').to_sym do
|
2
|
+
|
3
|
+
synopsis 'All lines must be indented consistenly'
|
4
|
+
severity :harsh
|
5
|
+
style :cosmetic
|
6
|
+
|
7
|
+
description <<EOF
|
8
|
+
|
9
|
+
When indenting the code all leading line indents should use the same
|
10
|
+
character: either all spaces or all tabs, but not mixed.
|
11
|
+
|
12
|
+
EOF
|
13
|
+
|
14
|
+
|
15
|
+
def run
|
16
|
+
ret = true
|
17
|
+
|
18
|
+
has_tabs = code.has?(/^\s*\t/)
|
19
|
+
tab_lines = code.lines
|
20
|
+
|
21
|
+
has_spcs = code.has?(/^\s* /)
|
22
|
+
spc_lines = code.lines
|
23
|
+
|
24
|
+
if has_tabs && has_spcs
|
25
|
+
errors.add "Code indented inconsistently: TABS and SPACES mixed"
|
26
|
+
errors.add " TAB indented: #{tab_lines}"
|
27
|
+
errors.add " SPC indented: #{spc_lines}"
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
return errors.empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
banalizer :explicitly_define_path_variable do
|
2
|
+
|
3
|
+
style :security
|
4
|
+
severity :gentle
|
5
|
+
|
6
|
+
description <<-EOF
|
7
|
+
|
8
|
+
PATH varaible should be defined explicitly in the script. It should *only* list absolute path names and does not have $PATH variable.
|
9
|
+
|
10
|
+
Quote from http://hub.opensolaris.org/bin/view/Community+Group+on/shellstyle#HPathnames
|
11
|
+
|
12
|
+
It is always a good idea to be careful about $PATH settings and
|
13
|
+
pathnames when writing shell scripts. This allows them to function
|
14
|
+
correctly even when the user invoking your script has some strange
|
15
|
+
$PATH set in their environment.
|
16
|
+
|
17
|
+
There are two acceptable ways to do this:
|
18
|
+
|
19
|
+
(1) make *all* command references in your script use explicit pathnames:
|
20
|
+
|
21
|
+
/usr/bin/chown root bar
|
22
|
+
/usr/bin/chgrp sys bar
|
23
|
+
or (2) explicitly reset $PATH in your script:
|
24
|
+
|
25
|
+
PATH=/usr/bin; export PATH
|
26
|
+
|
27
|
+
chown root bar
|
28
|
+
chgrp sys bar
|
29
|
+
|
30
|
+
DO NOT use a mixture of (1) and (2) in the same script. Pick one method and use it consistently.
|
31
|
+
|
32
|
+
|
33
|
+
EOF
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
parser :bash
|
38
|
+
|
39
|
+
def run
|
40
|
+
if code.dont_have?(/^\s*PATH=/)
|
41
|
+
errors.add "PATH variable is not defined in the script"
|
42
|
+
end
|
43
|
+
|
44
|
+
if code.has?(/PATH=.*\$\{?PATH\}?/)
|
45
|
+
errors.add "PATH variable defined by extending existing $PATH variable at: #{code.lines}"
|
46
|
+
errors.add " #{code.search.inspect}"
|
47
|
+
errors.add code
|
48
|
+
end
|
49
|
+
|
50
|
+
return errors.empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
banalizer :exit_on_error do
|
2
|
+
|
3
|
+
synopsis 'Script should be run with -e option or `set -o errexit`'
|
4
|
+
|
5
|
+
description <<EOF
|
6
|
+
Using this option forces bash script to exit on first error.
|
7
|
+
|
8
|
+
man bash:
|
9
|
+
|
10
|
+
-e Exit immediately if a simple command (see SHELL GRAMMAR above)
|
11
|
+
exits with a non-zero status. The shell does not exit if the
|
12
|
+
command that fails is part of the command list immediately
|
13
|
+
following a while or until keyword, part of the test in an if
|
14
|
+
statement, part of a && or || list, or if the command's return
|
15
|
+
value is being inverted via !. A trap on ERR, if set, is exe-
|
16
|
+
cuted before the shell exits.
|
17
|
+
|
18
|
+
|
19
|
+
`set -o errexit` is the same as `set -e`.
|
20
|
+
|
21
|
+
EOF
|
22
|
+
|
23
|
+
severity :gentle
|
24
|
+
|
25
|
+
def run
|
26
|
+
ret = true
|
27
|
+
#
|
28
|
+
# Prohibit using set +e option
|
29
|
+
#
|
30
|
+
errors.add "Setting +e option in shebang: #{shebang}" if (shebang.has? /\+e/)
|
31
|
+
|
32
|
+
if code.has? /set\s+\+e/
|
33
|
+
errors.add "Use of +e to unset -e option. Lines: #{code.lines}"
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
if ( code.dont_have?(/set\s+-e/) ||
|
38
|
+
code.dont_have?(/set\s+-o\s+errexit/)
|
39
|
+
) &&
|
40
|
+
shebang.dont_have?(/\s-e/)
|
41
|
+
errors.add "Can not find option -e or -o errexit anywhere in the script"
|
42
|
+
end
|
43
|
+
return errors.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|