owasp-glue 0.9.0
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/CHANGES +27 -0
- data/FEATURES +19 -0
- data/README.md +117 -0
- data/bin/glue +67 -0
- data/lib/glue.rb +317 -0
- data/lib/glue/event.rb +14 -0
- data/lib/glue/filters.rb +41 -0
- data/lib/glue/filters/base_filter.rb +19 -0
- data/lib/glue/filters/jira_one_time_filter.rb +57 -0
- data/lib/glue/filters/remove_all_filter.rb +16 -0
- data/lib/glue/filters/zap_consdensing_filter.rb +76 -0
- data/lib/glue/finding.rb +52 -0
- data/lib/glue/mounters.rb +55 -0
- data/lib/glue/mounters/base_mounter.rb +31 -0
- data/lib/glue/mounters/docker_mounter.rb +44 -0
- data/lib/glue/mounters/filesystem_mounter.rb +20 -0
- data/lib/glue/mounters/git_mounter.rb +52 -0
- data/lib/glue/mounters/iso_mounter.rb +42 -0
- data/lib/glue/mounters/url_mounter.rb +28 -0
- data/lib/glue/options.rb +269 -0
- data/lib/glue/reporters.rb +50 -0
- data/lib/glue/reporters/base_reporter.rb +21 -0
- data/lib/glue/reporters/csv_reporter.rb +19 -0
- data/lib/glue/reporters/jira_reporter.rb +59 -0
- data/lib/glue/reporters/json_reporter.rb +20 -0
- data/lib/glue/reporters/text_reporter.rb +19 -0
- data/lib/glue/scanner.rb +28 -0
- data/lib/glue/tasks.rb +124 -0
- data/lib/glue/tasks/av.rb +42 -0
- data/lib/glue/tasks/base_task.rb +80 -0
- data/lib/glue/tasks/brakeman.rb +58 -0
- data/lib/glue/tasks/bundle-audit.rb +95 -0
- data/lib/glue/tasks/checkmarx.rb +60 -0
- data/lib/glue/tasks/dawnscanner.rb +55 -0
- data/lib/glue/tasks/eslint.rb +69 -0
- data/lib/glue/tasks/fim.rb +60 -0
- data/lib/glue/tasks/findsecbugs.rb +90 -0
- data/lib/glue/tasks/npm.rb +58 -0
- data/lib/glue/tasks/nsp.rb +65 -0
- data/lib/glue/tasks/owasp-dep-check.rb +117 -0
- data/lib/glue/tasks/patterns.json +394 -0
- data/lib/glue/tasks/pmd.rb +63 -0
- data/lib/glue/tasks/retirejs.rb +107 -0
- data/lib/glue/tasks/scanjs-eslintrc +106 -0
- data/lib/glue/tasks/scanjs.rb +31 -0
- data/lib/glue/tasks/sfl.rb +67 -0
- data/lib/glue/tasks/snyk.rb +81 -0
- data/lib/glue/tasks/test.rb +47 -0
- data/lib/glue/tasks/zap.rb +99 -0
- data/lib/glue/tracker.rb +47 -0
- data/lib/glue/util.rb +36 -0
- data/lib/glue/version.rb +3 -0
- metadata +294 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'glue/tasks/base_task'
|
2
|
+
require 'glue/util'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
class Glue::Checkmarx < Glue::BaseTask
|
6
|
+
|
7
|
+
# Glue::Tasks.add self
|
8
|
+
include Glue::Util
|
9
|
+
|
10
|
+
def initialize(trigger, tracker)
|
11
|
+
super(trigger, tracker)
|
12
|
+
@name = "Checkmarx"
|
13
|
+
@description = "CxSAST"
|
14
|
+
@stage = :code
|
15
|
+
@labels << "code"
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
rootpath = @trigger.path
|
20
|
+
runsystem(true, "runCxConsole.sh", "scan", "-v",
|
21
|
+
"-CxUser", "#{@tracker.options[:checkmarx_user]}",
|
22
|
+
"-CxPassword", "#{@tracker.options[:checkmarx_password]}",
|
23
|
+
"-CxServer", "#{@tracker.options[:checkmarx_server]}",
|
24
|
+
"-LocationType", "folder",
|
25
|
+
"-LocationPath", "#{rootpath}",
|
26
|
+
"-ProjectName", "#{@tracker.options[:checkmarx_project]}",
|
27
|
+
"-ReportXML", "#{rootpath}checkmarx_results.xml",
|
28
|
+
"-Log", "#{@tracker.options[:checkmarx_log]}"
|
29
|
+
)
|
30
|
+
@results = Nokogiri::XML(File.read("#{rootpath}checkmarx_results.xml")).xpath '//Result'
|
31
|
+
end
|
32
|
+
|
33
|
+
def analyze
|
34
|
+
begin
|
35
|
+
@results.each do |result|
|
36
|
+
description = result.parent.attributes['name'].value.gsub('_', ' ')
|
37
|
+
detail = result.attributes['DeepLink'].value
|
38
|
+
source = { :scanner => @name, :file => result.attributes['FileName'].value, :line => result.attributes['Line'].value.to_i, :code => result.at_xpath('Path/PathNode/Snippet/Line/Code').text }
|
39
|
+
sev = severity(result.parent.attributes['Severity'].value)
|
40
|
+
fprint = fingerprint("#{description}#{source}#{sev}")
|
41
|
+
|
42
|
+
report description, detail, source, sev, fprint
|
43
|
+
end
|
44
|
+
rescue Exception => e
|
45
|
+
Glue.warn e.message
|
46
|
+
Glue.warn e.backtrace
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def supported?
|
51
|
+
supported=runsystem(true, "runCxConsole.sh", "--help")
|
52
|
+
if supported =~ /command not found/
|
53
|
+
Glue.notify "Install CxConsolePlugin"
|
54
|
+
return false
|
55
|
+
else
|
56
|
+
return true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'glue/tasks/base_task'
|
2
|
+
require 'glue/util'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
class Glue::DawnScanner < Glue::BaseTask
|
6
|
+
|
7
|
+
Glue::Tasks.add self
|
8
|
+
include Glue::Util
|
9
|
+
|
10
|
+
def initialize(trigger, tracker)
|
11
|
+
super(trigger, tracker)
|
12
|
+
@name = "DawnScanner"
|
13
|
+
@description = "DawnScanner ruby analyzer"
|
14
|
+
@stage = :code
|
15
|
+
@labels << "code"
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
Dir.chdir("#{@trigger.path}") do
|
20
|
+
@results_file = Tempfile.new(['dawnresults', 'xml'])
|
21
|
+
runsystem(true, "dawn", "-F", "#{@results_file.path}", "-j", ".")
|
22
|
+
@results = JSON.parse(File.read("#{@results_file.path}"))['vulnerabilities']
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def analyze
|
27
|
+
begin
|
28
|
+
@results.each do |result|
|
29
|
+
description = result['name'].gsub('\n',' ')
|
30
|
+
detail = "#{result['message']}\n#{result['remediation']}\n#{result['cve_link']}"
|
31
|
+
source = {:scanner => @name, :file => nil, :line => nil, :code => nil}
|
32
|
+
sev = severity(result['severity'])
|
33
|
+
fprint = fingerprint("#{description}#{detail}#{source}#{sev}")
|
34
|
+
|
35
|
+
report description, detail, source, sev, fprint
|
36
|
+
end
|
37
|
+
rescue Exception => e
|
38
|
+
Glue.warn e.message
|
39
|
+
Glue.warn e.backtrace
|
40
|
+
ensure
|
41
|
+
File.unlink @results_file
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def supported?
|
46
|
+
supported=runsystem(true, "dawn", "--version")
|
47
|
+
if supported =~ /command not found/
|
48
|
+
Glue.notify "Install dawnscanner: 'gem install dawnscanner'"
|
49
|
+
return false
|
50
|
+
else
|
51
|
+
return true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'glue/tasks/base_task'
|
2
|
+
require 'json'
|
3
|
+
require 'glue/util'
|
4
|
+
|
5
|
+
class Glue::ESLint < Glue::BaseTask
|
6
|
+
|
7
|
+
Glue::Tasks.add self
|
8
|
+
include Glue::Util
|
9
|
+
|
10
|
+
def initialize(trigger, tracker)
|
11
|
+
super(trigger,tracker)
|
12
|
+
@name = "ESLint/ScanJS"
|
13
|
+
@description = "Source analysis for JavaScript"
|
14
|
+
@stage = :code
|
15
|
+
@labels << "code" << "javascript"
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
rootpath = @trigger.path
|
20
|
+
currentpath = File.expand_path File.dirname(__FILE__)
|
21
|
+
Glue.debug "ESLint Config Path: #{currentpath}"
|
22
|
+
@result = `eslint -c #{currentpath}/scanjs-eslintrc --no-color --quiet --format json #{rootpath}`
|
23
|
+
end
|
24
|
+
|
25
|
+
def analyze
|
26
|
+
# puts @result
|
27
|
+
begin
|
28
|
+
parsed = JSON.parse(@result)
|
29
|
+
parsed.each do |result|
|
30
|
+
findings = {}
|
31
|
+
prints = []
|
32
|
+
messages = []
|
33
|
+
result['messages'].each do |msg|
|
34
|
+
message = msg['message']
|
35
|
+
findings[message] = {} if findings[message].nil?
|
36
|
+
findings[message][:detail] = msg['ruleId']
|
37
|
+
if messages.include?(message)
|
38
|
+
findings[message][:source] = "#{findings[message][:source]},#{msg['line']}" unless findings[message][:source].include?(",#{msg['line']}")
|
39
|
+
else
|
40
|
+
findings[message][:source] = "#{result['filePath']} Line: #{msg['line']}"
|
41
|
+
messages << message
|
42
|
+
end
|
43
|
+
findings[message][:severity] = severity(msg['severity'].to_s)
|
44
|
+
end
|
45
|
+
findings.each do |key, value|
|
46
|
+
print = fingerprint("#{key}#{value[:detail]}#{value[:source]}#{value[:sev]}")
|
47
|
+
unless prints.include?(print)
|
48
|
+
prints << print
|
49
|
+
report key, value[:detail], value[:source], value[:severity], print
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
rescue Exception => e
|
54
|
+
Glue.warn e.message
|
55
|
+
Glue.warn e.backtrace
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def supported?
|
60
|
+
supported=runsystem(true, "eslint", "-c", "~/.scanjs-eslintrc")
|
61
|
+
if supported =~ /command not found/
|
62
|
+
Glue.notify "Install eslint and the scanjs .eslintrc"
|
63
|
+
return false
|
64
|
+
else
|
65
|
+
return true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# https://github.com/jessek/hashdeep/releases/tag/release-4.4
|
2
|
+
|
3
|
+
require 'glue/tasks/base_task'
|
4
|
+
require 'open3'
|
5
|
+
|
6
|
+
class Glue::FIM < Glue::BaseTask
|
7
|
+
|
8
|
+
Glue::Tasks.add self
|
9
|
+
|
10
|
+
def initialize(trigger, tracker)
|
11
|
+
super(trigger,tracker)
|
12
|
+
@name = "FIM"
|
13
|
+
@description = "File integrity monitor"
|
14
|
+
@stage = :file
|
15
|
+
@result = ''
|
16
|
+
@labels << "filesystem"
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
rootpath = @trigger.path
|
21
|
+
if File.exists?("/area81/tmp/#{rootpath}/filehash")
|
22
|
+
Glue.notify "File Hashes found, comparing to file system"
|
23
|
+
cmd="hashdeep -j99 -r -a -vv -k /area81/tmp/#{rootpath}/filehash #{rootpath}"
|
24
|
+
|
25
|
+
# Ugly stdout parsing
|
26
|
+
r=/(.*): No match/
|
27
|
+
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
|
28
|
+
while line = stdout.gets
|
29
|
+
if line.match r
|
30
|
+
@result << line
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
else
|
35
|
+
Glue.notify "No existing baseline - generating initial hashes"
|
36
|
+
cmd="mkdir -p /area81/tmp/#{rootpath}; hashdeep -j99 -r #{rootpath} > /area81/tmp/#{rootpath}/filehash"
|
37
|
+
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
|
38
|
+
while line = stdout.gets
|
39
|
+
puts "."
|
40
|
+
end
|
41
|
+
end
|
42
|
+
@result = ''
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def analyze
|
47
|
+
list = @result.split(/\n/)
|
48
|
+
list.each do |v|
|
49
|
+
# v.slice! installdir
|
50
|
+
Glue.notify v
|
51
|
+
report "File changed.", v, @name, :low
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def supported?
|
56
|
+
# In future, verify tool is available.
|
57
|
+
return true
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'glue/tasks/base_task'
|
2
|
+
require 'glue/util'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'mkmf'
|
6
|
+
|
7
|
+
MakeMakefile::Logging.instance_variable_set(:@logfile, File::NULL)
|
8
|
+
|
9
|
+
class Glue::FindSecurityBugs < Glue::BaseTask
|
10
|
+
|
11
|
+
Glue::Tasks.add self
|
12
|
+
include Glue::Util
|
13
|
+
|
14
|
+
def initialize(trigger, tracker)
|
15
|
+
super(trigger, tracker)
|
16
|
+
@name = "FindSecurityBugs"
|
17
|
+
@description = "FindSecurityBugs plugin for FindBugs"
|
18
|
+
@stage = :code
|
19
|
+
@labels << "code"
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
@results_file = Tempfile.new(['findsecbugs','xml'])
|
24
|
+
|
25
|
+
unless File.exist?("#{@trigger.path}/.git/config")
|
26
|
+
Dir.chdir(@trigger.path) do
|
27
|
+
runsystem(true, "git", "init")
|
28
|
+
runsystem(true, "git", "add", "*")
|
29
|
+
runsystem(true, "git", "commit", "-am", "fake commit for mvn compile")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
directories_with?('pom.xml').each do |dir|
|
34
|
+
Dir.chdir(dir) do
|
35
|
+
runsystem(true, "mvn", "compile", "-fn")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
Dir.chdir(@tracker.options[:findsecbugs_path]) do
|
40
|
+
runsystem(true, "/bin/sh", "#{@tracker.options[:findsecbugs_path]}/findsecbugs.sh", "-effort:max", "-quiet", "-xml:withMessages", "-output", "#{@results_file.path}", "#{@trigger.path}")
|
41
|
+
@results = Nokogiri::XML(File.read(@results_file)).xpath '//BugInstance'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def analyze
|
46
|
+
begin
|
47
|
+
@results.each do |result|
|
48
|
+
description = result.xpath('ShortMessage').text
|
49
|
+
bug_type = result.attributes['type'].value
|
50
|
+
detail = "Class: #{result.at_xpath('Method').attributes['classname'].value}, Method: #{result.at_xpath('Method').attributes['name'].value}\n#{result.xpath('LongMessage').text}\nhttps://find-sec-bugs.github.io/bugs.htm##{bug_type}"
|
51
|
+
|
52
|
+
file = result.at_xpath('SourceLine').attributes['sourcepath'].value
|
53
|
+
trigger_path = Pathname.new(@trigger.path)
|
54
|
+
real_path = nil
|
55
|
+
trigger_path.find {|path| real_path = path if path.fnmatch "*/#{file}"}
|
56
|
+
file = real_path.relative_path_from(trigger_path).to_s unless real_path.nil?
|
57
|
+
|
58
|
+
line = result.at_xpath('SourceLine[@primary="true"]').attributes['start'].value
|
59
|
+
code = "#{result.at_xpath('String').attributes['value'].value}"
|
60
|
+
source = {:scanner => @name, :file => file, :line => line, :code => code}
|
61
|
+
sev = result.attributes['priority'].value
|
62
|
+
fprint = fingerprint("#{description}#{detail}#{source}")
|
63
|
+
|
64
|
+
report description, detail, source, sev, fprint
|
65
|
+
end
|
66
|
+
rescue Exception => e
|
67
|
+
Glue.warn e.message
|
68
|
+
Glue.warn e.backtrace
|
69
|
+
ensure
|
70
|
+
File.unlink @results_file
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def supported?
|
75
|
+
unless find_executable0('mvn') and File.exist?("#{@trigger.path}/pom.xml")
|
76
|
+
Glue.notify "FindSecurityBugs support requires maven and pom.xml"
|
77
|
+
Glue.notify "Please install maven somewhere in your PATH and include a valid pom.xml in the project root"
|
78
|
+
return false
|
79
|
+
end
|
80
|
+
|
81
|
+
unless @tracker.options.has_key?(:findsecbugs_path) and File.exist?("#{@tracker.options[:findsecbugs_path]}/findsecbugs.sh")
|
82
|
+
Glue.notify "#{@tracker.options[:findsecbugs_path]}"
|
83
|
+
Glue.notify "Download and unpack the latest findsecbugs-cli release: https://github.com/find-sec-bugs/find-sec-bugs/releases"
|
84
|
+
return false
|
85
|
+
else
|
86
|
+
return true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'glue/tasks/base_task'
|
2
|
+
require 'glue/util'
|
3
|
+
require 'find'
|
4
|
+
require 'pry'
|
5
|
+
|
6
|
+
class Glue::Npm < Glue::BaseTask
|
7
|
+
|
8
|
+
Glue::Tasks.add self
|
9
|
+
include Glue::Util
|
10
|
+
|
11
|
+
def initialize(trigger, tracker)
|
12
|
+
super(trigger, tracker)
|
13
|
+
@name = "NPM"
|
14
|
+
@description = "Node Package Manager"
|
15
|
+
@stage = :file
|
16
|
+
@labels << "file" << "javascript"
|
17
|
+
@results = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
exclude_dirs = ['node_modules','bower_components']
|
22
|
+
exclude_dirs = exclude_dirs.concat(@tracker.options[:exclude_dirs]).uniq if @tracker.options[:exclude_dirs]
|
23
|
+
directories_with?('package.json', exclude_dirs).each do |dir|
|
24
|
+
Glue.notify "#{@name} scanning: #{dir}"
|
25
|
+
Dir.chdir(dir) do
|
26
|
+
if @tracker.options.has_key?(:npm_registry)
|
27
|
+
registry = "--registry #{@tracker.options[:npm_registry]}"
|
28
|
+
else
|
29
|
+
registry = nil
|
30
|
+
end
|
31
|
+
@command = "npm install -q --ignore-scripts #{registry}"
|
32
|
+
@results << runsystem(true, @command)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def analyze
|
38
|
+
begin
|
39
|
+
if @results.include? false
|
40
|
+
Glue.warn 'Error installing javascript dependencies with #{@command}'
|
41
|
+
end
|
42
|
+
rescue Exception => e
|
43
|
+
Glue.warn e.message
|
44
|
+
Glue.warn e.backtrace
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def supported?
|
49
|
+
supported = find_executable0('npm')
|
50
|
+
unless supported
|
51
|
+
Glue.notify "Install npm: https://nodejs.org/en/download/"
|
52
|
+
return false
|
53
|
+
else
|
54
|
+
return true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'glue/tasks/base_task'
|
2
|
+
require 'glue/util'
|
3
|
+
|
4
|
+
class Glue::NodeSecurityProject < Glue::BaseTask
|
5
|
+
|
6
|
+
Glue::Tasks.add self
|
7
|
+
include Glue::Util
|
8
|
+
|
9
|
+
def initialize(trigger, tracker)
|
10
|
+
super(trigger, tracker)
|
11
|
+
@name = "NodeSecurityProject"
|
12
|
+
@description = "Node Security Project"
|
13
|
+
@stage = :code
|
14
|
+
@labels << "code"
|
15
|
+
@results = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
exclude_dirs = ['node_modules','bower_components']
|
20
|
+
exclude_dirs = exclude_dirs.concat(@tracker.options[:exclude_dirs]).uniq if @tracker.options[:exclude_dirs]
|
21
|
+
directories_with?('package.json', exclude_dirs).each do |dir|
|
22
|
+
Glue.notify "#{@name} scanning: #{dir}"
|
23
|
+
Dir.chdir(dir) do
|
24
|
+
res = runsystem(true, "nsp", "check", "--output", "json")
|
25
|
+
@results << JSON.parse(res)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def analyze
|
31
|
+
begin
|
32
|
+
@results.each do |dir_result|
|
33
|
+
# This block iterates through each package name found and selects the unique nsp advisories
|
34
|
+
# regardless of version, and builds a Glue finding hash for each unique package/advisory combo.
|
35
|
+
dir_result.uniq {|finding| finding['module']}.each do |package|
|
36
|
+
dir_result.select {|f| f['module'] == package['module']}.uniq {|m| m['advisory']}.each do |unique_finding|
|
37
|
+
description = "#{unique_finding['module']} - #{unique_finding['title']}"
|
38
|
+
detail = "Upgrade to versions: #{unique_finding['patched_versions']}\n#{unique_finding['advisory']}"
|
39
|
+
source = {
|
40
|
+
:scanner => 'NodeSecurityProject',
|
41
|
+
:file => "#{unique_finding['module']} - #{unique_finding['vulnerable_versions']}",
|
42
|
+
:line => nil,
|
43
|
+
:code => nil
|
44
|
+
}
|
45
|
+
report description, detail, source, 'medium', fingerprint("#{description}#{detail}#{source}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
rescue Exception => e
|
50
|
+
Glue.warn e.message
|
51
|
+
Glue.warn e.backtrace
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def supported?
|
56
|
+
supported=runsystem(true, "nsp", "--version")
|
57
|
+
if supported =~ /command not found/
|
58
|
+
Glue.notify "Install nodesecurity: 'npm install -g nsp'"
|
59
|
+
return false
|
60
|
+
else
|
61
|
+
return true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|