Zum Hauptinhalt springen

Prüfungen und Regeln

Diese Seite erklärt, welche Prüfungen in welchem Kommando laufen, welche Regeln es gibt und wann ein Fund den Lauf blockiert.

Ablauf eines Prüflaufs​

Die Phasen laufen in dieser Reihenfolge:

  1. Pfadregeln (security): Liegt jede angefragte oder geänderte Datei im erlaubten Bereich? Ist eine geschützte Datei betroffen? Ein Verstoß beendet den Lauf sofort mit Exit 6. Danach läuft nichts mehr.
  2. Inhaltsregeln (security): Swift-Konstrukte, Secrets, sensible Daten im Log, Netzwerkziele, eigene Regeln.
  3. Plattform-Checks (platform): statische Prüfung von Xcode-Targets und Paket-Targets mit Privacy Manifest.
  4. Externe statische Werkzeuge: format, swiftlint und swiftlint_metrics. Ihre Prozesse starten parallel, der Bericht ordnet sie fest in dieser Reihenfolge.
  5. compile: Build für jede Plattform, nacheinander.
  6. tests: nur wenn der Build auf allen gebauten Plattformen erfolgreich war.

Ein blockierender Fund aus den Schritten 2 bis 6 ergibt Exit 1. Die Schritte danach laufen trotzdem, mit einer Ausnahme: tests startet nicht nach einem fehlgeschlagenen Build.

Welche Prüfung in welchem Kommando läuft​

Prüfungcheck-filecheck-diffcheck-projectStandardAbschaltbar durch Projekt?
securityjajajaan, verpflichtendnein
platformjajajaan, nicht verpflichtendja
formatjajajaan, verpflichtendnur über scopes
swiftlintjajajaan, verpflichtendnur über scopes
swiftlint_metricsjajajaaus, bis Metrikregeln konfiguriert sindüber rules.metrics
compileniejajaan, verpflichtendnur über scopes
testsniejajaan, verpflichtendnur über scopes

„Verpflichtend“ (required: true) heißt: Fehlt das Werkzeug oder bleibt die Prüfung unvollständig, endet der Lauf mit Exit 3. Eine nicht verpflichtende Prüfung erzeugt in diesem Fall nur eine Warnung. Wie man das konfiguriert, steht unter Projektkonfiguration.

compile und tests werden nur angefordert, wenn im Projektwurzelverzeichnis ein Package.swift oder ein .xcodeproj/.xcworkspace liegt.

Pfadregeln​

Regel-IDSchwereAuslöserExit
path-outside-allowed-scopeerrorDatei liegt außerhalb von paths.include oder in paths.exclude6 (scope_violation)
protected-file-changecriticalEine geänderte Datei passt auf protected_files6 (protected_file)

Beide sind nie waivebar: Der Waiver stünde in der .codeguard.yml des Projekts, die derselbe Diff ändern könnte.

Standardmäßig geschützt sind:

