diffsense 2.2.12__py3-none-any.whl
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.
- adapters/__init__.py +0 -0
- adapters/base.py +27 -0
- adapters/github_adapter.py +164 -0
- adapters/gitlab_adapter.py +207 -0
- adapters/local_adapter.py +136 -0
- banner.py +71 -0
- cli.py +606 -0
- config/__init__.py +1 -0
- config/rules.yaml +371 -0
- core/__init__.py +235 -0
- core/ast_detector.py +853 -0
- core/change.py +46 -0
- core/composer.py +93 -0
- core/evaluator.py +15 -0
- core/ignore_manager.py +71 -0
- core/knowledge.py +77 -0
- core/parser.py +181 -0
- core/parser_manager.py +104 -0
- core/quality_manager.py +117 -0
- core/renderer.py +197 -0
- core/rule_base.py +98 -0
- core/rule_runtime.py +103 -0
- core/rules.py +718 -0
- core/run_config.py +85 -0
- core/semantic_diff.py +359 -0
- core/signal_model.py +21 -0
- core/signals_registry.py +62 -0
- diffsense-2.2.12.dist-info/METADATA +18 -0
- diffsense-2.2.12.dist-info/RECORD +58 -0
- diffsense-2.2.12.dist-info/WHEEL +5 -0
- diffsense-2.2.12.dist-info/entry_points.txt +3 -0
- diffsense-2.2.12.dist-info/licenses/LICENSE +176 -0
- diffsense-2.2.12.dist-info/top_level.txt +11 -0
- diffsense_mcp/__init__.py +1 -0
- diffsense_mcp/launcher.py +28 -0
- diffsense_mcp/server.py +687 -0
- governance/lifecycle.py +54 -0
- main.py +318 -0
- rules/__init__.py +246 -0
- rules/api_compatibility.py +372 -0
- rules/collection_handling.py +349 -0
- rules/concurrency.py +194 -0
- rules/concurrency_adapter.py +250 -0
- rules/cross_language_adapter.py +444 -0
- rules/exception_handling.py +320 -0
- rules/go_rules.py +401 -0
- rules/null_safety.py +301 -0
- rules/resource_management.py +222 -0
- rules/yaml_adapter.py +195 -0
- run_audit.py +478 -0
- sdk/cpp_adapter.py +238 -0
- sdk/go_adapter.py +199 -0
- sdk/java_adapter.py +199 -0
- sdk/javascript_adapter.py +229 -0
- sdk/language_adapter.py +313 -0
- sdk/python_adapter.py +195 -0
- sdk/rule.py +63 -0
- sdk/signal.py +14 -0
sdk/cpp_adapter.py
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""
|
|
2
|
+
C++ Language Adapter for DiffSense
|
|
3
|
+
|
|
4
|
+
Provides C++-specific patterns and constructs for rule development.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from typing import List, Set, Pattern, Dict
|
|
9
|
+
from .language_adapter import LanguageAdapter
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CppAdapter(LanguageAdapter):
|
|
13
|
+
"""
|
|
14
|
+
Language adapter for C++.
|
|
15
|
+
|
|
16
|
+
Provides C++-specific patterns for resource management, memory safety,
|
|
17
|
+
concurrency, and other language constructs.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
super().__init__("cpp")
|
|
22
|
+
self._compile_patterns()
|
|
23
|
+
|
|
24
|
+
def _compile_patterns(self):
|
|
25
|
+
"""Pre-compile commonly used patterns."""
|
|
26
|
+
# Thread Safety / Concurrency Patterns
|
|
27
|
+
self._lock_patterns = [
|
|
28
|
+
re.compile(r'std::lock_guard\s*<'),
|
|
29
|
+
re.compile(r'std::unique_lock\s*<'),
|
|
30
|
+
re.compile(r'std::shared_lock\s*<'),
|
|
31
|
+
re.compile(r'pthread_mutex_lock\s*\('),
|
|
32
|
+
re.compile(r'boost::mutex'),
|
|
33
|
+
re.compile(r'std::mutex'),
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
self._unlock_patterns = [
|
|
37
|
+
re.compile(r'std::lock_guard.*release\('),
|
|
38
|
+
re.compile(r'std::unique_lock.*release\('),
|
|
39
|
+
re.compile(r'pthread_mutex_unlock\s*\('),
|
|
40
|
+
re.compile(r'\.unlock\(\)'),
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
self._volatile_patterns = [
|
|
44
|
+
re.compile(r'std::atomic<'),
|
|
45
|
+
re.compile(r'volatile\s+\w+'),
|
|
46
|
+
re.compile(r'atomic_\w+\s*\('),
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
# Resource Management Patterns (RAII)
|
|
50
|
+
self._resource_creation = [
|
|
51
|
+
re.compile(r'new\s+\w+'),
|
|
52
|
+
re.compile(r'fopen\s*\('),
|
|
53
|
+
re.compile(r'ifstream\s*\('),
|
|
54
|
+
re.compile(r'ofstream\s*\('),
|
|
55
|
+
re.compile(r'socket\s*\('),
|
|
56
|
+
re.compile(r'connect\s*\('),
|
|
57
|
+
re.compile(r'std::make_unique'),
|
|
58
|
+
re.compile(r'std::make_shared'),
|
|
59
|
+
re.compile(r'std::move'),
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
# Error Handling Patterns
|
|
63
|
+
self._error_check = [
|
|
64
|
+
re.compile(r'catch\s*\(\s*\w+.*\)\s*\{'),
|
|
65
|
+
re.compile(r'if\s*\(\s*errno'),
|
|
66
|
+
re.compile(r'if\s*\(\s*!'),
|
|
67
|
+
re.compile(r'if\s*\(\s*nullptr'),
|
|
68
|
+
re.compile(r'if\s*\(\s*\w+\s*==\s*NULL'),
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
self._error_ignore = [
|
|
72
|
+
re.compile(r'catch\s*\([^)]*\)\s*\{\s*\}'),
|
|
73
|
+
re.compile(r'catch\s*\(\s*\w+\s*&\s*\w+\s*\)\s*\{\s*\}'),
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
# Memory Management Patterns
|
|
77
|
+
self._memory_patterns = [
|
|
78
|
+
re.compile(r'delete\s+\w+'),
|
|
79
|
+
re.compile(r'delete\[\]\s+\w+'),
|
|
80
|
+
re.compile(r'free\s*\('),
|
|
81
|
+
re.compile(r'malloc\s*\('),
|
|
82
|
+
re.compile(r'realloc\s*\('),
|
|
83
|
+
re.compile(r'std::unique_ptr'),
|
|
84
|
+
re.compile(r'std::shared_ptr'),
|
|
85
|
+
re.compile(r'std::weak_ptr'),
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
# Security Patterns
|
|
89
|
+
self._security = {
|
|
90
|
+
'buffer_overflow': [
|
|
91
|
+
re.compile(r'strcpy\s*\('),
|
|
92
|
+
re.compile(r'strcat\s*\('),
|
|
93
|
+
re.compile(r'sprintf\s*\('),
|
|
94
|
+
re.compile(r'gets\s*\('),
|
|
95
|
+
re.compile(r'scanf\s*\([^)]*%s'),
|
|
96
|
+
],
|
|
97
|
+
'hardcoded_secret': [
|
|
98
|
+
re.compile(r'password\s*=\s*["\'][^"\']+["\']'),
|
|
99
|
+
re.compile(r'api[_-]?key\s*=\s*["\'][^"\']+["\']'),
|
|
100
|
+
re.compile(r'secret\s*=\s*["\'][^"\']+["\']'),
|
|
101
|
+
re.compile(r'token\s*=\s*["\'][^"\']+["\']'),
|
|
102
|
+
],
|
|
103
|
+
'command_injection': [
|
|
104
|
+
re.compile(r'system\s*\('),
|
|
105
|
+
re.compile(r'exec\s*\('),
|
|
106
|
+
re.compile(r'popen\s*\('),
|
|
107
|
+
re.compile(r'shell_exec\s*\('),
|
|
108
|
+
],
|
|
109
|
+
'format_string': [
|
|
110
|
+
re.compile(r'printf\s*\([^,]+%s'),
|
|
111
|
+
re.compile(r'sprintf\s*\([^,]+%s'),
|
|
112
|
+
],
|
|
113
|
+
'unsafe_pointer': [
|
|
114
|
+
re.compile(r'new\s+\w+\s*\(\s*\)'),
|
|
115
|
+
re.compile(r'\*\s*\w+\s*='),
|
|
116
|
+
re.compile(r'&(?!\w)'),
|
|
117
|
+
],
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# ==================== Thread Safety ====================
|
|
121
|
+
|
|
122
|
+
def get_thread_safe_types(self) -> Set[str]:
|
|
123
|
+
return {
|
|
124
|
+
'std::mutex', 'std::recursive_mutex', 'std::shared_mutex',
|
|
125
|
+
'std::lock_guard', 'std::unique_lock', 'std::shared_lock',
|
|
126
|
+
'std::atomic', 'std::atomic_flag',
|
|
127
|
+
'pthread_mutex_t', 'pthread_rwlock_t',
|
|
128
|
+
'concurrent_queue', 'thread',
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
def get_unsafe_types(self) -> Set[str]:
|
|
132
|
+
return {
|
|
133
|
+
'raw pointer', 'char*', 'void*', 'int*',
|
|
134
|
+
'std::vector', 'std::list', 'std::map', 'std::set',
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
def get_lock_patterns(self) -> List[Pattern]:
|
|
138
|
+
return self._lock_patterns
|
|
139
|
+
|
|
140
|
+
def get_unlock_patterns(self) -> List[Pattern]:
|
|
141
|
+
return self._unlock_patterns
|
|
142
|
+
|
|
143
|
+
def get_volatile_patterns(self) -> List[Pattern]:
|
|
144
|
+
return self._volatile_patterns
|
|
145
|
+
|
|
146
|
+
# ==================== Resource Management ====================
|
|
147
|
+
|
|
148
|
+
def get_cleanup_keywords(self) -> Set[str]:
|
|
149
|
+
return {
|
|
150
|
+
'delete', 'delete[]', 'free', 'close',
|
|
151
|
+
'fclose', 'shutdown', 'disconnect',
|
|
152
|
+
'std::unique_ptr', 'std::shared_ptr',
|
|
153
|
+
'RAII', 'destructor', '~'
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
def get_resource_creation_patterns(self) -> List[Pattern]:
|
|
157
|
+
return self._resource_creation
|
|
158
|
+
|
|
159
|
+
# ==================== Error Handling ====================
|
|
160
|
+
|
|
161
|
+
def get_error_types(self) -> Set[str]:
|
|
162
|
+
return {
|
|
163
|
+
'std::exception', 'std::runtime_error', 'std::logic_error',
|
|
164
|
+
'std::bad_alloc', 'std::bad_cast', 'std::out_of_range',
|
|
165
|
+
'std::invalid_argument', 'errno', 'NULL', 'nullptr'
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
def get_error_check_patterns(self) -> List[Pattern]:
|
|
169
|
+
return self._error_check
|
|
170
|
+
|
|
171
|
+
def get_error_ignore_patterns(self) -> List[Pattern]:
|
|
172
|
+
return self._error_ignore
|
|
173
|
+
|
|
174
|
+
# ==================== Null Safety ====================
|
|
175
|
+
|
|
176
|
+
def get_null_checks(self) -> Set[str]:
|
|
177
|
+
return {
|
|
178
|
+
'== nullptr', '!= nullptr', '!ptr',
|
|
179
|
+
'if (ptr)', 'if (!ptr)', 'if (ptr == nullptr)',
|
|
180
|
+
'NULL', 'nullptr', 'assert()'
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
def get_null_pointer_types(self) -> Set[str]:
|
|
184
|
+
return {
|
|
185
|
+
'nullptr', 'NULL', 'void*', 'char*', 'T*'
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
# ==================== Concurrency Primitives ====================
|
|
189
|
+
|
|
190
|
+
def get_concurrency_primitives(self) -> Set[str]:
|
|
191
|
+
return {
|
|
192
|
+
'std::thread', 'std::mutex', 'std::lock_guard',
|
|
193
|
+
'std::atomic', 'std::future', 'std::promise',
|
|
194
|
+
'pthread', 'pthread_mutex', 'pthread_cond',
|
|
195
|
+
'boost::thread', 'boost::mutex'
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
def get_thread_creation_patterns(self) -> List[Pattern]:
|
|
199
|
+
return [
|
|
200
|
+
re.compile(r'std::thread\s*\('),
|
|
201
|
+
re.compile(r'pthread_create\s*\('),
|
|
202
|
+
re.compile(r'boost::thread\s*\('),
|
|
203
|
+
]
|
|
204
|
+
|
|
205
|
+
# ==================== Security ====================
|
|
206
|
+
|
|
207
|
+
def get_dangerous_patterns(self) -> Dict[str, List[Pattern]]:
|
|
208
|
+
return self._security
|
|
209
|
+
|
|
210
|
+
# ==================== Additional C++ specific ====================
|
|
211
|
+
|
|
212
|
+
def get_memory_management_patterns(self) -> List[Pattern]:
|
|
213
|
+
"""Get patterns for memory management (smart vs raw pointers)."""
|
|
214
|
+
return self._memory_patterns
|
|
215
|
+
|
|
216
|
+
def get_raii_patterns(self) -> List[Pattern]:
|
|
217
|
+
"""Get patterns for RAII (Resource Acquisition Is Initialization)."""
|
|
218
|
+
return [
|
|
219
|
+
re.compile(r'class\s+\w+.*\{'),
|
|
220
|
+
re.compile(r'~?\w+\s*\(\s*\).*\{'),
|
|
221
|
+
re.compile(r'std::unique_ptr'),
|
|
222
|
+
re.compile(r'std::shared_ptr'),
|
|
223
|
+
]
|
|
224
|
+
|
|
225
|
+
def get_smart_pointer_patterns(self) -> List[Pattern]:
|
|
226
|
+
"""Get patterns for smart pointers (preferred over raw)."""
|
|
227
|
+
return [
|
|
228
|
+
re.compile(r'std::unique_ptr'),
|
|
229
|
+
re.compile(r'std::shared_ptr'),
|
|
230
|
+
re.compile(r'std::weak_ptr'),
|
|
231
|
+
re.compile(r'std::make_unique'),
|
|
232
|
+
re.compile(r'std::make_shared'),
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def get_adapter():
|
|
237
|
+
"""Get C++ adapter instance."""
|
|
238
|
+
return CppAdapter()
|
sdk/go_adapter.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Go Language Adapter for DiffSense
|
|
3
|
+
|
|
4
|
+
Provides Go-specific patterns and constructs for rule development.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from typing import List, Set, Pattern, Dict
|
|
9
|
+
from .language_adapter import LanguageAdapter
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class GoAdapter(LanguageAdapter):
|
|
13
|
+
"""
|
|
14
|
+
Language adapter for Go.
|
|
15
|
+
|
|
16
|
+
Provides Go-specific patterns for concurrency, channel management,
|
|
17
|
+
error handling, and other language constructs.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
super().__init__("go")
|
|
22
|
+
self._compile_patterns()
|
|
23
|
+
|
|
24
|
+
def _compile_patterns(self):
|
|
25
|
+
"""Pre-compile commonly used patterns."""
|
|
26
|
+
# Thread Safety / Concurrency Patterns
|
|
27
|
+
self._lock_patterns = [
|
|
28
|
+
re.compile(r'\.Lock\(\)'),
|
|
29
|
+
re.compile(r'\.RLock\(\)'),
|
|
30
|
+
re.compile(r'sync\.Mutex'),
|
|
31
|
+
re.compile(r'sync\.RWMutex'),
|
|
32
|
+
re.compile(r'sync\.Once\.Do'),
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
self._unlock_patterns = [
|
|
36
|
+
re.compile(r'\.Unlock\(\)'),
|
|
37
|
+
re.compile(r'\.RUnlock\(\)'),
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
self._volatile_patterns = [
|
|
41
|
+
re.compile(r'atomic\.(?:Load|Store|Add|Swap|CompareAndSwap)'),
|
|
42
|
+
re.compile(r'sync\/atomic\.'),
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
# Resource Management Patterns
|
|
46
|
+
self._resource_creation = [
|
|
47
|
+
re.compile(r'make\('),
|
|
48
|
+
re.compile(r'os\.Open\('),
|
|
49
|
+
re.compile(r'os\.Create\('),
|
|
50
|
+
re.compile(r'http\.(?:ListenAndServe|NewRequest)'),
|
|
51
|
+
re.compile(r'database\.Open\('),
|
|
52
|
+
re.compile(r'grpc\.Dial'),
|
|
53
|
+
re.compile(r'conn\.Open\('),
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
# Error Handling Patterns
|
|
57
|
+
self._error_check = [
|
|
58
|
+
re.compile(r'if\s+err\s*!=\s*nil'),
|
|
59
|
+
re.compile(r'if\s+err\s*==\s*nil'),
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
self._error_ignore = [
|
|
63
|
+
re.compile(r'_\s*=\s*\w+'), # _ = function()
|
|
64
|
+
re.compile(r'_\s*:='), # _ := function()
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
# Goroutine/Thread Creation Patterns
|
|
68
|
+
self._thread_creation = [
|
|
69
|
+
re.compile(r'\bgo\s+(?:func|\w+)'),
|
|
70
|
+
re.compile(r'go\s+\w+\('),
|
|
71
|
+
re.compile(r'go\s+func\('),
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
# Security Patterns
|
|
75
|
+
self._security = {
|
|
76
|
+
'command_injection': [
|
|
77
|
+
re.compile(r'exec\.Command'),
|
|
78
|
+
re.compile(r'syscall\.Exec'),
|
|
79
|
+
re.compile(r'syscall\.Spawn'),
|
|
80
|
+
re.compile(r'os\.Exec'),
|
|
81
|
+
],
|
|
82
|
+
'hardcoded_secret': [
|
|
83
|
+
re.compile(r'password\s*:=\s*"[^"]+"'),
|
|
84
|
+
re.compile(r'api[_-]?key\s*:=\s*"[^"]+"'),
|
|
85
|
+
re.compile(r'secret\s*:=\s*"[^"]+"'),
|
|
86
|
+
re.compile(r'token\s*:=\s*"[^"]+"'),
|
|
87
|
+
re.compile(r'aws[_-]?secret'),
|
|
88
|
+
],
|
|
89
|
+
'sql_injection': [
|
|
90
|
+
re.compile(r'Query\([^)]*\+[^)]*\)'),
|
|
91
|
+
re.compile(r'Exec\([^)]*\+[^)]*\)'),
|
|
92
|
+
re.compile(r'\.QueryRow\([^)]*\+'),
|
|
93
|
+
],
|
|
94
|
+
'deserialization': [
|
|
95
|
+
re.compile(r'gob\.Decode'),
|
|
96
|
+
re.compile(r'gob\.NewDecoder'),
|
|
97
|
+
re.compile(r'json\.Unmarshal'),
|
|
98
|
+
re.compile(r'xml\.Unmarshal'),
|
|
99
|
+
],
|
|
100
|
+
'unsafe_usage': [
|
|
101
|
+
re.compile(r'unsafe\.Pointer'),
|
|
102
|
+
re.compile(r'unsafe\.Sizeof'),
|
|
103
|
+
re.compile(r'unsafe\.Alignof'),
|
|
104
|
+
re.compile(r'unsafe\.Offsetof'),
|
|
105
|
+
],
|
|
106
|
+
'path_traversal': [
|
|
107
|
+
re.compile(r'http\.(?:Get|Post)\s*\([^)]*\+[^)]*\)'),
|
|
108
|
+
re.compile(r'ioutil\.ReadFile\([^)]*\+[^)]*\)'),
|
|
109
|
+
],
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# ==================== Thread Safety ====================
|
|
113
|
+
|
|
114
|
+
def get_thread_safe_types(self) -> Set[str]:
|
|
115
|
+
return {
|
|
116
|
+
'sync.Mutex', 'sync.RWMutex', 'sync.WaitGroup', 'sync.Once',
|
|
117
|
+
'sync.Cond', 'sync.Pool', 'sync.Map', 'sync.Locker',
|
|
118
|
+
'chan', 'sync.AtomicInt32', 'sync.AtomicInt64',
|
|
119
|
+
'atomic.Int32', 'atomic.Int64', 'atomic.Uint32', 'atomic.Uint64',
|
|
120
|
+
'atomic.Pointer', 'context.Context', 'errgroup.Group'
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
def get_unsafe_types(self) -> Set[str]:
|
|
124
|
+
return {
|
|
125
|
+
'map', 'slice', '[]',
|
|
126
|
+
'interface{}', # empty interface
|
|
127
|
+
'string', # but safe for read
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
def get_lock_patterns(self) -> List[Pattern]:
|
|
131
|
+
return self._lock_patterns
|
|
132
|
+
|
|
133
|
+
def get_unlock_patterns(self) -> List[Pattern]:
|
|
134
|
+
return self._unlock_patterns
|
|
135
|
+
|
|
136
|
+
def get_volatile_patterns(self) -> List[Pattern]:
|
|
137
|
+
return self._volatile_patterns
|
|
138
|
+
|
|
139
|
+
# ==================== Resource Management ====================
|
|
140
|
+
|
|
141
|
+
def get_cleanup_keywords(self) -> Set[str]:
|
|
142
|
+
return {
|
|
143
|
+
'defer', 'close()', 'defer close()',
|
|
144
|
+
'defer func()', 'defer wg.Done()',
|
|
145
|
+
'defer cancel()' # context cancellation
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
def get_resource_creation_patterns(self) -> List[Pattern]:
|
|
149
|
+
return self._resource_creation
|
|
150
|
+
|
|
151
|
+
# ==================== Error Handling ====================
|
|
152
|
+
|
|
153
|
+
def get_error_types(self) -> Set[str]:
|
|
154
|
+
return {
|
|
155
|
+
'error', 'error interface',
|
|
156
|
+
'io.EOF', 'io.ErrUnexpectedEOF',
|
|
157
|
+
'syscall.Errno'
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
def get_error_check_patterns(self) -> List[Pattern]:
|
|
161
|
+
return self._error_check
|
|
162
|
+
|
|
163
|
+
def get_error_ignore_patterns(self) -> List[Pattern]:
|
|
164
|
+
return self._error_ignore
|
|
165
|
+
|
|
166
|
+
# ==================== Null Safety ====================
|
|
167
|
+
|
|
168
|
+
def get_null_checks(self) -> Set[str]:
|
|
169
|
+
return {
|
|
170
|
+
'== nil', '!= nil', 'if err != nil', 'if err == nil',
|
|
171
|
+
'if ctx == nil', 'if ch == nil', 'if m == nil',
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
def get_null_pointer_types(self) -> Set[str]:
|
|
175
|
+
return {
|
|
176
|
+
'pointer', 'interface', 'slice', 'map', 'chan', 'func',
|
|
177
|
+
'error' # error is nil-checkable
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# ==================== Concurrency Primitives ====================
|
|
181
|
+
|
|
182
|
+
def get_concurrency_primitives(self) -> Set[str]:
|
|
183
|
+
return {
|
|
184
|
+
'go', 'chan', 'select', 'defer',
|
|
185
|
+
'sync.Mutex', 'sync.RWMutex', 'sync.WaitGroup',
|
|
186
|
+
'sync.Once', 'sync.Cond', 'sync.Pool', 'sync.Map',
|
|
187
|
+
'context.Context', 'context.WithCancel',
|
|
188
|
+
'context.WithTimeout', 'context.WithDeadline',
|
|
189
|
+
'errgroup.Group', 'golang.org/x/sync/errgroup',
|
|
190
|
+
'atomic', 'runtime.GOMAXPROCS'
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
def get_thread_creation_patterns(self) -> List[Pattern]:
|
|
194
|
+
return self._thread_creation
|
|
195
|
+
|
|
196
|
+
# ==================== Security ====================
|
|
197
|
+
|
|
198
|
+
def get_dangerous_patterns(self) -> Dict[str, List[Pattern]]:
|
|
199
|
+
return self._security
|
sdk/java_adapter.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Java Language Adapter for DiffSense
|
|
3
|
+
|
|
4
|
+
Provides Java-specific patterns and constructs for rule development.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from typing import List, Set, Pattern, Dict
|
|
9
|
+
from .language_adapter import LanguageAdapter
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class JavaAdapter(LanguageAdapter):
|
|
13
|
+
"""
|
|
14
|
+
Language adapter for Java.
|
|
15
|
+
|
|
16
|
+
Provides Java-specific patterns for thread safety, resource management,
|
|
17
|
+
error handling, and other language constructs.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
super().__init__("java")
|
|
22
|
+
self._compile_patterns()
|
|
23
|
+
|
|
24
|
+
def _compile_patterns(self):
|
|
25
|
+
"""Pre-compile commonly used patterns."""
|
|
26
|
+
# Thread Safety Patterns
|
|
27
|
+
self._lock_patterns = [
|
|
28
|
+
re.compile(r'synchronized'),
|
|
29
|
+
re.compile(r'\.lock\(\)'),
|
|
30
|
+
re.compile(r'\.tryLock\(\)'),
|
|
31
|
+
re.compile(r'\.wait\(\)'),
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
self._unlock_patterns = [
|
|
35
|
+
re.compile(r'\.unlock\(\)'),
|
|
36
|
+
re.compile(r'\.notify\(\)'),
|
|
37
|
+
re.compile(r'\.notifyAll\(\)'),
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
self._volatile_patterns = [
|
|
41
|
+
re.compile(r'\bvolatile\b'),
|
|
42
|
+
re.compile(r'AtomicInteger'),
|
|
43
|
+
re.compile(r'AtomicLong'),
|
|
44
|
+
re.compile(r'AtomicBoolean'),
|
|
45
|
+
re.compile(r'AtomicReference'),
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
# Resource Management Patterns
|
|
49
|
+
self._resource_creation = [
|
|
50
|
+
re.compile(r'new\s+(?:File|Input|Output)?Stream\('),
|
|
51
|
+
re.compile(r'new\s+Connection\('),
|
|
52
|
+
re.compile(r'DriverManager\.getConnection'),
|
|
53
|
+
re.compile(r'new\s+(?:Socket|ServerSocket)\('),
|
|
54
|
+
re.compile(r'new\s+Thread\('),
|
|
55
|
+
re.compile(r'Executors\.new'),
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
# Error Handling Patterns
|
|
59
|
+
self._error_check = [
|
|
60
|
+
re.compile(r'catch\s*\(\s*(\w+\s+)?\w+Exception'),
|
|
61
|
+
re.compile(r'if\s+\w+\s*==\s*null'),
|
|
62
|
+
re.compile(r'if\s+\w+\s*!=\s*null'),
|
|
63
|
+
re.compile(r'Objects\.isNull'),
|
|
64
|
+
re.compile(r'Objects\.nonNull'),
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
self._error_ignore = [
|
|
68
|
+
re.compile(r'catch\s*\(\s*Exception\s*\)'),
|
|
69
|
+
re.compile(r'catch\s*\(\s*\s*\)'),
|
|
70
|
+
re.compile(r'catch\s*\(\s*Throwable\s*\)'),
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
# Thread Creation Patterns
|
|
74
|
+
self._thread_creation = [
|
|
75
|
+
re.compile(r'new\s+Thread\('),
|
|
76
|
+
re.compile(r'executor\.submit'),
|
|
77
|
+
re.compile(r'CompletableFuture\.runAsync'),
|
|
78
|
+
re.compile(r'CompletableFuture\.supplyAsync'),
|
|
79
|
+
re.compile(r'new\s+(?:Fixed|Cached|Single)ThreadPool'),
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
# Security Patterns
|
|
83
|
+
self._security = {
|
|
84
|
+
'sql_injection': [
|
|
85
|
+
re.compile(r'Statement.*\+'),
|
|
86
|
+
re.compile(r'executeQuery.*\+'),
|
|
87
|
+
re.compile(r'createStatement\(\).*\+'),
|
|
88
|
+
],
|
|
89
|
+
'hardcoded_secret': [
|
|
90
|
+
re.compile(r'password\s*=\s*"[^"]+"'),
|
|
91
|
+
re.compile(r'api[_-]?key\s*=\s*"[^"]+"'),
|
|
92
|
+
re.compile(r'secret\s*=\s*"[^"]+"'),
|
|
93
|
+
re.compile(r'token\s*=\s*"[^"]+"'),
|
|
94
|
+
],
|
|
95
|
+
'command_injection': [
|
|
96
|
+
re.compile(r'Runtime\.getRuntime\(\)\.exec'),
|
|
97
|
+
re.compile(r'ProcessBuilder'),
|
|
98
|
+
],
|
|
99
|
+
'deserialization': [
|
|
100
|
+
re.compile(r'ObjectInputStream'),
|
|
101
|
+
re.compile(r'XMLDecoder'),
|
|
102
|
+
re.compile(r'YAML\.load'),
|
|
103
|
+
],
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# ==================== Thread Safety ====================
|
|
107
|
+
|
|
108
|
+
def get_thread_safe_types(self) -> Set[str]:
|
|
109
|
+
return {
|
|
110
|
+
'ConcurrentHashMap', 'ConcurrentMap', 'ConcurrentLinkedQueue',
|
|
111
|
+
'ConcurrentLinkedDeque', 'ConcurrentSkipListMap', 'ConcurrentSkipListSet',
|
|
112
|
+
'CopyOnWriteArrayList', 'CopyOnWriteArraySet',
|
|
113
|
+
'AtomicInteger', 'AtomicLong', 'AtomicBoolean', 'AtomicReference',
|
|
114
|
+
'AtomicIntegerArray', 'AtomicLongArray',
|
|
115
|
+
'Collections.synchronizedMap', 'Collections.synchronizedList',
|
|
116
|
+
'Collections.synchronizedSet', 'Vector', 'Stack', 'Hashtable',
|
|
117
|
+
'BlockingQueue', 'BlockingDeque', 'LinkedBlockingQueue',
|
|
118
|
+
'Semaphore', 'CountDownLatch', 'CyclicBarrier', 'Exchanger'
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
def get_unsafe_types(self) -> Set[str]:
|
|
122
|
+
return {
|
|
123
|
+
'HashMap', 'ArrayList', 'HashSet', 'LinkedList',
|
|
124
|
+
'LinkedHashMap', 'LinkedHashSet', 'TreeMap', 'TreeSet',
|
|
125
|
+
'Map', 'List', 'Set', 'Collection',
|
|
126
|
+
'StringBuilder', 'StringBuffer', # when shared across threads
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
def get_lock_patterns(self) -> List[Pattern]:
|
|
130
|
+
return self._lock_patterns
|
|
131
|
+
|
|
132
|
+
def get_unlock_patterns(self) -> List[Pattern]:
|
|
133
|
+
return self._unlock_patterns
|
|
134
|
+
|
|
135
|
+
def get_volatile_patterns(self) -> List[Pattern]:
|
|
136
|
+
return self._volatile_patterns
|
|
137
|
+
|
|
138
|
+
# ==================== Resource Management ====================
|
|
139
|
+
|
|
140
|
+
def get_cleanup_keywords(self) -> Set[str]:
|
|
141
|
+
return {
|
|
142
|
+
'close()', 'finally', 'try-with-resources',
|
|
143
|
+
'closeQuietly', 'IOUtils.closeQuietly'
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
def get_resource_creation_patterns(self) -> List[Pattern]:
|
|
147
|
+
return self._resource_creation
|
|
148
|
+
|
|
149
|
+
# ==================== Error Handling ====================
|
|
150
|
+
|
|
151
|
+
def get_error_types(self) -> Set[str]:
|
|
152
|
+
return {
|
|
153
|
+
'Exception', 'RuntimeException', 'Throwable',
|
|
154
|
+
'Error', 'IOException', 'SQLException',
|
|
155
|
+
'NullPointerException', 'IllegalArgumentException',
|
|
156
|
+
'IllegalStateException', 'ConcurrentModificationException'
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
def get_error_check_patterns(self) -> List[Pattern]:
|
|
160
|
+
return self._error_check
|
|
161
|
+
|
|
162
|
+
def get_error_ignore_patterns(self) -> List[Pattern]:
|
|
163
|
+
return self._error_ignore
|
|
164
|
+
|
|
165
|
+
# ==================== Null Safety ====================
|
|
166
|
+
|
|
167
|
+
def get_null_checks(self) -> Set[str]:
|
|
168
|
+
return {
|
|
169
|
+
'== null', '!= null', 'Objects.isNull', 'Objects.nonNull',
|
|
170
|
+
'Optional.ofNullable', 'Optional.empty', 'isPresent',
|
|
171
|
+
'if (obj == null)', 'if (obj != null)'
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
def get_null_pointer_types(self) -> Set[str]:
|
|
175
|
+
return {
|
|
176
|
+
'Object', 'String', 'List', 'Map', 'Set', 'Collection',
|
|
177
|
+
'Array', 'Integer', 'Long', 'Double', 'Float', 'Boolean',
|
|
178
|
+
'Date', 'BigDecimal', 'Optional'
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# ==================== Concurrency Primitives ====================
|
|
182
|
+
|
|
183
|
+
def get_concurrency_primitives(self) -> Set[str]:
|
|
184
|
+
return {
|
|
185
|
+
'synchronized', 'volatile', 'Thread', 'Runnable', 'Callable',
|
|
186
|
+
'ExecutorService', 'Executor', 'ThreadPoolExecutor',
|
|
187
|
+
'Future', 'CompletableFuture', 'CountDownLatch',
|
|
188
|
+
'CyclicBarrier', 'Semaphore', 'ReentrantLock', 'ReentrantReadWriteLock',
|
|
189
|
+
'AtomicInteger', 'AtomicLong', 'AtomicBoolean', 'AtomicReference',
|
|
190
|
+
'ConcurrentHashMap', 'ConcurrentMap', 'Collections.synchronizedMap'
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
def get_thread_creation_patterns(self) -> List[Pattern]:
|
|
194
|
+
return self._thread_creation
|
|
195
|
+
|
|
196
|
+
# ==================== Security ====================
|
|
197
|
+
|
|
198
|
+
def get_dangerous_patterns(self) -> Dict[str, List[Pattern]]:
|
|
199
|
+
return self._security
|