Skip to content

Commit 05871e0

Browse files
committed
Add PR scanning job
1 parent 6ef8257 commit 05871e0

File tree

4 files changed

+528
-0
lines changed

4 files changed

+528
-0
lines changed

.github/workflows/pr-security.yml

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: PR Security Scan
2+
3+
on:
4+
pull_request:
5+
branches: [ "main" ]
6+
7+
jobs:
8+
security-scan:
9+
name: Security Analysis with SonarCloud
10+
runs-on: macos-latest
11+
12+
steps:
13+
- uses: actions/checkout@v4
14+
with:
15+
fetch-depth: 0 # Shallow clones should be disabled for better relevancy of analysis
16+
17+
- name: Setup Xcode
18+
uses: maxim-lobanov/setup-xcode@v1
19+
with:
20+
xcode-version: latest-stable
21+
22+
- name: Show environment
23+
run: |
24+
swift --version
25+
xcodebuild -version
26+
27+
- uses: actions/cache/restore@v3
28+
id: cache
29+
with:
30+
path: /Users/runner/Library/Developer/Xcode/DerivedData/**/SourcePackages/checkouts
31+
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
32+
33+
- name: Resolve dependencies
34+
if: steps.cache.outputs.cache-hit != 'true'
35+
run: make resolve-dependencies
36+
37+
- uses: actions/cache@v3
38+
if: steps.cache.outputs.cache-hit != 'true'
39+
with:
40+
path: /Users/runner/Library/Developer/Xcode/DerivedData/**/SourcePackages/checkouts
41+
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
42+
restore-keys: |
43+
${{ runner.os }}-spm-
44+
45+
- name: Install SwiftLint
46+
run: |
47+
brew install swiftlint
48+
49+
- name: Run SwiftLint
50+
run: |
51+
swiftlint --reporter json > swiftlint-results.json || true
52+
swiftlint
53+
54+
- name: Build and Test
55+
run: |
56+
make test-library
57+
58+
- name: Run build-wrapper for SonarCloud
59+
run: |
60+
curl -sSLo build-wrapper-macosx-x86.zip \
61+
https://sonarcloud.io/static/cpp/build-wrapper-macosx-x86.zip
62+
unzip build-wrapper-macosx-x86.zip
63+
./build-wrapper-macosx-x86/build-wrapper-macosx-x86 \
64+
--out-dir bw-output swift build
65+
66+
- name: Run SonarCloud Scan
67+
uses: SonarSource/sonarcloud-github-action@master
68+
env:
69+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
70+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
71+
with:
72+
args: >
73+
-Dsonar.cfamily.build-wrapper-output=bw-output
74+
-Dsonar.pullrequest.key=${{ github.event.number }}
75+
-Dsonar.pullrequest.branch=${{ github.head_ref }}
76+
-Dsonar.pullrequest.base=${{ github.base_ref }}
77+
78+
- name: Upload SwiftLint results
79+
uses: actions/upload-artifact@v3
80+
if: always()
81+
with:
82+
name: swiftlint-results
83+
path: swiftlint-results.json
84+
85+
danger:
86+
name: Danger Checks
87+
runs-on: macos-latest
88+
needs: security-scan
89+
if: always() # Run even if security scan fails
90+
91+
steps:
92+
- uses: actions/checkout@v4
93+
with:
94+
fetch-depth: 0
95+
96+
- name: Setup Xcode
97+
uses: maxim-lobanov/setup-xcode@v1
98+
with:
99+
xcode-version: latest-stable
100+
101+
- name: Download SwiftLint results
102+
uses: actions/download-artifact@v3
103+
with:
104+
name: swiftlint-results
105+
continue-on-error: true
106+
107+
- name: Install Danger and SwiftLint
108+
run: |
109+
# Install Danger via Homebrew
110+
brew install danger/tap/danger-swift
111+
# Install SwiftLint for code quality checks
112+
brew install swiftlint
113+
114+
- name: Run Danger
115+
run: danger-swift ci
116+
env:
117+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.swiftlint.yml

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
# SwiftLint Configuration for Security-focused Swift Project
2+
3+
# Paths to include/exclude
4+
included:
5+
- Sources
6+
- Tests
7+
- Example
8+
9+
excluded:
10+
- .build
11+
- .swiftpm
12+
- Package.swift
13+
- "**/*.generated.swift"
14+
- "**/*.pb.swift"
15+
16+
# Rules Configuration
17+
disabled_rules:
18+
# Formatting rules - handled by swift-format
19+
- trailing_whitespace
20+
- vertical_whitespace
21+
- comma
22+
- colon
23+
- opening_brace
24+
- statement_position
25+
- operator_whitespace
26+
# Content rules handled elsewhere
27+
- todo # We track TODOs in Danger instead
28+
29+
opt_in_rules:
30+
# Security-focused rules
31+
- force_unwrapping
32+
- implicitly_unwrapped_optional
33+
- weak_delegate
34+
- unused_private_declaration
35+
- unused_import
36+
- explicit_init
37+
- explicit_self
38+
- explicit_type_interface
39+
- fatal_error_message
40+
- file_header
41+
- first_where
42+
- force_cast
43+
- force_try
44+
- discouraged_direct_init
45+
- discouraged_optional_boolean
46+
- discouraged_optional_collection
47+
- empty_string
48+
- empty_count
49+
- fallthrough
50+
- identical_operands
51+
- implicit_return
52+
- joined_default_parameter
53+
- let_var_whitespace
54+
- literal_expression_end_indentation
55+
- modifier_order
56+
- nimble_operator
57+
- no_extension_access_modifier
58+
- no_grouping_extension
59+
- object_literal
60+
- operator_usage_whitespace
61+
- overridden_super_call
62+
- override_in_extension
63+
- pattern_matching_keywords
64+
- private_action
65+
- private_outlet
66+
- prohibited_super_call
67+
- reduce_boolean
68+
- redundant_nil_coalescing
69+
- redundant_type_annotation
70+
- required_enum_case
71+
- single_test_class
72+
- sorted_first_last
73+
- sorted_imports
74+
- static_operator
75+
- strict_fileprivate
76+
- switch_case_on_newline
77+
- toggle_bool
78+
- unavailable_function
79+
- unneeded_parentheses_in_closure_argument
80+
- vertical_parameter_alignment_on_call
81+
- vertical_whitespace_closing_braces
82+
- vertical_whitespace_opening_braces
83+
- yoda_condition
84+
85+
# Rule Configuration
86+
force_cast: error
87+
force_try: error
88+
force_unwrapping: error
89+
implicitly_unwrapped_optional: error
90+
91+
# Line length
92+
line_length:
93+
warning: 120
94+
error: 150
95+
ignores_function_declarations: true
96+
ignores_comments: true
97+
ignores_urls: true
98+
99+
# Function body length
100+
function_body_length:
101+
warning: 50
102+
error: 100
103+
104+
# File length
105+
file_length:
106+
warning: 400
107+
error: 1000
108+
109+
# Type body length
110+
type_body_length:
111+
warning: 200
112+
error: 400
113+
114+
# Cyclomatic complexity
115+
cyclomatic_complexity:
116+
warning: 10
117+
error: 20
118+
119+
# Nesting levels
120+
nesting:
121+
type_level:
122+
warning: 3
123+
error: 6
124+
statement_level:
125+
warning: 5
126+
error: 10
127+
128+
# Large tuple
129+
large_tuple:
130+
warning: 3
131+
error: 4
132+
133+
# Type name
134+
type_name:
135+
min_length:
136+
warning: 3
137+
error: 2
138+
max_length:
139+
warning: 40
140+
error: 50
141+
142+
# Identifier name
143+
identifier_name:
144+
min_length:
145+
warning: 2
146+
error: 1
147+
max_length:
148+
warning: 40
149+
error: 60
150+
excluded:
151+
- id
152+
- i
153+
- j
154+
- k
155+
- x
156+
- y
157+
- z
158+
159+
# Custom rules for security
160+
custom_rules:
161+
# Detect potential hardcoded secrets
162+
no_hardcoded_secrets:
163+
name: "No Hardcoded Secrets"
164+
regex: '(api[_-]?key|secret[_-]?key|password|token)\s*[=:]\s*"[^"]{8,}"'
165+
match_kinds:
166+
- string
167+
message: "Potential hardcoded secret detected. Use environment variables or secure storage."
168+
severity: error
169+
170+
# Prevent HTTP usage in production code (except localhost)
171+
no_http_urls:
172+
name: "No HTTP URLs"
173+
regex: '"http://(?!localhost|127\.0\.0\.1)'
174+
match_kinds:
175+
- string
176+
message: "Use HTTPS instead of HTTP for secure communication."
177+
severity: warning
178+
179+
# Detect SQL string concatenation
180+
no_sql_injection:
181+
name: "No SQL Injection"
182+
regex: '(SELECT|INSERT|UPDATE|DELETE).*[\+].*"'
183+
match_kinds:
184+
- string
185+
message: "Potential SQL injection. Use parameterized queries instead of string concatenation."
186+
severity: error
187+
188+
# Force error handling for security-critical operations
189+
force_error_handling:
190+
name: "Force Error Handling"
191+
regex: 'try!\s+(keychain|encrypt|decrypt|authenticate|verify)'
192+
message: "Security operations should use proper error handling instead of force try."
193+
severity: error
194+
195+
reporter: "xcode"

0 commit comments

Comments
 (0)