.codeguard.yml, .codeguard.yaml, Package.swift, Package.resolved,
**/*.entitlements, **/*.xcodeproj/project.pbxproj, .github/workflows/**,
.gitlab-ci.yml, **/*.xcconfig, .claude/settings.json, .claude/settings.local.json,
.claude/hooks/**, .opencode/package.json, .opencode/plugins/**, opencode.json,
opencode.jsonc, Tools/CodeGuard/**, .codeguard/baselines/**
Geschützte Dateien in check-diff

Ändert ein Diff eine geschützte Datei, etwa Package.swift, die .codeguard.yml oder eine CI-Datei, endet check-diff mit Exit 6. Das gilt auch in Merge Requests. Solche Änderungen brauchen bewusst eine menschliche Prüfung.

Echte Ausgabe nach einer Änderung an Package.swift:

CODEGUARD_BLOCKED

1 blocking violations found in 2 changed files.
Validation: file (complete)
Exit: 6 (protected_file)
Requested: security
Completed: security
Skipped:
Konfiguration: .codeguard.yml (auto, sha256 5967f32a…)
Run: cg_3cd7b976-4cc7-498d-b1fe-da50c5fab091

1. CRITICAL Package.swift
Rule: protected-file-change
The operation targets a protected project path.
Do not change protected files without an explicit policy review.

Next action: Request human review for the reported findings.

Inhaltsregeln​

Die Inhaltsregeln laufen in der Phase security. Sie lesen jede Datei genau einmal, starten keinen Prozess und brauchen kein Trust-Ticket. Die Swift-Regeln arbeiten auf Lexer-Tokens: Kommentare und String-Inhalte zählen nicht als Code, eine Interpolation \(…) schon. Es gibt keine Unterdrückung per Kommentar und keinen Autofix.

IDStandardWas gefunden wird
forbidden-fatal-errorerror, in Testpfaden ausAufruf fatalError( oder Swift.fatalError(
forbidden-force-tryerror, in Testpfaden austry!
forbidden-force-casterror, in Testpfaden ausas!
sensitive-data-in-logcritical, auch in TestsLogger-Aufruf mit sensiblem Bezeichner in den Argumenten
hardcoded-secretcritical bzw. warningSecret-Formate, Passwort in einer URL, sensible Zuweisung
unauthorized-network-destinationerror, in Testpfaden warningNetzwerkziel außerhalb der Allowlist (nur aktiv, wenn eine Allowlist gesetzt ist)

Testpfade sind Tests/**, **/*Tests/** und **/*UITests/**. Sie sind als Standard-path_overrides hinterlegt und lassen sich überschreiben.

forbidden-fatal-error, forbidden-force-try, forbidden-force-cast​

  • Alle #if-Zweige werden geprüft, auch inaktive. Makroexpansionen sind nicht sichtbar.
  • x.fatalError( mit anderem Empfänger zählt nicht. Deklariert die Datei selbst func fatalError, ist der Fund nur probable.
  • try !x ist kein Treffer für forbidden-force-try.
  • Unbalancierte Klammern in einer .swift-Datei ergeben codeguard-content-scan-incomplete statt eines stillen Passes.

sensitive-data-in-log​

  • Logger sind freie Funktionen aus logger_symbols.functions (Standard: print, debugPrint, dump, NSLog, os_log) oder Methoden aus logger_symbols.methods (Standard: debug, info, notice, log, trace, warning, error, fault, critical). Eine Methode zählt nur, wenn das letzte Glied der Empfängerkette log enthält (z. B. logger.info(…)).
  • Sensible Bezeichner (sensitive_identifiers, Standard: token, accessToken, refreshToken, password, passwd, secret, apiKey, authorization, cookie, sessionID, privateKey):
    • Bezeichner gleich einem Eintrag (ohne Groß-/Kleinschreibung): certain, Schwere wie konfiguriert.
    • Nur ein camelCase-Bestandteil passt (z. B. userPasswordHash): possible, höchstens warning.
  • privacy: .private macht einen Aufruf nicht sicher.
  • Der Bericht enthält nie den Argumenttext, nur die Kategorie: Possible password is written to a log.

hardcoded-secret​

StufeDateienTrefferSchwere
Formatalle Textdateien15 Detektoren: AWS Access Key ID, GitHub-Tokens (classic, fine-grained), GitLab PAT, Slack, Stripe Secret Key, OpenAI (zwei Formate), Anthropic, Twilio API Key, SendGrid, npm, PEM-Private-Key-Header, JWT, Google API Keycritical; Google API Key warning
URL-Userinfoalle Textdateienscheme://user:passwort@host mit nicht leerem Passwortcritical
Sensible ZuweisungSwift/ObjC, Plist, JSON, YAML, .xcconfig, .envsensibler Bezeichner mit einem Literal von mindestens 8 Zeichen, das kein Platzhalter istwarning

Kein Treffer sind Platzhalter (<…>, YOUR_…, xxx…, changeme, $(…), ${…}, %@, $VAR), UUIDs, Hex-Digests, offizielle Beispielwerte (z. B. der AWS-Beispielschlüssel) und Swift-enum-Rohwerte. In Package.resolved, *.lock und *.xcassets/**/Contents.json läuft nur die Formatstufe.

