| 232 | # =========================================================================== |
| 233 | |
| 234 | class TestNewTaintSources: |
| 235 | |
| 236 | def test_input_to_getattr(self): |
| 237 | """input() → attr → getattr() must fire (TS006 source).""" |
| 238 | code = """ |
| 239 | attr = input('Enter attribute: ') |
| 240 | value = getattr(obj, attr) |
| 241 | """ |
| 242 | assert findings_for(code, "GETATTR828"), \ |
| 243 | "GETATTR828 must fire when attr comes from input()" |
| 244 | |
| 245 | def test_environ_to_open_no_finding(self): |
| 246 | """os.environ.get() is now OperatorConfig — opening a path the operator |
| 247 | set via environment variable is intentional, not a vulnerability.""" |
| 248 | code = """ |
| 249 | import os |
| 250 | path = os.environ.get('CONFIG_PATH') |
| 251 | with open(path) as f: |
| 252 | data = f.read() |
| 253 | """ |
| 254 | assert not findings_for(code, "OPEN1149"), \ |
| 255 | "OPEN1149 must NOT fire when path comes from os.environ.get() (operator-trusted)" |
| 256 | |
| 257 | def test_http_request_to_open_still_fires(self): |
| 258 | """HTTP request parameter → open() must still fire (attacker-controlled).""" |
| 259 | code = """ |
| 260 | path = request.GET.get('file') |
| 261 | with open(path) as f: |
| 262 | data = f.read() |
| 263 | """ |
| 264 | assert findings_for(code, "OPEN1149"), \ |
| 265 | "OPEN1149 must still fire when path comes from HTTP request" |
| 266 | |
| 267 | |
| 268 | # =========================================================================== |
nothing calls this directly
no outgoing calls
no test coverage detected