mirror of
https://github.com/Quad4-Software/Reticulum-Go
synced 2026-08-29 23:48:44 -04:00
132 lines
4.1 KiB
Go
132 lines
4.1 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
// Copyright (c) 2024-2026 Quad4.io
|
|
|
|
package zenfix
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
func analyzePython(root string) ([]Finding, error) {
|
|
var out []Finding
|
|
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { // #nosec G122 -- user-supplied scan root
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if info.IsDir() {
|
|
base := info.Name()
|
|
if base == "vendor" || base == ".git" || strings.HasPrefix(base, ".") {
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
if !strings.HasSuffix(path, ".py") {
|
|
return nil
|
|
}
|
|
data, err := os.ReadFile(path) // #nosec G122,G304 -- paths under user scan root
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = append(out, scanPython(path, string(data))...)
|
|
return nil
|
|
})
|
|
return dedupeFindings(out), err
|
|
}
|
|
|
|
type pyScope struct {
|
|
pathWaitLine int
|
|
requestPathLine int
|
|
inWhile bool
|
|
inLinkWait bool
|
|
}
|
|
|
|
func scanPython(path, src string) []Finding {
|
|
var out []Finding
|
|
lines := strings.Split(src, "\n")
|
|
scope := pyScope{}
|
|
|
|
for i, line := range lines {
|
|
trim := strings.TrimSpace(line)
|
|
lineno := i + 1
|
|
|
|
if strings.HasPrefix(trim, "def ") {
|
|
scope = pyScope{}
|
|
}
|
|
|
|
if strings.Contains(trim, "require_shared_instance=True") || strings.Contains(trim, "require_shared_instance = True") {
|
|
out = append(out, NewFinding(RulePythonRequireShared, path, lineno, 1, nil))
|
|
}
|
|
if lineHasAny(trim, "on_interface=", "on_interface =") && lineHasAny(trim, "request_path", "await_path") {
|
|
out = append(out, NewFinding(RulePythonOnInterface, path, lineno, 1, nil))
|
|
}
|
|
|
|
if strings.HasPrefix(trim, "while ") || strings.HasPrefix(trim, "for ") {
|
|
scope.inWhile = true
|
|
}
|
|
|
|
if strings.Contains(trim, "while not") && lineHasAny(trim, "link_ready", "link_failed") {
|
|
scope.inLinkWait = true
|
|
}
|
|
|
|
if scope.inLinkWait && strings.Contains(trim, "time.sleep") {
|
|
out = append(out, NewFinding(RulePythonLinkSpin, path, lineno, 1, nil))
|
|
scope.inLinkWait = false
|
|
}
|
|
|
|
if scope.inWhile && strings.Contains(trim, "Transport.has_path") {
|
|
out = append(out, NewFinding(RulePythonPathSpin, path, lineno, 1, nil))
|
|
if scope.requestPathLine > 0 && scope.requestPathLine < lineno {
|
|
out = append(out, NewFinding(RulePythonPathThenSpin, path, lineno, 1, nil))
|
|
}
|
|
}
|
|
if scope.inWhile && strings.Contains(trim, "Transport.request_path") {
|
|
out = append(out, NewFinding(RulePythonRequestPathLoop, path, lineno, 1, nil))
|
|
}
|
|
if scope.inWhile && strings.Contains(trim, "await_path") {
|
|
out = append(out, NewFinding(RulePythonAwaitInLoop, path, lineno, 1, nil))
|
|
}
|
|
if scope.inWhile && lineHasAny(trim, "link.status", "Link.ACTIVE", "link.status ==") && strings.Contains(trim, "time.sleep") {
|
|
out = append(out, NewFinding(RulePythonLinkStatusSpin, path, lineno, 1, nil))
|
|
}
|
|
if scope.inWhile && lineHasAny(trim, "link.status", "Link.ACTIVE") {
|
|
// status check in loop body, sleep may be on next line
|
|
if i+1 < len(lines) && strings.Contains(strings.TrimSpace(lines[i+1]), "time.sleep") {
|
|
out = append(out, NewFinding(RulePythonLinkStatusSpin, path, lineno, 1, nil))
|
|
}
|
|
}
|
|
|
|
if strings.Contains(trim, "await_path") && !scope.inWhile {
|
|
if scope.pathWaitLine == 0 {
|
|
scope.pathWaitLine = lineno
|
|
}
|
|
}
|
|
if strings.Contains(trim, "Transport.has_path") && !scope.inWhile && !strings.HasPrefix(trim, "while ") {
|
|
if scope.pathWaitLine == 0 {
|
|
scope.pathWaitLine = lineno
|
|
}
|
|
}
|
|
if strings.Contains(trim, ".recall(") || strings.Contains(trim, "Identity.recall(") {
|
|
if scope.pathWaitLine == 0 || lineno < scope.pathWaitLine {
|
|
out = append(out, NewFinding(RulePythonRecallBeforePath, path, lineno, 1, nil))
|
|
}
|
|
}
|
|
|
|
if strings.Contains(trim, "Transport.request_path") && !scope.inWhile {
|
|
scope.requestPathLine = lineno
|
|
}
|
|
|
|
if strings.Contains(trim, "LINK_TIMEOUT") && strings.Contains(trim, "= 15") {
|
|
out = append(out, NewFinding(RulePythonFixed15s, path, lineno, 1, nil))
|
|
}
|
|
|
|
if strings.HasPrefix(trim, "while ") || strings.HasPrefix(trim, "for ") {
|
|
continue
|
|
}
|
|
if trim == "" || strings.HasPrefix(trim, "#") {
|
|
scope.inWhile = false
|
|
}
|
|
}
|
|
return out
|
|
}
|