Der gefundene Wert erscheint nie im Bericht, im Audit-Log oder im Fingerprint. Die Meldung lautet immer A hard-coded credential was detected. Einen bekannten, harmlosen Wert gibst du dauerhaft über rules.hardcoded-secret.allowlist_sha256 frei (siehe Projektkonfiguration).

PEM-Header in Dokumentation

Schon der Kopf eines PEM-Private-Keys in Doku, Kommentar oder String ist ein critical-Fund.

unauthorized-network-destination​

  • Nur aktiv, wenn security.network.allowed_destinations nicht leer ist. Diese Liste kann nur die Organisationsrichtlinie setzen (siehe Organisationsrichtlinie).
  • Format der Einträge: scheme://host[:port], auch scheme://*.host[:port] für Subdomains beliebiger Tiefe (nicht die Domain selbst). Ohne Port gilt der Standardport.
  • Geprüft werden String-Literale in .swift und String-Werte in Plist und JSON, die mit http://, https://, ws:// oder wss:// beginnen.
  • Ziel außerhalb der Liste: error. Interpolation im Schema- oder Host-Teil oder nicht parsebare URL: warning/possible.
  • Ignoriert werden reservierte Namen (example.com/.org/.net, *.example, *.test, *.invalid, localhost, *.localhost) und Apples Plist-DTD-URL.

Eigene Regeln (custom_rules)​

EngineVerhalten
regexMuster läuft zeilenweise auf dem Rohtext, ein Fund pro Zeile. Kommentare und Strings werden mit durchsucht. Zeilen über 4 KiB werden übersprungen.
fileJede erfasste Datei ist ein Fund (auch Binärdateien). pattern ist verboten.
dependency, architectureNicht umgesetzt. Erzeugt die Warnung codeguard-custom-rule-unsupported.

Eigene Regeln sind nie waivebar, aber über ihre paths steuerbar. Konfiguration siehe Projektkonfiguration.

Was die Inhaltsregeln durchsuchen​

ScopeDateien
check-filedie angefragten Dateien
check-diffhinzugefügte, geänderte, umbenannte, kopierte und (mit --include-untracked) unversionierte Dateien, jeweils ganz
check-projectalle nicht ausgeschlossenen Textdateien

Nie durchsucht werden .git/, .build/, DerivedData/ und *.xcresult in jeder Tiefe.

Wann ein Inhaltsfund in check-diff blockiert​

Jeder Fund bekommt eine scope_relation:

WertBedeutungBlockiert?
introducedFund in einer hinzugefügten Zeile (neue und unversionierte Dateien: immer)ja, wenn die Regel blockiert
touchedFund in einem geänderten Bereich ohne hinzugefügte Startzeileja, wenn die Regel blockiert
pre_existingFund in unverändertem Code einer geänderten Dateinur bei critical

Damit blockiert Altcode in einer angefassten Datei nicht, außer bei kritischen Funden wie Secrets.

Plattform-Checks​

Die Phase platform prüft rein statisch, ohne xcodebuild und ohne Trust-Ticket.

