fastlane-plugin-seclane 1.0.0 → 1.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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 115fb009ca9775fe981b9e4d60963a747a9ae061314c890bbb54a250b1d19e66
|
|
4
|
+
data.tar.gz: 384da5ee580f34b77121d56821afd5924cfedf388aa6b169e75db88f62d61799
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 27da8ab0bc8d46f630f8a7dca0bea3d3424266f4b3958b02e9ef38575983bdbe8d7429e96fa8163b9432f6f9eaf953b7dbcf429342eff9ee5c49a716f4010d99
|
|
7
|
+
data.tar.gz: e966b7846584da5c34b17d29963d7c7da9236cd518957ba0a28cf9c750353646738dc132b67c0f9dd7bdeed15386602ad2b5abcdb79c6485a0cee3d912deeab2
|
|
@@ -1,39 +1,126 @@
|
|
|
1
|
-
require "
|
|
1
|
+
require "json"
|
|
2
|
+
require "open-uri"
|
|
3
|
+
require "fileutils"
|
|
2
4
|
|
|
3
5
|
module Fastlane
|
|
4
6
|
module Actions
|
|
5
7
|
class SeclaneScanAction < Action
|
|
8
|
+
SECLANE_VERSION = "1.0.1"
|
|
9
|
+
|
|
6
10
|
def self.run(params)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
11
|
+
binary = ensure_binary(params[:version] || SECLANE_VERSION)
|
|
12
|
+
|
|
13
|
+
args = [
|
|
14
|
+
"scan",
|
|
15
|
+
"--platform", params[:platform],
|
|
16
|
+
"--scan-mode", params[:scan_mode],
|
|
17
|
+
"--base-branch", params[:base_branch],
|
|
18
|
+
"--severity-threshold", params[:severity_threshold],
|
|
19
|
+
"--fail-on-severity", params[:fail_on_severity],
|
|
20
|
+
"--fail-on-count", params[:fail_on_count].to_s,
|
|
21
|
+
"--output-format", params[:output_format],
|
|
22
|
+
"--config-file", params[:config_file],
|
|
23
|
+
"--json-stdout"
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
if params[:custom_patterns] && !params[:custom_patterns].empty?
|
|
27
|
+
args += ["--custom-patterns", params[:custom_patterns].join(",")]
|
|
28
|
+
end
|
|
29
|
+
if params[:exclude_patterns] && !params[:exclude_patterns].empty?
|
|
30
|
+
args += ["--exclude-patterns", params[:exclude_patterns].join(",")]
|
|
31
|
+
end
|
|
32
|
+
if params[:disabled_rules] && !params[:disabled_rules].empty?
|
|
33
|
+
args += ["--disabled-rules", params[:disabled_rules].join(",")]
|
|
34
|
+
end
|
|
35
|
+
if params[:disabled_categories] && !params[:disabled_categories].empty?
|
|
36
|
+
args += ["--disabled-categories", params[:disabled_categories].join(",")]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
report_file = File.join(Dir.tmpdir, "seclane-report.#{params[:output_format]}")
|
|
40
|
+
args += ["--output-file", report_file]
|
|
41
|
+
|
|
42
|
+
cmd = ([binary] + args).shelljoin
|
|
43
|
+
output = `#{cmd} 2>&1`
|
|
44
|
+
exit_code = $?.exitstatus
|
|
45
|
+
|
|
46
|
+
if exit_code == 2
|
|
47
|
+
UI.user_error!("Seclane configuration error: #{output}")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
report = File.exist?(report_file) ? File.read(report_file) : output
|
|
26
51
|
UI.message(report)
|
|
27
52
|
|
|
28
|
-
|
|
29
|
-
|
|
53
|
+
findings_count = 0
|
|
54
|
+
begin
|
|
55
|
+
parsed = JSON.parse(output.strip.split("\n").last)
|
|
56
|
+
findings_count = parsed["findings_count"] || 0
|
|
57
|
+
rescue JSON::ParserError
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
Actions.lane_context[SharedValues::SECLANE_FINDINGS_COUNT] = findings_count
|
|
30
61
|
Actions.lane_context[SharedValues::SECLANE_REPORT] = report
|
|
31
62
|
|
|
32
|
-
if
|
|
33
|
-
UI.user_error!("Seclane found #{
|
|
63
|
+
if exit_code == 1
|
|
64
|
+
UI.user_error!("Seclane found #{findings_count} secret(s) meeting the failure threshold")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
findings_count
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def self.ensure_binary(version)
|
|
71
|
+
unless version.match?(/\A\d+\.\d+\.\d+\z/)
|
|
72
|
+
UI.user_error!("Invalid version format: #{version}. Must be X.Y.Z")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
bin_dir = File.join(Dir.home, ".seclane", "bin")
|
|
76
|
+
FileUtils.mkdir_p(bin_dir)
|
|
77
|
+
|
|
78
|
+
os = RUBY_PLATFORM.include?("darwin") ? "darwin" : "linux"
|
|
79
|
+
|
|
80
|
+
if RUBY_PLATFORM =~ /mingw|mswin|cygwin/
|
|
81
|
+
UI.user_error!("Seclane does not support Windows. Supported platforms: Linux (amd64, arm64), macOS (amd64, arm64).")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
arch = case RUBY_PLATFORM
|
|
85
|
+
when /arm64|aarch64/ then "arm64"
|
|
86
|
+
else "amd64"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
binary = File.join(bin_dir, "seclane-#{version}")
|
|
90
|
+
|
|
91
|
+
unless File.executable?(binary)
|
|
92
|
+
base_url = "https://github.com/tsvetilian-ty/seclane/releases/download/v#{version}"
|
|
93
|
+
bin_url = "#{base_url}/seclane-#{os}-#{arch}"
|
|
94
|
+
checksums_url = "#{base_url}/checksums.txt"
|
|
95
|
+
|
|
96
|
+
UI.message("Downloading seclane v#{version}...")
|
|
97
|
+
URI.open(bin_url) do |remote|
|
|
98
|
+
File.open(binary, "wb") { |f| f.write(remote.read) }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
require "digest"
|
|
102
|
+
actual_sha256 = Digest::SHA256.file(binary).hexdigest
|
|
103
|
+
|
|
104
|
+
begin
|
|
105
|
+
checksums_content = URI.open(checksums_url).read
|
|
106
|
+
expected_line = checksums_content.lines.find { |l| l.include?("seclane-#{os}-#{arch}") }
|
|
107
|
+
if expected_line
|
|
108
|
+
expected_sha256 = expected_line.strip.split(/\s+/).first
|
|
109
|
+
unless actual_sha256 == expected_sha256
|
|
110
|
+
File.delete(binary) if File.exist?(binary)
|
|
111
|
+
UI.user_error!("Checksum verification failed for seclane binary. Expected #{expected_sha256}, got #{actual_sha256}")
|
|
112
|
+
end
|
|
113
|
+
else
|
|
114
|
+
UI.important("Could not find checksum entry for seclane-#{os}-#{arch} in checksums.txt, skipping verification")
|
|
115
|
+
end
|
|
116
|
+
rescue OpenURI::HTTPError => e
|
|
117
|
+
UI.important("Could not download checksums.txt (#{e.message}), skipping verification")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
FileUtils.chmod(0o755, binary)
|
|
34
121
|
end
|
|
35
122
|
|
|
36
|
-
|
|
123
|
+
binary
|
|
37
124
|
end
|
|
38
125
|
|
|
39
126
|
def self.description
|
|
@@ -49,7 +136,7 @@ module Fastlane
|
|
|
49
136
|
end
|
|
50
137
|
|
|
51
138
|
def self.details
|
|
52
|
-
"Uses git diff to detect changed files and scans them for hardcoded secrets, API keys, tokens, private keys, and other sensitive data. Supports Android and
|
|
139
|
+
"Uses git diff to detect changed files and scans them for hardcoded secrets, API keys, tokens, private keys, and other sensitive data. Supports Android, iOS, Flutter, and React Native."
|
|
53
140
|
end
|
|
54
141
|
|
|
55
142
|
def self.available_options
|
|
@@ -59,12 +146,7 @@ module Fastlane
|
|
|
59
146
|
env_name: "SECLANE_PLATFORM",
|
|
60
147
|
description: "Target platform: 'android', 'ios', 'flutter', 'react_native'",
|
|
61
148
|
optional: false,
|
|
62
|
-
type: String
|
|
63
|
-
verify_block: proc do |value|
|
|
64
|
-
unless %w[android ios flutter react_native].include?(value)
|
|
65
|
-
UI.user_error!("Invalid platform: #{value}. Must be 'android', 'ios', 'flutter', or 'react_native'")
|
|
66
|
-
end
|
|
67
|
-
end
|
|
149
|
+
type: String
|
|
68
150
|
),
|
|
69
151
|
FastlaneCore::ConfigItem.new(
|
|
70
152
|
key: :scan_mode,
|
|
@@ -72,12 +154,7 @@ module Fastlane
|
|
|
72
154
|
description: "Scan mode: 'diff' for changed files only, 'full' for all files",
|
|
73
155
|
default_value: "diff",
|
|
74
156
|
optional: true,
|
|
75
|
-
type: String
|
|
76
|
-
verify_block: proc do |value|
|
|
77
|
-
unless %w[diff full].include?(value)
|
|
78
|
-
UI.user_error!("Invalid scan_mode: #{value}. Must be 'diff' or 'full'")
|
|
79
|
-
end
|
|
80
|
-
end
|
|
157
|
+
type: String
|
|
81
158
|
),
|
|
82
159
|
FastlaneCore::ConfigItem.new(
|
|
83
160
|
key: :base_branch,
|
|
@@ -93,12 +170,7 @@ module Fastlane
|
|
|
93
170
|
description: "Minimum severity to report: 'low', 'medium', 'high'",
|
|
94
171
|
default_value: "low",
|
|
95
172
|
optional: true,
|
|
96
|
-
type: String
|
|
97
|
-
verify_block: proc do |value|
|
|
98
|
-
unless %w[low medium high].include?(value)
|
|
99
|
-
UI.user_error!("Invalid severity_threshold: #{value}")
|
|
100
|
-
end
|
|
101
|
-
end
|
|
173
|
+
type: String
|
|
102
174
|
),
|
|
103
175
|
FastlaneCore::ConfigItem.new(
|
|
104
176
|
key: :fail_on_severity,
|
|
@@ -106,17 +178,12 @@ module Fastlane
|
|
|
106
178
|
description: "Fail the lane at this severity: 'low', 'medium', 'high', 'none'",
|
|
107
179
|
default_value: "high",
|
|
108
180
|
optional: true,
|
|
109
|
-
type: String
|
|
110
|
-
verify_block: proc do |value|
|
|
111
|
-
unless %w[low medium high none].include?(value)
|
|
112
|
-
UI.user_error!("Invalid fail_on_severity: #{value}")
|
|
113
|
-
end
|
|
114
|
-
end
|
|
181
|
+
type: String
|
|
115
182
|
),
|
|
116
183
|
FastlaneCore::ConfigItem.new(
|
|
117
184
|
key: :fail_on_count,
|
|
118
185
|
env_name: "SECLANE_FAIL_ON_COUNT",
|
|
119
|
-
description: "Number of findings at fail severity needed to fail the lane
|
|
186
|
+
description: "Number of findings at fail severity needed to fail the lane",
|
|
120
187
|
default_value: 1,
|
|
121
188
|
optional: true,
|
|
122
189
|
type: Integer
|
|
@@ -137,19 +204,6 @@ module Fastlane
|
|
|
137
204
|
optional: true,
|
|
138
205
|
type: Array
|
|
139
206
|
),
|
|
140
|
-
FastlaneCore::ConfigItem.new(
|
|
141
|
-
key: :output_format,
|
|
142
|
-
env_name: "SECLANE_OUTPUT_FORMAT",
|
|
143
|
-
description: "Output format: 'text', 'json', 'junit', 'markdown'",
|
|
144
|
-
default_value: "text",
|
|
145
|
-
optional: true,
|
|
146
|
-
type: String,
|
|
147
|
-
verify_block: proc do |value|
|
|
148
|
-
unless %w[text json junit markdown sonarqube].include?(value)
|
|
149
|
-
UI.user_error!("Invalid output_format: #{value}")
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
),
|
|
153
207
|
FastlaneCore::ConfigItem.new(
|
|
154
208
|
key: :disabled_rules,
|
|
155
209
|
env_name: "SECLANE_DISABLED_RULES",
|
|
@@ -161,11 +215,19 @@ module Fastlane
|
|
|
161
215
|
FastlaneCore::ConfigItem.new(
|
|
162
216
|
key: :disabled_categories,
|
|
163
217
|
env_name: "SECLANE_DISABLED_CATEGORIES",
|
|
164
|
-
description: "List of rule categories to disable
|
|
218
|
+
description: "List of rule categories to disable",
|
|
165
219
|
default_value: [],
|
|
166
220
|
optional: true,
|
|
167
221
|
type: Array
|
|
168
222
|
),
|
|
223
|
+
FastlaneCore::ConfigItem.new(
|
|
224
|
+
key: :output_format,
|
|
225
|
+
env_name: "SECLANE_OUTPUT_FORMAT",
|
|
226
|
+
description: "Output format: 'text', 'json', 'junit', 'markdown', 'sonarqube'",
|
|
227
|
+
default_value: "text",
|
|
228
|
+
optional: true,
|
|
229
|
+
type: String
|
|
230
|
+
),
|
|
169
231
|
FastlaneCore::ConfigItem.new(
|
|
170
232
|
key: :config_file,
|
|
171
233
|
env_name: "SECLANE_CONFIG_FILE",
|
|
@@ -173,6 +235,14 @@ module Fastlane
|
|
|
173
235
|
default_value: ".seclane.yml",
|
|
174
236
|
optional: true,
|
|
175
237
|
type: String
|
|
238
|
+
),
|
|
239
|
+
FastlaneCore::ConfigItem.new(
|
|
240
|
+
key: :version,
|
|
241
|
+
env_name: "SECLANE_VERSION",
|
|
242
|
+
description: "Seclane CLI version to download",
|
|
243
|
+
default_value: SECLANE_VERSION,
|
|
244
|
+
optional: true,
|
|
245
|
+
type: String
|
|
176
246
|
)
|
|
177
247
|
]
|
|
178
248
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fastlane-plugin-seclane
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Seclane by Cluelane
|
|
@@ -10,20 +10,6 @@ bindir: bin
|
|
|
10
10
|
cert_chain: []
|
|
11
11
|
date: 2026-03-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
-
- !ruby/object:Gem::Dependency
|
|
14
|
-
name: seclane-core
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - "~>"
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '1.0'
|
|
20
|
-
type: :runtime
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - "~>"
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '1.0'
|
|
27
13
|
- !ruby/object:Gem::Dependency
|
|
28
14
|
name: bundler
|
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|