Target-ArtErkennungRegeln
App-artigApplication, Watch-App, App Clip, App Extension, ExtensionKit Extensionalle
FrameworkFramework-Targetnur Privacy
Swift-Paket-TargetOrdner Sources/<Target>/ mit PrivacyInfo.xcprivacynur Privacy, nie privacy-manifest-missing
alles andereCLI-Tool, Test-Bundle, statische Bibliothek, Paket-Target ohne Manifestkeine
Regel-IDStandardAuslöser
privacy-manifest-invaliderror.xcprivacy nicht parsebar, falscher Typ oder unvollständiger Eintrag in NSPrivacyAccessedAPITypes
privacy-manifest-missingerrorApp- oder Framework-Target nutzt eine Required-Reason-API, hat aber kein PrivacyInfo.xcprivacy
privacy-required-reason-undeclarederrorAPI-Kategorie genutzt, aber nicht in NSPrivacyAccessedAPITypes deklariert
privacy-reason-code-invaliderrorReason-Code ist für die Kategorie nicht zugelassen
privacy-tracking-domains-missingwarningNSPrivacyTracking: true ohne NSPrivacyTrackingDomains
purpose-string-missingerrorGeschützte API genutzt, Schlüssel fehlt in Info.plist und INFOPLIST_KEY_* (in mindestens einer Konfiguration)
purpose-string-emptyerrorSchlüssel vorhanden, aber leer
ats-arbitrary-loadswarningNSAllowsArbitraryLoads, …InWebContent oder …ForMedia ist true
ats-insecure-exception-domainwarningAusnahme-Domain mit NSExceptionAllowsInsecureHTTPLoads: true oder TLS unter 1.2
entitlements-file-missingerrorCODE_SIGN_ENTITLEMENTS zeigt auf eine fehlende Datei
entitlement-weakens-hardened-runtimewarningz. B. com.apple.security.cs.disable-library-validation: true
entitlement-get-task-allow-releasewarningget-task-allow: true in einer Konfiguration namens Release
macos-app-sandbox-missingwarningApp-artiges macOS-Target ohne com.apple.security.app-sandbox: true

Jedes Target wird für alle seine Build-Konfigurationen geprüft. Die Analyse darf dafür Dateien außerhalb von paths.include lesen (Projektdatei, Info.plist, Entitlements), paths.exclude gilt aber weiter. Lässt sich ein Setting oder eine Datei nicht sicher auflösen, entsteht codeguard-platform-check-incomplete statt eines geratenen Ergebnisses.

Echter Ausschnitt aus einem macOS-Projekt ohne App Sandbox:

2. WARNING App.entitlements
Rule: entitlement-weakens-hardened-runtime
Entitlement com.apple.security.cs.disable-library-validation is true, weakening the hardened runtime.
Remove com.apple.security.cs.disable-library-validation unless the target has a specific, documented need for it.

3. WARNING App.entitlements
Rule: macos-app-sandbox-missing
Target "App" configuration "Debug" does not set com.apple.security.app-sandbox = true.
Set com.apple.security.app-sandbox to true, or waive this rule for non-App-Store distribution.

Format: swift-format​

  • Regel-ID swift-format, immer error und blockierend. Die Kategorie von swift-format steht in eckigen Klammern vor der Meldung, z. B. [Indentation] unindent by 2 spaces.
  • Geprüft wird immer die ganze Datei.
  • Ohne Projektdatei gelten die swift-format-Standards (unter anderem 2 Leerzeichen Einrückung, 100 Zeichen Zeilenlänge).
  • Eine .swift-format im Projektwurzelverzeichnis wird beachtet. Sie muss eine reguläre Datei (kein Symlink) von höchstens 64 KiB sein und darf nur Schlüssel enthalten, die das swift-format aus Xcode 27 kennt. Unbekannte Schlüssel, falsche Typen oder leere verschachtelte Objekte ergeben Exit 2, eine nicht reguläre Datei Exit 6.
Teilweise rules-Map

Enthält rules in der .swift-format nur einen Teil der Regeln, schaltet swift-format alle nicht genannten Regeln ab. Willst du die Standardwerte der übrigen behalten, nenne sie ausdrücklich (swift-format dump-configuration zeigt alle).

Beispiel einer .swift-format, mit der die Beispielprojekte dieser Hilfe geprüft wurden:

{
"version": 1,
"indentation": { "spaces": 4 },
"lineLength": 120
}

Lint: swiftlint​

  • Regel-IDs swiftlint.<regel>, z. B. swiftlint.force_try. Die SwiftLint-Schwere bleibt erhalten: error blockiert, warning nicht.
  • Beachtet wird höchstens <Projektwurzel>/.swiftlint.yml. Abgelehnt (Exit 6) werden parent_config, child_config, plugins, Remote-URLs in diesen Schlüsseln sowie absolute Pfade und .. in included/excluded.
  • included/excluded der .swiftlint.yml wirken unter CodeGuard nicht, weil SwiftLint auf einer privaten Kopie der Dateien läuft. Steuere den Umfang über paths.include/paths.exclude von CodeGuard.
  • // swiftlint:disable wirkt weiterhin, auch für die verwalteten Metrikregeln.

Inhaltsregeln und SwiftLint können dieselbe Stelle melden, zum Beispiel forbidden-force-try und swiftlint.force_try. Es sind zwei getrennte Funde.

Metriken: swiftlint_metrics​

Ein zweiter, von CodeGuard erzeugter swiftlint-Lauf setzt Metrikregeln aus rules.metrics durch, unabhängig von der .swiftlint.yml des Projekts. Er läuft nur, wenn checks.swiftlint aktiv ist und mindestens eine Metrikregel einen Schwellenwert hat. Es gibt keine eingebauten Standardschwellen.

Schlüssel in rules.metricsRegel-ID
line_lengthcodeguard-metric-line-length
file_lengthcodeguard-metric-file-length
type_body_lengthcodeguard-metric-type-body-length
function_body_lengthcodeguard-metric-function-body-length
closure_body_lengthcodeguard-metric-closure-body-length
cyclomatic_complexitycodeguard-metric-cyclomatic-complexity
nestingcodeguard-metric-nesting
function_parameter_countcodeguard-metric-function-parameter-count
large_tuplecodeguard-metric-large-tuple
enum_case_associated_values_countcodeguard-metric-enum-case-associated-values-count

Überschreitet Code die error-Schwelle, erhält der Fund die Schwere aus rules.metrics.severity (error oder critical) und blockiert. Eine warning-Überschreitung blockiert nicht. Parameter siehe Projektkonfiguration.

Build: compile​

ProjektartmacOSiOS, watchOS, tvOS, visionOS
Swift Packageswift buildxcodebuild build auf einer Kopie des Pakets mit dem Paket-Scheme
Xcode-Projektxcodebuild buildxcodebuild build mit generischem Simulator-Ziel
  • CodeGuard erkennt die Plattformen selbst (Xcode: -showdestinations; Pakete: platforms im Manifest, ohne Angabe nur macOS) und baut die Schnittmenge aus erkannt, konfiguriert (project.platforms) und lokal verfügbar (-showsdks).
  • Die Plattformen werden nacheinander gebaut. Ein Compile-Fehler auf einer Plattform stoppt die übrigen nicht. Timeout, Abbruch oder Werkzeugfehler stoppen die Matrix.
  • Builds laufen offline, ohne Code-Signing, mit einem Job (-jobs 1 bzw. --jobs 1), in einem Arbeitsverzeichnis außerhalb des Projekts. Xcode-Builds laufen auf einer Kopie des Projekts (höchstens 50 000 Einträge, 2 GiB, Tiefe 100; ohne .git, .build, DerivedData, xcuserdata, *.xcresult).
  • Regel-IDs: swift-compiler-error, swift-compiler-warning, swift-compiler-note (SwiftPM) bzw. xcodebuild-compiler-error, xcodebuild-compiler-warning (Xcode).

Echter Compile-Fehler aus einem Swift Package:

1. ERROR Sources/SwiftPMClean/SwiftPMClean.swift:2:17
Rule: swift-compiler-error
[swift-compiler-error] cannot find 'missingValue' in scope

Tests: tests​

  • macOS wird nativ getestet (swift test bzw. xcodebuild test).
  • Für iOS, watchOS, tvOS und visionOS legt CodeGuard Wegwerf-Simulatoren an, bootet sie, testet und löscht sie wieder. Ziele: ios-phone, ios-pad (nur bei iPad-fähigem Xcode-Target, TARGETED_DEVICE_FAMILY enthält 2), watchos, tvos, visionos. Pakete bekommen keinen iPad-Lauf.
  • Runtime: die neueste verfügbare mit Version ≥ Deployment-Target. Gerätetyp: der erste passende Eintrag einer festen Präferenzliste (neuestes iPhone, iPad Pro 13", größte Watch, Apple TV 4K, Vision Pro). Beides lässt sich mit project.test_destinations überschreiben.
  • Regel-ID swift-test-failure. Schlägt derselbe Test auf mehreren Zielen fehl, ist es ein Fund mit allen Zielen.

Echter Fund aus einer iOS-App mit iPad-Unterstützung:

1. [iOS/iPhone, iOS/iPad] ERROR Tests/AppTests.swift:8:1
Rule: swift-test-failure
Expectation failed: 1 == 2

Fehlt für eine erkannte Plattform eine Runtime, wird das Ziel übersprungen (simulatorUnavailable) und es gibt eine Warnung. Bleibt dadurch kein ausführbares Testziel und ist macOS nicht unter den gebauten Plattformen, endet der Lauf mit Exit 3, weil tests verpflichtend ist. Echte Ausgabe einer watchOS-App auf einem Rechner ohne watchOS-Runtime:

1. ERROR
Rule: codeguard-simulator-selection-failed
checks.tests.required is set, but no test target can run on this host; no test ran.
Install a simulator runtime for a detected platform, or set checks.tests.required: false.

2. WARNING
Rule: codeguard-simulator-unavailable
No usable watchOS simulator runtime or device type with deployment target 10.0; its tests were skipped. There are no installed watchOS simulator runtimes.
hinweis

checks.tests.required: false kann nur die Organisationsrichtlinie setzen. Das Projekt kann tests lediglich über scopes: [] herausnehmen. Siehe Projektkonfiguration.

Laufdiagnosen​

Laufdiagnosen beschreiben den Zustand des Laufs, keine Code-Regel. Sie sind nicht waivebar, und severity_floors wirken nicht auf sie.

Regel-IDSchwereBedeutung
codeguard-build-execution-deniederrorBuild/Test nicht autorisiert (kein Trust-Ticket bzw. keine CI-Attestierung). Führt zu Exit 6.
codeguard-tool-output-unparseableerrorWerkzeugausgabe nicht sicher lesbar. Führt zu Exit 4, nie zu einem Pass.
codeguard-dependency-unavailableerror/warningWerkzeug fehlt, oder SwiftPM konnte Remote-Abhängigkeiten nicht laden (offline)
codeguard-workspace-copy-failederrorDie Projektkopie für Xcode überschritt ein festes Limit (Exit 4)
codeguard-content-scan-incompletewarningEine Datei war nicht sicher auswertbar (Größe, Kodierung, Lexer, Zeitbudget, unparsebares JSON/Plist …). Bei checks.security.required: true Exit 3.
codeguard-custom-rule-unsupportedwarningCustom Rule mit Engine dependency oder architecture
codeguard-tool-configuration-rejectedwarningProjekt-.swift-format oder .swiftlint.yml abgelehnt
codeguard-project-config-ignoredwarning--no-project-config, obwohl eine .codeguard.yml existiert
codeguard-platform-check-incompletewarningPlattform-Check konnte ein Target nicht sicher auswerten
codeguard-platform-unavailablewarningErkannte Plattform ohne lokales SDK übersprungen
codeguard-platform-not-builtinfoPlattform im Manifest, die CodeGuard nicht baut (z. B. maccatalyst, linux)
codeguard-platform-resolution-failederrorPlattformauflösung beendet den Lauf vor dem ersten Build
codeguard-simulator-unavailablewarningKeine passende Runtime/Gerätetyp oder kein simctl
codeguard-simulator-selection-failederrorErzwungenes Ziel nicht erfüllbar oder kein Testziel ausführbar
codeguard-simulator-failederrorEin simctl-Schritt ist gescheitert (Exit 4/5)
codeguard-simulator-cleanup-failedwarningEin Simulator ließ sich nicht aufräumen. Ändert den Exit-Code nicht.
codeguard-simulator-sweep-incompletewarningVerwaiste Geräte konnten nicht vollständig aufgeräumt werden

Siehe auch​