Kriterien und Metriken

Nach welchen Maßstäben wird geprüft?

Ein Finding braucht einen konkreten Beleg. Eine Metrik braucht eine nachvollziehbare Formel. Der gemeinsame Katalog erklärt die Regeln, die Prompt-Eingaben und ihre Grenzen.

Regeln, Prompts und Belege

Verstehen, was ein Review aussagt.

Quality Studio verbindet benannte Review-Regeln, Repository-Richtlinien und reproduzierbare Nachweise. Reviewer erläutern ein konkretes Problem; Messwerte helfen zu entscheiden, wo genauer hingesehen werden sollte. Ein Punktwert allein belegt keine Korrektheit.

33 Regeln · 6 Metriken

Findings, aus denen sich konkrete Maßnahmen ableiten lassen

Ein Finding sollte dabei helfen, ein Problem zu reproduzieren und zu beheben, statt eine persönliche Vorliebe des Reviewers festzuhalten.

So prüfen wir
Beschreibe den Auslöser, die Fundstelle im Quellcode, das betroffene Verhalten und eine angemessene Korrektur. Prüfe Randfälle, gleichzeitig ausgeführte Arbeit und die Wiederherstellung nach Fehlern; leite den Schweregrad aus den konkreten Auswirkungen ab.
Grenzen
Vorschläge zur Lesbarkeit sind Empfehlungen, sofern sie keine vereinbarte Regel verletzen oder ein nachgewiesenes Wartungsproblem verursachen. Fehlender Kontext bedeutet Unsicherheit und ist kein Beleg für einen Defekt.

Klare Zuständigkeiten und Abhängigkeitsrichtung

Explizite Grenzen erleichtern es, eine Funktion zu finden und zu verstehen, welche Änderungen sie beeinflussen können.

So prüfen wir
Vergleiche die Ablage des Quellcodes und die Imports mit dem Architekturvertrag des Repositorys. Halte Angular-Code einer Funktion zusammen und Komponenten auf ihre Aufgabe konzentriert; trenne in .NET die Verantwortlichkeiten der Anwendung.
Grenzen
backend/<projects>, backend/tests und frontend/src sind die Konvention dieses Repositorys. Ein Verzeichnis namens src ist nicht grundsätzlich falsch, und ein Verzeichnisbaum kann den inhaltlichen Zusammenhalt einer Komponente nicht belegen.

Konsistente und gut bedienbare Oberflächen

Gemeinsame Bedienelemente und Typografie verringern visuelle Abweichungen und sorgen dafür, dass sich ähnliche Aktionen konsistent verhalten.

So prüfen wir
Verwende die festgelegten Design-Tokens und native Interaktionssemantik. Prüfe die Tastaturbedienung, Lade- und Fehlerzustände sowie die tatsächlich gerenderte Oberfläche; halte die Schrift- und Bundle-Budgets des Repositorys ein.
Grenzen
Das lokale Minimum von 11 px prüft eine vereinbarte Untergrenze, aber keine vollständige Lesbarkeit oder Barrierefreiheit. Browser-Zoom, Kontrast, Inhaltslänge und Interaktion müssen weiterhin geprüft werden.

Begrenzte Arbeit und erkennbare Fehler

Eine Änderung muss verständlich bleiben und weiterhin zügig reagieren, wenn Datenmengen wachsen, Anfragen miteinander konkurrieren oder Abhängigkeiten ausfallen.

So prüfen wir
Prüfe Abbruchmöglichkeiten, Cache-Schlüssel, Ressourcengrenzen und veraltete Antworten. Vermesse realistische Szenarien und schreibe Tests, die fehlschlagen, wenn von außen sichtbares Verhalten nicht mehr funktioniert.
Grenzen
Ein erfolgreicher Benchmark ist ein Nachweis für seine Arbeitslast und seinen Rechner. Testabdeckung und ein erfolgreicher Testlauf belegen nicht, dass jedes wichtige Verhalten durch Assertions geprüft wurde.

Ein Regelpool, für jedes Review passend aufgelöst

Die 33 benannten Regeln unten bilden den mitgelieferten Pool der Review-Engine. Passende Regeln werden nach Review-Art und Technologie ausgewählt und mit den Richtlinien des Repositorys kombiniert. Die Website erklärt die Standardwerte; sie behauptet nicht, dass jede Regel in jedem Review ausgeführt wird.

Benannte Regeln machen Findings nachvollziehbar. Überschreibungen im Repository passen sie an lokale Anforderungen an; ausdrückliche Eingabegrenzen machen fehlenden Kontext sichtbar.

Im Tool zeigt Review policy die wirksamen Regeln, abweichende Schweregrade und ihre Quelle. Prompt input preview zeigt die repositoryweiten Eingaben auf Dateiebene für alle Technologien, einschließlich ausgelassener oder gekürzter Texte und ihres Zeichenbudgets. Das ist eine Vorschau der Eingaben, nicht der gespeicherte Prompt eines abgeschlossenen Laufs. Begründungen und Beispiele erklären die Regeln hier; sie verbrauchen kein Budget im Regel-Prompt.

Metriken mit ihren Annahmen

Messwerte helfen bei der Entscheidung, wo genauer geprüft werden sollte. Keiner dieser Werte ist eine Wahrscheinlichkeit dafür, dass eine Datei korrekt ist.

Review-Abdeckung

Zeigt, für welche für Reviews geeigneten Dateien Review-Nachweise vorliegen.

round(100 × Dateien mit Review-Dokumenten / für Reviews geeignete Dateien, 1)

Ein leerer Prüfbereich ergibt 0 %. Eine Datei zählt, sobald für sie ein Review-Dokument einer beliebigen Review-Art vorliegt.

Grenzen: Auch ein veraltetes Review zählt; dies ist weder Testabdeckung noch ein Beleg dafür, dass alle Review-Arten aktuell sind.

Implementierung
Testabdeckung der Codezeilen

Zeigt, welche der gemeldeten ausführbaren Zeilen von Tests erreicht wurden.

round(100 × abgedeckte Zeilen / ausführbare Zeilen, 1)

Liest unterstützte Abdeckungsberichte; eine in XML gemeldete Zeilenabdeckungsrate kann einen Prozentwert ohne absolute Anzahlen liefern.

Grenzen: Fehlende oder ungültige Berichte bedeuten nicht verfügbar, nicht 0 %. Ausgeführte Zeilen belegen weder sinnvolle Assertions noch Zweigabdeckung oder korrektes Verhalten.

Implementierung
Punktwert der Risikoansicht

Priorisiert Dateien mit schwachen gespeicherten Bewertungen, geringerer Abdeckung und mehr jüngeren Änderungen.

round(0.4 × (100 − Code-Bewertung) + 0.4 × (100 − Zeilenabdeckung %) + 20 × Änderungen / max(1, höchste Änderungsanzahl), 2)

Das Zeitfenster für die Änderungshäufigkeit entspricht der angeforderten Anzahl an Tagen, standardmäßig 90. Fehlt eine Bewertung oder ein Abdeckungswert, bleibt der Punktwert unbekannt.

Grenzen: Dies ist eine Heuristik zur Priorisierung und keine Ausfallwahrscheinlichkeit. Prüfe die Aktualität von Reviews und Testabdeckung, bevor du den Punktwert verwendest.

Implementierung
Findings pro KLOC

Setzt die Finding-Anzahl des Dashboards ins Verhältnis zur Dateigröße.

round(1000 × ungelöste Findings in Review-Dokumenten / max(1, Dateizeilen), 2)

Zählt Findings über die gespeicherten Review-Arten hinweg und verwendet dabei den Filter des Dashboards für den Lösungsstatus.

Grenzen: Eine kleine Datei kann eine hohe Dichte aufweisen. Findings hängen vom Review-Umfang, den Richtlinien, der Aktualität und dem Urteil des Reviewers ab; vergleiche nur Vergleichbares.

Implementierung
Hotspot-Priorität im Dashboard

Verbindet Änderungshäufigkeit, Finding-Dichte und gespeicherte Bewertung, um die Review-Kandidaten im Dashboard zu sortieren.

round(log10(Änderungen + 1) × (Findings pro KLOC + 1) × (101 − (Code-Bewertung oder 50)) / 100, 2)

Bei unbekannter Code-Bewertung wird für diese Rangfolge 50 verwendet; null Änderungen ergeben null. Dies unterscheidet sich von der auf Testabdeckung beruhenden Risikoansicht.

Grenzen: Der Ersatzwert ist eine explizite Annahme für die Rangfolge und keine vergebene Bewertung. Ein niedriger Punktwert bedeutet weder Sicherheit noch vollständige Review-Abdeckung.

Implementierung
Gespeicherte und effektive Review-Bewertung

Erläutert sowohl die gespeicherte Review-Bewertung als auch die aktuelle, durch die Einstufung der Findings angepasste Bewertung. Eine Änderung des Finding-Status kann den angezeigten effektiven Punktwert ohne weiteres Review verändern.

Wenn E > 0: effektiv = min(Sicherheitsobergrenze, clamp(roundAway(100 − (100 − Basiswert) × I / (I + E)), Basiswert, 100)). Wenn E = 0: effektiv = Basiswert. Projektzusammenfassung: roundEven(Mittelwert(verfügbare gespeicherte Bewertungen der Wurzelknoten je Review-Art)).

Der Basiswert ist der gespeicherte Review-Punktwert von 0 bis 100. I summiert die Gewichte der berücksichtigten Findings: offen und akzeptiert. E summiert die ausgeschlossenen Gewichte: erlassen, falsch positiv, behoben oder aktiv unterdrückt. Die Gewichte betragen 16 für kritisch, 8 für hoch, 4 für mittel, 2 für niedrig und ansonsten 1. roundAway rundet einen Wert genau in der Mitte von null weg; roundEven rundet einen solchen Wert auf die nächste gerade ganze Zahl. Bei Reviews mit einem Sicherheitsurteil liegt die Obergrenze bei 79 für warn und bei 59 für block oder unavailable; andernfalls beträgt sie 100. Die Obergrenze gilt für die Anpassung, wenn E > 0. Beobachtetes Beispiel: Bei drei Findings mit hohem Schweregrad, von denen eines als falsch positiv markiert ist, bleiben I = 16 und E = 8; aus dem Basiswert 72 (C) wird 81 (B), sofern keine Sicherheitsobergrenze greift. Die Bewertungsstufen sind A ≥ 90, B ≥ 80, C ≥ 70, D ≥ 60 und ansonsten F.

Grenzen: Der Basiswert beruht auf dem Urteil des Reviewers. Die Einstufung der Findings kann die effektive Bewertung erhöhen, ohne Code oder Testabdeckung zu verändern; ausgeschlossene Beobachtungen bleiben im Verlauf erhalten. Projektzusammenfassungen mitteln die gespeicherten Bewertungen der Wurzelknoten, nicht die effektiven Bewertungen aller Dateien; fehlende Punktwerte werden ausgeschlossen. Prüfe neben jeder Bewertung auch Findings, Richtlinien, Sicherheitssignale und Aktualität.

Implementierung

Regelkatalog 1.5.0

Alle 33 benannten Regeln ansehen
QS-CS-001 · Minimal-API-Endpunkte als typisierte statische Handler gestalten.NET · Code · Standardmäßig aktiviert

Ein Minimal-API-Endpunkt ist eine `static`-Handlerfunktion oder eine lokale Handlerfunktion, die ihre Abhängigkeiten (`HttpContext`, registrierte Dienste, Routen- und Body-Werte) über die Parameterbindung von ASP.NET Core als Parameter erhält und ein typisiertes `IResult` (`Results.Ok`, `Results.Created`, `Results.NoContent`, ...) zurückgibt. Die Routenregistrierung (`app.MapGet`/`MapPost`/...) bleibt ein einzeiliger Verweis auf diesen Handler.

Warum das wichtig ist
`backend/QualityStudio.Api/Program.cs` setzt diese Form bereits konsequent um (`Guidelines`, `InstallGuideline`, `CreateGuideline`, ...): Der Handler lässt sich unabhängig testen, ohne die HTTP-Pipeline zu starten. Abhängigkeiten sind explizit in der Signatur sichtbar, statt aus einem implizit verfügbaren Service Locator geholt zu werden. Jede Route gibt dieselbe kleine Menge typisierter Ergebnisse zurück, die das Framework vorhersehbar serialisieren kann.
So erkennen wir das Problem
Prüfe `app.Map*`-Registrierungen auf Inline-Lambdas, deren Rumpf länger als ein Ausdruck ist, und Handler auf die Verwendung von `IServiceProvider`/`GetRequiredService`, manuelle Schreibzugriffe auf `HttpContext.Response` oder einen anderen Rückgabetyp als `IResult`/`Task<IResult>`. Eine einzeilige Lambda, die an einen benannten Handler weiterleitet, entspricht der vorgesehenen Form.

Schweregrad: mittel. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

static IResult InstallGuideline(HttpContext context, string catalogueId, RepositoryRegistry registry, GuidelineStore store)
{
    var (_, repository) = ResolveRepository(context, registry);
    var installed = store.Install(repository.Root, catalogueId);
    return Results.Created($"{context.Request.PathBase}/api/guidelines/{Uri.EscapeDataString(installed.Id)}", installed);
}

Problematisches Beispiel

app.MapPost("/api/widgets", async (HttpContext context) =>
{
    var store = context.RequestServices.GetRequiredService<WidgetStore>(); // service-locator pull
    var body = await JsonSerializer.DeserializeAsync<WidgetRequest>(context.Request.Body);
    await store.SaveAsync(body!);
    context.Response.StatusCode = 201; // untyped result, no IResult
});
QS-CS-002 · Die kürzeste passende DI-Lebensdauer registrieren; per Konstruktor injizieren.NET · Code · Standardmäßig aktiviert

Registriere einen Dienst nur dann als `Singleton`, wenn er zustandslos ist oder sein interner Zustand sicher von allen Anfragen gemeinsam genutzt werden kann. Registriere pro Operation benötigte oder kurzlebige Helfer als `Transient`, beziehungsweise als `Scoped`, wenn anfragegebundener Zustand tatsächlich erforderlich ist. Beziehe Abhängigkeiten über Parameter des Konstruktors oder Primärkonstruktors. Löse sie innerhalb einer Klasse niemals ad hoc über `IServiceProvider.GetService`/`GetRequiredService` auf, wenn die Klasse sie stattdessen als Konstruktorparameter entgegennehmen könnte.

Warum das wichtig ist
`backend/QualityStudio.Api/Program.cs` registriert `GuidelineStore` als `Singleton`, weil er zustandslos ist und pro Aufruf an das Dateisystem delegiert, `GuidelineImpactAnalyzer` hingegen als `Transient`, weil er pro Analyse arbeitet. Eine unpassende Zuordnung, etwa ein pro Anfrage benötigter Analyzer als Singleton, birgt das Risiko, Zustand zwischen unabhängigen Anfragen weiterzugeben. Konstruktorinjektion macht die tatsächlichen Abhängigkeiten einer Klasse in ihrer Signatur sichtbar und ermöglicht es, sie in Unit-Tests ohne DI-Container einfach zu erzeugen.
So erkennen wir das Problem
Lies die `builder.Services.Add*`-Registrierung zusammen mit dem tatsächlichen Zustand des Typs. Beanstande `AddSingleton` bei einem Typ mit veränderlichen Feldern pro Operation sowie jeden `GetService`/`GetRequiredService`-Aufruf in einer Klasse, deren Konstruktor die Abhängigkeit bereits entgegennehmen könnte. Die Verwendung von `IServiceProvider` im Composition Root ist kein Verstoß.

Schweregrad: mittel. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// Program.cs
builder.Services.AddSingleton<GuidelineStore>();
builder.Services.AddTransient<GuidelineImpactAnalyzer>();

// ReviewJobs.cs — dependencies arrive as primary-constructor parameters
public sealed class ReviewExecutorFactory(
    SensorRegistry sensors,
    StalenessEvaluator stalenessEvaluator) : IReviewExecutorFactory

Problematisches Beispiel

public sealed class ReviewExecutorFactory : IReviewExecutorFactory
{
    public IReviewExecutor Create(IServiceProvider provider, ...) =>
        new ReviewExecutor(new ReviewRunner(sensorRegistry: provider.GetRequiredService<SensorRegistry>()));
        // hides the real dependency behind a service-locator pull instead of a constructor parameter
}
QS-CS-003 · CancellationToken weiterreichen; niemals async void schreiben.NET · Code · Standardmäßig aktiviert

Jede `async`-Methode mit Ein- oder Ausgabe nimmt einen `CancellationToken` entgegen und reicht ihn an jeden mit await abgewarteten Aufruf weiter, der einen akzeptiert. `async`-Methoden geben `Task`/`Task<T>` oder `ValueTask`/`ValueTask<T>` zurück, niemals `async void`, außer bei einem vom Framework vorgeschriebenen Ereignishandler. Blockiere asynchrone Arbeit nicht mit `.Result`, `.Wait()` oder `GetAwaiter().GetResult()`.

Warum das wichtig ist
`GuidelineImpactAnalyzer.AnalyzeAsync(string, GuidelineImpactRequest, CancellationToken, ...)` und die `Async`-Methoden von `ReviewRunner` reichen einen `CancellationToken` durch jeden mit await abgewarteten Aufruf, damit ein Aufrufer ein lang laufendes Review tatsächlich abbrechen kann. `async void` verschluckt Exceptions, statt sie über den zurückgegebenen Task sichtbar zu machen, und synchrones Blockieren auf asynchroner Arbeit kann unter Last einen Anfrage-Thread in einen Deadlock führen. Beides unterläuft die Garantien für Abbruch und Fehlerweitergabe, auf die sich die übrige Codebasis bereits verlässt.
So erkennen wir das Problem
Suche nach `async void`, `.Result`, `.Wait()`, `GetAwaiter().GetResult()` und nach mit await abgewarteten Aufrufen, die eine `CancellationToken`-Überladung besitzen, aber ohne Token aufgerufen werden, obwohl einer im Gültigkeitsbereich verfügbar ist. Beanstande außerdem eine `async`-Methode mit Ein- oder Ausgabe, deren Signatur keinen `CancellationToken` entgegennimmt.

Schweregrad: hoch. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

public virtual async Task<GuidelineImpactResult> AnalyzeAsync(
    string repositoryRoot, GuidelineImpactRequest request, CancellationToken cancellationToken)
{
    var content = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(false);
    // ...
}

Problematisches Beispiel

public async void Refresh() // async void: exceptions never surface to the caller
{
    var content = File.ReadAllText(path); // blocking I/O on an async method
    var result = LoadAsync().Result;      // sync-over-async: can deadlock
}
QS-CS-004 · Tests nach Arrange-Act-Assert isolieren und nach Verhalten benennen.NET · Code · Standardmäßig aktiviert

Der Name einer Testmethode beschreibt das geprüfte beobachtbare Verhalten, etwa `Project_input_overrides_global_by_id`, nicht die getestete Methode. Jeder Test bereitet seine eigene isolierte Testumgebung vor, etwa ein neues temporäres Verzeichnis oder einen Speicher im Arbeitsspeicher, führt eine Aktion aus und prüft die Ergebnisse. Er hängt weder von Zustand ab, den ein anderer Test hinterlassen hat, noch von der Ausführungsreihenfolge. Gib selbst angelegte Ressourcen frei; implementiere `IDisposable`, wenn eine Testumgebung Dateien oder Verzeichnisse erzeugt.

Warum das wichtig ist
`InputResolverTests` erzeugt pro Testinstanz ein eindeutiges temporäres Verzeichnis und implementiert `IDisposable`, um es aufzuräumen. Jeder Testmethodenname benennt das geprüfte Verhalten, statt nur die aufgerufene Methode zu nennen. Dadurch lässt sich die Testsuite sicher parallel ausführen, und schon der Name eines fehlgeschlagenen Tests zeigt, was nicht mehr funktioniert, ohne zuerst den Testrumpf öffnen zu müssen.
So erkennen wir das Problem
Prüfe, ob die Testklasse ihre Testumgebung selbst besitzt und freigibt, etwa einen temporären Pfad pro Instanz oder einen eigenen Speicher, ob der Methodenname ein Verhalten statt eines Membernamens beschreibt und ob kein Test Zustand liest oder schreibt, den ein anderer Test angelegt hat. Gemeinsam genutzte unveränderliche Testdaten sind kein Verstoß.

Schweregrad: niedrig. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

public sealed class InputResolverTests : IDisposable
{
    private readonly string root = Path.Combine(Path.GetTempPath(), "quality-input-tests", Guid.NewGuid().ToString("N"));

    [Fact]
    public void Project_input_overrides_global_by_id()
    {
        Write(global, "rules.md", "rules", "all", "all", 10, "global body");
        Write(Project, "rules.md", "rules", "code", "file", 1, "project body");

        var result = new InputResolver().Resolve(root, "code", ReviewLevel.File, global);

        Assert.Equal("project body", Assert.Single(result.Inputs).Content);
    }
}

Problematisches Beispiel

[Fact]
public void Test1() // name describes nothing; shares a hard-coded path with other tests
{
    Directory.CreateDirectory("/tmp/shared-fixture");
    var resolver = new InputResolver();
    // ... asserts three unrelated behaviors in one test, no cleanup
}
QS-CS-005 · Jeden Repository-Pfad mit dem gemeinsamen Hilfsmodul auf seinen Bereich begrenzen.NET · Sicherheit · Standardmäßig aktiviert

Ein Pfad, der aus einer Anfrage, einer Konfigurationsdatei oder einem gespeicherten Dokument zum Dateisystem gelangt, wird mit `Path.GetFullPath` kanonisiert und darauf geprüft, dass er unterhalb seines konfigurierten Wurzelverzeichnisses liegt. Er wird Segment für Segment durchlaufen, um symbolische Links und Junctions zurückzuweisen. Verwende dafür `PathConfinement` und keine zweite Implementierung. Weise verwurzelte Pfade und `..`-Segmente zurück, statt sie stillschweigend wegzunormalisieren.

Warum das wichtig ist
`RepositoryAccess.NormalizeRelativePath` und `RepositoryAccess.ResolveFile` leiten jeden Dateilesezugriff von Studio durch `PathConfinement.IsWithin` und `PathConfinement.RejectReparseTraversal`. Deshalb antwortet `GET /api/file?path=../other-repo/Second.cs` mit 400, statt den Quellcode eines anderen Repositorys auszuliefern. Ein Präfixvergleich allein reicht unter Windows nicht aus: Groß- und Kleinschreibung können abweichen, und eine Junction innerhalb des Wurzelverzeichnisses kann auf beliebige Orte zeigen. Eine zweite, leicht abweichende Kopie der Prüfung führt dazu, dass eine Aufrufstelle nur den schwächeren Teil übernimmt.
So erkennen wir das Problem
Suche nach `Path.Combine` oder `Path.GetFullPath` für einen Wert von außerhalb des Prozesses, gefolgt von einer Datei- oder Verzeichnisoperation ohne Bereichsprüfung. Prüfe `StartsWith`-Vergleiche auf fehlende abschließende Pfadtrenner oder einen falschen `StringComparison` und private `IsWithin`/`ContainedPath`-Hilfsfunktionen auf Duplizierung von `PathConfinement`. Ein vollständig aus Konstanten aufgebauter Pfad ist kein Verstoß.

Schweregrad: kritisch. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// backend/QualityStudio.Api/PathConfinement.cs
public static void RejectReparseTraversal(string root, string candidate)
{
    if (!IsWithin(root, candidate)) throw new ArgumentException("Path escapes its configured root.");
    var current = Path.GetFullPath(root);
    foreach (var segment in Path.GetRelativePath(root, candidate).Split(
                 [Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar], StringSplitOptions.RemoveEmptyEntries))
    {
        current = Path.Combine(current, segment);
        if (File.GetAttributes(current).HasFlag(FileAttributes.ReparsePoint))
            throw new ArgumentException("Paths cannot traverse symbolic links or junctions.");
    }
}

Problematisches Beispiel

var absolute = Path.Combine(repositoryRoot, request.Path);   // ".." is still in there
if (absolute.StartsWith(repositoryRoot))                     // no separator, no case rule, no link check
{
    return await File.ReadAllTextAsync(absolute, cancellationToken);
}
QS-CS-006 · Externe Prozesse mit festgelegter ausführbarer Datei und Argumentliste starten.NET · Sicherheit · Standardmäßig aktiviert

Die ausführbare Datei eines gestarteten Prozesses stammt aus dem Code oder einer vom Host verwalteten Positivliste, niemals aus Repository- oder Anfragedaten. Argumente werden einzeln in `ProcessStartInfo.ArgumentList` eingetragen, niemals zu einer `Arguments`-Zeichenfolge zusammengesetzt. Dabei gelten `UseShellExecute = false`, `CreateNoWindow = true`, umgeleitete Streams und ein `CancellationToken` beim Warten.

Warum das wichtig ist
`ProcessSensorCommandRunner.RunAsync`, `GitleaksSecurityScanner.RunScanAsync` und `GitleaksBinaryResolver.RunVersionAsync` verwenden alle `ArgumentList`. Damit ist ein Repository-Pfad mit Leerzeichen oder Anführungszeichen ein Argument und kein neuer Befehl. Die ausführbare Datei ist der Teil, den das Escaping von Argumenten nicht schützen kann: Eine konfigurierbare Befehlszeichenfolge erlaubt einem Repository, eine Shell zu benennen. Dieser Prozess liest außerhalb seines Arbeitsverzeichnisses, erbt die Zugangsdaten des Hosts, erreicht das Netzwerk und schreibt für den Host sichtbare Dateien. Die darüberliegenden Bereichsbegrenzungen sind dann nur noch Dekoration.
So erkennen wir das Problem
Suche nach Zuweisungen einer interpolierten Zeichenfolge an `ProcessStartInfo.Arguments`, nach ausführbaren Dateien, die aus Konfiguration, Anfrage oder Repository-Datei gelesen werden, und nach `WaitForExit()` ohne Token oder Zeitlimit. `ArgumentList.Add` für Werte, die das Kindprogramm nur als Daten erreichen, entspricht der vorgesehenen Form; der Dateiname muss festgelegt sein.

Schweregrad: kritisch. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// backend/AgentOrchestrator.CodeQuality/DependencyVulnerabilitySensor.cs
StartInfo = new ProcessStartInfo(executable)
{
    WorkingDirectory = workingDirectory,
    RedirectStandardOutput = true, RedirectStandardError = true,
    UseShellExecute = false, CreateNoWindow = true,
},
...
foreach (var argument in arguments) process.StartInfo.ArgumentList.Add(argument);
await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false);

Problematisches Beispiel

var command = configuration.AnalyzerCommand;              // free-form, repository-owned
var parts = command.Split(' ');
using var process = Process.Start(new ProcessStartInfo(parts[0])
{
    Arguments = string.Join(' ', parts.Skip(1)) + " " + target,   // one quote away from a new command
});
process!.WaitForExit();                                   // no token, no timeout
QS-CS-007 · Geheimnisse und Hostdetails aus Logs, Findings und Antworten heraushalten.NET · Sicherheit · Standardmäßig aktiviert

Ein von einem Scanner gefundenes Geheimnis wird niemals in ein Modell eingelesen, das geschrieben, protokolliert oder zurückgegeben werden kann. Eine Exception-Nachricht, ein Dateisystempfad und die Ausgabe eines Kindprozesses bleiben im Log; der Aufrufer erhält einen stabilen Titel und einen Statuscode. Lösche einen temporären Bericht mit sensibler Ausgabe in einem `finally`-Block.

Warum das wichtig ist
`GitleaksSecurityScanner.ParseJsonFinding` liest Regel-ID, Fundstelle, Beschreibung und Fingerprint und greift niemals auf die gitleaks-Felder `Secret` oder `Match` zu. `BuildFinding` übergibt `null` für den Nachweis des Findings. Dadurch ist das Geheimnis strukturell in nichts enthalten, was Quality Studio speichert: eine Schwärzung, die später nicht vergessen werden kann. Die API folgt demselben Prinzip: Nur `ReviewModelSelectionException.Message` wird an den Aufrufer zurückgegeben, damit niemals ein Pfad oder ein internes Detail zurückgereicht wird.
So erkennen wir das Problem
Prüfe, ob ein geheimnishaltiges Scanner-Feld in einen Record übernommen wird oder ob `exception.Message`, `exception.ToString()`, ein vollständiger Pfad oder stdout beziehungsweise stderr eines Kindprozesses in eine HTTP-Antwort oder ein gespeichertes Dokument gelangen. Suche außerdem nach temporären Dateien mit Scanner-Ausgaben, die nicht auf jedem Ausführungspfad gelöscht werden. Dieselben Details über den Logger zu protokollieren, entspricht der vorgesehenen Form.

Schweregrad: hoch. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// backend/AgentOrchestrator.CodeQuality/GitleaksSecurityScanner.cs
process.StartInfo.ArgumentList.Add("--redact=100");
// ParseJsonFinding reads RuleID, File, the range, Description and Fingerprint —
// never the Secret or Match fields the report also carries.
return new SecurityFindingRecord(ruleId, severity, description, location, Evidence: null, path, accepted);

Problematisches Beispiel

catch (IOException exception)
{
    // Hands the caller the host path and the scanner's raw output, secret included.
    return Results.Problem(detail: exception.ToString() + "\n" + process.StandardOutput.ReadToEnd());
}
QS-CS-008 · Jede Deserialisierung fremder Daten prüfen und begrenzen.NET · Sicherheit · Standardmäßig aktiviert

Bei der Deserialisierung eines Dokuments aus einem Repository, einer Anfrage oder einem anderen Prozess werden Schema-ID und Version geprüft, bevor sein Inhalt verwendet wird. Nicht im Vertrag deklarierte Member werden zurückgewiesen (`JsonUnmappedMemberHandling.Disallow`), und die eingelesene Menge wird begrenzt: eine Größenprüfung vor dem Laden der Bytes, ein explizites `MaxDepth` und keine Abkürzung über `AllowTrailingCommas`.

Warum das wichtig ist
`QualityRunReport` setzt `UnmappedMemberHandling.Disallow` und weist ein Dokument zurück, dessen `schemaVersion` oder `$schema` nicht dem unterstützten Wert entspricht. Dadurch scheitert ein altes oder fremdes Dokument deutlich, statt nur die Hälfte seiner Felder zu binden. `ReviewMetaContract`, `QualityFindingContract` und `FindingStateStore` folgen demselben Muster. Der fehlende Teil ist die Größe: Wird eine Repository-Datei ohne Obergrenze in eine Zeichenfolge gelesen, kann ein Aufrufer durch wiederholte Anfragen den Prozessspeicher erschöpfen. Zudem setzt nichts in dieser Codebasis `MaxDepth`, sodass allein der Standardwert von 64 Ebenen die Verschachtelung begrenzt.
So erkennen wir das Problem
Suche nach `JsonSerializer.Deserialize`, `JsonDocument.Parse` oder `File.ReadAllText`/`ReadAllBytes` für einen Pfad oder Stream, der nicht dem Prozess gehört. Prüfe drei Dinge, bevor das Ergebnis verwendet wird: eine Schema- und Versionsprüfung, die Zurückweisung nicht zugeordneter Member und eine Byte- oder Längenbegrenzung. Das erneute Einlesen eines Dokuments, das dieser Prozess gerade selbst geschrieben hat, ist kein Verstoß.

Schweregrad: hoch. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// backend/AgentOrchestrator.CodeQuality/QualityRunReport.cs
private static JsonSerializerOptions CreateOptions() => new(JsonSerializerDefaults.Web)
{
    WriteIndented = true,
    Encoder = JavaScriptEncoder.Default,
    UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow,
};

if (report.SchemaVersion != 1 || !string.Equals(report.Schema, SchemaId, StringComparison.Ordinal))
    throw new JsonException("Unsupported quality run report schema.");

Problematisches Beispiel

// No size check, no depth bound, no version gate: the file decides how much memory this costs.
var text = await File.ReadAllTextAsync(sidecarPath, cancellationToken);
var meta = JsonSerializer.Deserialize<ReviewMeta>(text)!;
return meta.Findings;
QS-CS-009 · Blockierende Ein- und Ausgabe sowie Prozesswartezeiten aus Anfragepfaden und Locks fernhalten.NET · Performance · Standardmäßig aktiviert

Code, der während einer offenen Anfrage ausgeführt wird, verwendet die asynchronen Datei-, Stream- und Prozess-APIs und wartet mit einem `CancellationToken` auf sie. Kein `File.ReadAllText`, `StandardOutput.ReadToEnd`, `WaitForExit()` oder `.Result` auf einem Anfragepfad, und nichts davon innerhalb eines `lock`: Dadurch wird ein langsamer Aufruf zu einer Warteschlange für alle anderen Aufrufer derselben Sperre.

Warum das wichtig ist
Der Browservertrag von Quality Studio verlangt weniger als 100 ms bis zu einem sichtbaren Übergang und weniger als 500 ms bis zu einem nutzbaren Dashboard. `PERF.md` misst für einen kalten Hierarchiescan 85 % der Zeit eines Repository-Wechsels. Auf einem Anfragepfad ist deshalb kein Platz für einen Thread, der auf Datenträger oder Kindprozess wartet. Blockieren unter einer gehaltenen Sperre verschärft das Problem: `RepositoryHierarchyCache` liest jede geänderte Datei, während die Slot-Sperre gehalten wird. So wird ein mehrsekündiger Scan für jeden gleichzeitigen Wechsel zu diesem Repository zu einer mehrsekündigen Wartezeit, nicht nur für den Aufruf, der ihn ausgelöst hat.
So erkennen wir das Problem
Suche nach synchronen Membern von `File`, `Directory`, `Stream` und `Process` in allem, was ein Endpunkt, Handler oder Hintergrundleser erreichen kann, sowie nach `.Result`, `.Wait()` und `GetAwaiter().GetResult()`. Prüfe, ob solche Aufrufe im Quelltext innerhalb eines `lock`-Blocks oder zwischen `Semaphore.Wait` und der Freigabe stehen. Synchrone Ein- und Ausgabe im Startcode, einem CLI-Befehl oder einer Testumgebung ist kein Verstoß.

Schweregrad: hoch. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// backend/QualityStudio.Api/RepositorySnapshotPrewarmer.cs
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    // Keep host startup non-blocking: the API becomes reachable while snapshots warm in the background.
    await Task.Yield();
    await foreach (var registration in queue.Reader.ReadAllAsync(stoppingToken))
    {
        var hierarchy = await Task.Run(() => hierarchyCache.GetMeasured(registration.Root), stoppingToken);
    }
}

Problematisches Beispiel

lock (slot.Gate)                                  // every other caller of this slot now waits too
{
    var head = process.StandardOutput.ReadToEnd();     // blocking read of a child process
    process.WaitForExit();                             // no token, no timeout
    foreach (var path in changed) hash.Append(File.ReadAllText(path));
}
QS-CS-010 · Cache-Schlüssel aus dem Zustand ableiten und jeden Cache begrenzen.NET · Performance · Standardmäßig aktiviert

Ein Cache-Schlüssel wird aus dem Inhalt abgeleitet, den er repräsentiert. Dadurch ist ein veralteter Eintrag unmöglich und nicht nur unwahrscheinlich; ein Zeitstempel oder eine Gültigkeitsdauer genügt nicht. Jeder Cache legt außerdem seine Grenze fest: eine Größenobergrenze mit Verdrängung oder einen Schlüsselraum, der nachweislich nicht wachsen kann, etwa ein Eintrag pro registriertem Repository statt pro beobachtetem Commit.

Warum das wichtig ist
`RepositoryHierarchyCache.GetMeasured` bildet den Schlüssel eines Slots aus dem Git-Zustand: HEAD, dem vorgemerkten Index und den gehashten Inhalten jedes geänderten oder nicht erfassten Pfads. Deshalb dauert ein warmer Wechsel 17,60 ms gegenüber 9.781,93 ms für einen kalten Scan, ohne ein Aktualitätsfenster schätzen zu müssen. Die Begrenzung wird leicht vergessen: `ProjectDashboardService` verwendet denselben abgeleiteten Schlüssel, hält aber ein vollständiges Dashboard pro `(root, gitState)`-Paar. Damit bleibt jeder jemals beobachtete Commit und jeder Zustand mit lokalen Änderungen für die gesamte Prozesslaufzeit im Speicher.
So erkennen wir das Problem
Prüfe jedes Feld vom Typ `ConcurrentDictionary`, `Dictionary` oder `MemoryCache` auf zwei Dinge: woraus der Schlüssel abgeleitet wird und wodurch ein Eintrag entfernt wird. Ein Schlüssel mit Zeitstempel, Lauf-ID oder einem monoton wachsenden Wert ohne Verdrängung ist ein Verstoß; ein durch eine Registrierungsliste begrenzter Schlüsselraum ist keiner.

Schweregrad: mittel. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// backend/QualityStudio.Api/ProjectDashboard.cs
var key = root + "\0" + snapshot.GitState;   // derived from content, so a stale hit cannot happen
if (cache.TryGetValue(key, out var cached)) return cached;

Problematisches Beispiel

// One retained entry per commit and per dirty state, for the life of the process.
private readonly ConcurrentDictionary<string, Dashboard> cache = new();
public Dashboard Get(string root, string gitState) =>
    cache.GetOrAdd(root + "\0" + gitState, _ => Build(root));
QS-CS-011 · Einen Befehl pro Prüfbereich ausführen, nicht pro Projekt oder Datei.NET · Performance · Standardmäßig aktiviert

Wenn ein Werkzeug für eine ganze Solution oder ein ganzes Repository antworten kann, rufe es einmal auf und übertrage sein Ergebnis auf die Einheiten, die es benötigen. Starte keinen Prozess, öffne keine Verbindung und wiederhole keinen repositoryweiten Scan innerhalb einer Schleife über Projekte, Dateien oder Findings.

Warum das wichtig ist
`DependencyVulnerabilitySensor` führt `dotnet list <project> package --vulnerable` einmal pro gefundenem Projekt aus, obwohl ein einziger Befehl für die gesamte Solution dieselbe Frage beantworten würde. Der Review-Runner ruft repositoryweite Sicherheitssensoren innerhalb jeder Dateioperation auf und filtert das Ergebnis anschließend auf diese eine Datei. Ein Durchlauf über 92 Dateien wiederholt den gesamten Repository-Scan 92-mal. Prozessstart und Repository-Durchlauf dominieren diese Kosten; die Schleife macht den Durchlauf langsam, nicht das Werkzeug.
So erkennen wir das Problem
Suche nach einem Prozessstart, einem HTTP-Aufruf, einer Datenbankabfrage oder einem vollständigen Repository-Durchlauf innerhalb eines `foreach` über Projekte, Dateien oder Findings sowie nach einem Aufruf, dessen Ergebnis unmittelbar auf ein einzelnes Prüfobjekt gefiltert wird. Stapelverarbeitungs-APIs, die tatsächlich nur ein Prüfobjekt pro Aufruf annehmen, sind kein Verstoß; die Wiederholung eines Aufrufs, der bereits den gesamten Prüfbereich akzeptiert, ist einer.

Schweregrad: hoch. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// One repository-wide scan, projected onto the subjects that need it.
var evidence = request.DeterministicEvidence
    ?? await CollectDeterministicEvidenceAsync(request, root, cancellationToken).ConfigureAwait(false);
var forThisUnit = DeterministicEvidenceProjection.ForSubjects(evidence, subjectPaths);

Problematisches Beispiel

foreach (var project in discoveredProjects)   // one process per project
{
    var result = await runner.RunAsync("dotnet",
        ["list", project, "package", "--vulnerable", "--format", "json"], root, cancellationToken);
    findings.AddRange(Parse(result));
}
QS-CS-012 · Jede externe Operation zeitlich begrenzen und den gemeinsamen Warteschlangenleser freihalten.NET · Performance · Standardmäßig aktiviert

Eine Operation, die auf einen Prozess, einen Netzwerkaufruf oder einen Agenten wartet, läuft zusätzlich zu ihrem `CancellationToken` unter einem expliziten Zeitlimit für die verstrichene Gesamtdauer. Der Leser einer Warteschlange startet und überwacht solche Arbeit; er wartet nicht bis zu ihrem Abschluss. So kann eine Operation, die nie zurückkehrt, nicht alle nachfolgenden Operationen aufhalten.

Warum das wichtig ist
`ReviewJobService.ExecuteAsync` verwendet einen Kanal mit einem einzigen Leser, der jeden Lauf bis zum Abschluss abwartet. Eine Operation, die nie zurückkehrt, blockiert den Leser daher dauerhaft. Ihr Abbruch setzt den gespeicherten Zustand auf einen Endstatus, ohne den Leser freizugeben. Die Warteschlange steht dann dauerhaft still, obwohl sie funktionsfähig wirkt. Der Boundary-Sensor veranschaulicht das: 1,4 s bei 144 Dateien, aber bei einem Frontend mit 1.269 Dateien keine Rückkehr innerhalb eines clientseitigen Zeitlimits von 300 Sekunden.
So erkennen wir das Problem
Suche nach `await`s für Prozess-, HTTP- oder Agentenaufrufe ohne verknüpftes Token für ein Zeitlimit und nach einem Kanal- oder Warteschlangenleser, dessen Schleifenrumpf die gesamte Operation abwartet. Ein Zeitlimit, das nur auf dem Client existiert, genügt nicht; die Begrenzung muss auf der Seite liegen, die die Ressource hält.

Schweregrad: hoch. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

using var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
timeout.CancelAfter(OperationTimeout);
var run = Task.Run(() => sensor.ScanAsync(root, timeout.Token), timeout.Token);
_supervisor.Track(operationId, run);   // the reader keeps draining the queue

Problematisches Beispiel

await foreach (var job in reader.ReadAllAsync(stoppingToken))
{
    // One non-returning operation parks the only reader; nothing behind it is ever picked up.
    await runner.RunAsync(job, stoppingToken);
}
QS-GN-001 · Fremde Inhalte als Daten behandeln, niemals als Anweisungallgemein · Code, Sicherheit · Standardmäßig aktiviert

Repository-Quellcode, Modellausgaben, Sensorausgaben und alles, was ein Aufrufer liefert, sind Daten. Werden sie in einen Prompt, Befehl, eine Vorlage, Abfrage oder ein Dokument eingebettet, kennzeichne oder escape ihre Grenze so, dass der Inhalt diese Grenze nicht selbst beenden kann. Validiere die Rückgabe, statt ihren Aussagen über sich selbst zu vertrauen.

Warum das wichtig ist
`ReviewPromptBuilder` erzeugt pro Prompt eine neue 128-Bit-Grenzmarkierung, gerade damit Dateiinhalte die schließende Markierung nicht erraten und fälschen können. Die Prompts erklären alles zwischen den Markierungen unabhängig von seinen eigenen Behauptungen zu nicht vertrauenswürdigem Inhalt. Die Ausgabeseite ist die andere Hälfte: `ReviewResponseParser` weist ein Finding zurück, das eine deterministische Herkunft behauptet, und `FindingIdentity` berechnet die Hashes der Ausschnitte selbst. Denn eine vom Modell gelieferte Aussage über seine eigene Vertrauenswürdigkeit ist kein Nachweis. Die Formulierung des Prompts allein schafft keine Grenze; dafür sorgen die Markierung, die Validierung und die vom Host berechneten Anker.
So erkennen wir das Problem
Suche nach nicht vertrauenswürdigen Inhalten, die mit festen oder vorhersehbaren Trennzeichen in einen Prompt, Shell-Befehl, eine SQL-Anweisung, einen Pfad oder Markup eingefügt werden. Prüfe außerdem, ob ein vom Erzeuger kontrolliertes Feld bestimmt, wie sehr diesem Erzeuger vertraut wird, etwa eine behauptete Quelle, ein behaupteter Hash oder Schweregrad. Als gebundener Parameter, Argumentlisteneintrag oder escapeter Wert übergebener Inhalt ist kein Verstoß.

Schweregrad: hoch. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// backend/AgentOrchestrator.CodeQuality/ReviewPromptBuilder.cs
// Fresh per prompt so repository content cannot pre-guess and forge a closing marker.
private static string GenerateContentBoundary() =>
    "QS-CONTENT-" + Convert.ToHexStringLower(RandomNumberGenerator.GetBytes(16));

Problematisches Beispiel

var prompt = "Review this file:\n---\n" + fileContent + "\n---\n" + instructions;
// and, on the way back, believing what the response says about itself:
if (response["source"]?.GetValue<string>() == "analyzer") finding.Trusted = true;
QS-GN-002 · Jede Ressource explizit begrenzenallgemein · Code, Sicherheit, Performance · Standardmäßig aktiviert

Alles, dessen Größe jemand anderes bestimmt, hat eine festgelegte Grenze: Bytes von Anfragen und Dokumenten, Verschachtelungstiefe, Anzahl von Sammlungselementen und Ergebnissen, aufbewahrte Cache-Einträge, gleichzeitige Operationen und verstrichene Gesamtdauer. Die Grenze wird dort validiert, wo sie konfiguriert wird, und dort durchgesetzt, wo die Ressource verbraucht wird. Ihre Überschreitung führt zu einer klaren Zurückweisung statt zu einem langsamen Ausfall.

Warum das wichtig ist
`ApiSecurity` validiert seine Grenzen beim Start: Anfrageinhalte zwischen 1 KiB und 10 MiB, 1 bis 1024 gleichzeitige Anfragen und 1 bis 1000 kostenverursachende Anfragen pro Minute. So lässt eine Fehlkonfiguration den Host scheitern, statt stillschweigend eine Grenze aufzuheben. Die Lücken zeigen, was ohne diese Disziplin passiert: Das Lesen einer Repository-Datei ohne Größenprüfung erlaubt einem Aufrufer, durch wiederholte Anfragen den Prozessspeicher zu erschöpfen. Der Sidecar-Index behält zudem ein geparstes Dokument pro Datei ohne Byte-Obergrenze. Die Kosten wachsen damit anhand einer vom Repository bestimmten Anzahl statt einer vom Prozess akzeptierten Grenze.
So erkennen wir das Problem
Frage bei jeder Ressource, die eine Grenze überschreitet, nach ihrem Limit und dessen Durchsetzung: ein Lesezugriff ohne Längenprüfung, ein Parse-Vorgang ohne Tiefengrenze, eine in einer Schleife unbegrenzt aufgebaute Sammlung, ein Cache ohne Verdrängung, ein Warten ohne Zeitlimit oder ein Wiederholungsversuch ohne Höchstgrenze. Ein Limit, das nur in der Dokumentation oder im Client steht, ist keine Begrenzung.

Schweregrad: hoch. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// backend/QualityStudio.Api/ApiSecurity.cs
if (MaxRequestBodyBytes is < 1024 or > 10 * 1024 * 1024)
    throw new InvalidOperationException("MaxRequestBodyBytes must be between 1 KiB and 10 MiB.");
if (MaxConcurrentRequests is < 1 or > 1024)
    throw new InvalidOperationException("MaxConcurrentRequests must be between 1 and 1024.");

Problematisches Beispiel

// Size chosen by the repository, retained for the life of the process, parsed at default depth.
var payload = JsonDocument.Parse(File.ReadAllText(sidecarPath));
index[relativePath] = payload.RootElement.Clone();
QS-GN-003 · Die deklarierte Architektur des Repositorys erhaltenallgemein · Code · Standardmäßig aktiviert

Respektiere die im Repository festgelegten Zuständigkeiten für Quellcode und die Abhängigkeitsrichtung. Existiert ein `quality-architecture.json`-Vertrag, halte erforderliche Verzeichnisse, aufgegebene Quellcodeablagen und Verzeichnis-Positivlisten damit konsistent. Behandle eine beabsichtigte Architekturänderung als abgestimmte Aktualisierung von Code, Vertrag, Skripten und Dokumentation.

Warum das wichtig ist
Ein asymmetrischer oder auseinanderlaufender Repository-Aufbau erschwert es, Zuständigkeiten zu erkennen, und lässt Build-, Test- und Review-Werkzeuge auf veraltete Orte verweisen. Ein versionierter Vertrag macht aus einer vereinbarten Architektur prüfbare Nachweise, ohne anzunehmen, dass eine Ordnerkonvention für jedes Repository richtig ist.
So erkennen wir das Problem
Verwende die mit Quellcodefundstellen versehenen Findings des Architektursensors und vergleiche geänderte Pfade mit dem repositoryeigenen Vertrag. Beanstande Quellcode, der an eine aufgegebene Ablage zurückkehrt, fehlende erforderliche Verzeichnisse, unerwartete direkte Untereinträge und ungültige Verträge. Leite bei Repositorys ohne Vertrag keine Verstöße allein aus Ordnernamen ab. Generierte Build-Ausgaben und historische Review-Metadaten sind kein Produktquellcode. Verzeichnisprüfungen belegen weder den inhaltlichen Zusammenhalt einer Komponente noch die Abhängigkeitsrichtung; das leisten sprachspezifische Analyzer und das Urteil des Reviewers.

Schweregrad: mittel. Deterministische Zuordnungen: architecture/missing-directory, architecture/forbidden-source-path, architecture/unexpected-entry, architecture/invalid-contract

Beispiele

Passendes Beispiel

{
  "schemaVersion": 1,
  "requiredDirectories": ["backend", "backend/tests", "frontend/src"],
  "forbiddenSourcePaths": ["src", "backend/src"],
  "directoryRules": []
}

Problematisches Beispiel

quality-architecture.json  # declares backend and forbids retired src locations
backend/Api/Program.cs
backend/src/AnotherApi/Program.cs  # reintroduces an unnecessary retired source wrapper
QS-GN-004 · Konkrete Fehler melden und ihre Auswirkungen erklärenallgemein · Code, Sicherheit, Performance · Standardmäßig aktiviert

Begründe jedes Finding mit einer konkreten Fundstelle im Quellcode und einem plausiblen Auslöser. Erkläre das betroffene Verhalten oder die betroffene Vertrauensgrenze, die Folge und eine angemessene Korrektur. Verwende die ID der verletzten Entwicklungsregel, falls eine zutrifft. Unterscheide einen Verstoß gegen den Repository-Vertrag von einer persönlichen Vorliebe und benenne Unsicherheit, wenn erforderlicher Kontext fehlt.

Warum das wichtig ist
Ein Review ist nützlich, wenn ein anderer Entwickler verstehen kann, was fehlschlägt und warum die vorgeschlagene Änderung es behebt. Unbelegte Aussagen zum Schweregrad und willkürliche Stilforderungen erzeugen Rauschen. Fehlerpfade, Nebenläufigkeit und für Nutzer sichtbare Regressionen verdienen dagegen eine ausdrückliche Begründung.
So erkennen wir das Problem
Verfolge bei einem vermuteten Problem die Eingabe oder den Zustandsübergang bis zur beobachtbaren Folge. Prüfe angrenzende Aufrufer, den Repository-Vertrag und relevante Tests. Benenne die fehlende Bedingung oder falsche Abhängigkeit, statt nur allgemein auf einen schlechten Codegeruch hinzuweisen. Mache aus einer ungeprüften Annahme, einer Vorliebe für Ordnernamen oder einer Zielzahl für Tests kein Finding. Bevorzuge einen kleinen Regressionstest für das Verhalten, wenn er den Fehler nachweist; verlange keine Tests, die lediglich triviale Implementierungsdetails wiederholen.

Schweregrad: mittel. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

When request A finishes after the user selects repository B, its response replaces B's tree.
The completion handler does not compare the captured repository id with the current one.
Guard the response before applying it and test the A/B response order.

Problematisches Beispiel

The service is long, so it must contain critical bugs. Rename src and add more tests.
QS-GN-005 · Für die Suche vorgesehene öffentliche Seiten indexierbar haltenallgemein · Code · Standardmäßig deaktiviert

Bei HTML-Seiten, die ausdrücklich über Suchmaschinen gefunden werden sollen, müssen die ausgelieferte Antwort und die Crawler-Anweisungen zu diesem Zweck passen. Beabsichtigte Ausschlüsse für private Seiten, Vorschauen und nicht für die Suche vorgesehene Seiten bleiben erhalten.

Warum das wichtig ist
Eine öffentliche Produktseite kann das Produkt nicht in der Suche erklären, wenn beim Deployment versehentlich die noindex-Anweisung einer Vorschau erhalten bleibt. Crawl-Zugriff und Indexierungserlaubnis sind getrennte Fragen: robots.txt ist weder eine Zugriffskontrolle noch ein zuverlässiges Mittel, um eine URL aus der Suche zu entfernen. Primärquellen: [Google: Indexierung verhindern](https://developers.google.com/search/docs/crawling-indexing/block-indexing) und [Speicherort und Geltungsbereich von robots.txt](https://developers.google.com/crawling/docs/robots-txt/create-robots-txt).
So erkennen wir das Problem
Vor einem Finding die beabsichtigte Zielgruppe und Route klären; öffentlich erreichbar bedeutet nicht automatisch SEO-relevant. Eine widersprüchliche Produktionsantwort, ein robots-Meta-Tag, X-Robots-Tag, eine Authentifizierungsweiterleitung oder eine tatsächlich geltende robots.txt-Anweisung auf Host-Ebene belegen. Eine robots.txt unter /quality/ steuert nicht den gesamten Host. Ein durch robots.txt blockierter Crawler kann die noindex-Anweisung dieser Seite nicht lesen. Nicht das Entfernen von Authentifizierung, beabsichtigtem noindex oder Datenschutzmaßnahmen empfehlen. Eine fehlende robots.txt ist für sich genommen kein Fehler. Ohne Deployment- oder Header-Belege nur den nachweisbaren Konflikt im Quellcode melden; keinen tatsächlichen Indexierungsfehler oder Rankingverlust behaupten.

Schweregrad: hoch. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

<!-- Public product overview intended for search; no preview exclusion. -->
<!doctype html>
<html lang="de">
<head><title>Quality Studio: nachvollziehbare Code-Reviews</title></head>
<body><main><h1>Code-Reviews mit nachvollziehbaren Findings</h1></main></body>
</html>

Problematisches Beispiel

<!-- Production overview still inherits the preview-only directive. -->
<meta name="robots" content="noindex">
<h1>Discover Quality Studio</h1>
QS-GN-006 · Canonical- und Sprach-URLs konsistent haltenallgemein · Code · Standardmäßig deaktiviert

Bei suchrelevanten HTML-Seiten müssen deklarierte Canonical-URLs, Weiterleitungen und Sprachalternativen zu den tatsächlichen Seiten passen. Separat adressierte Sprachfassungen benötigen stabile URLs und bei Verwendung von hreflang wechselseitige Verweise auf die Alternativen.

Warum das wichtig ist
Eine Sprachumschaltung auf nur einer URL kann nicht mehrere unabhängig adressierbare Sprachseiten beschreiben. Widersprüchliche Canonical- und Alternativverweise können verschleiern, welche Seite ein Besucher erreichen soll. Canonical-Angaben sind Signale; sie garantieren nicht, welche URL die Suchmaschine auswählt. Primärquellen: [Google: Canonical-URLs](https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls) und [lokalisierte Versionen](https://developers.google.com/search/docs/specialty/international/localized-versions).
So erkennen wir das Problem
Die tatsächlich ausgelieferte Sprache, Canonical-Angabe, Weiterleitungen und deklarierten Sprachziele der geprüften Seite vergleichen. Widersprüchliche Canonicals, nicht vorhandene Sprach-URLs oder fehlende Rückverweise in einem bestehenden hreflang-Satz mit präzisen Quellcode- oder Antwortbelegen melden. Für jede Sprache eine Canonical-URL derselben Sprache verwenden, sofern eine entsprechende Fassung existiert; nicht jede Sprachfassung auf die deutsche Startseite kanonisieren. Bei generierten href-Werten die gerenderte Ausgabe prüfen. Allein das Fehlen von Canonical oder hreflang ist kein allgemeingültiger Fehler; zunächst Duplikate oder eine erklärte Lokalisierungsanforderung belegen. Nicht daraus schließen, dass eine JavaScript-Anwendung nicht indexiert werden kann oder ein nicht besuchtes Ziel defekt ist.

Schweregrad: mittel. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

<!-- /quality/en/criteria/; the German page links back to this URL. -->
<html lang="en">
<head>
  <link rel="canonical" href="https://agent-orchestrator.dev/quality/en/criteria/">
  <link rel="alternate" hreflang="en" href="https://agent-orchestrator.dev/quality/en/criteria/">
  <link rel="alternate" hreflang="de" href="https://agent-orchestrator.dev/quality/criteria/">
</head>
</html>

Problematisches Beispiel

<!-- English criteria content points to an unrelated German overview. -->
<link rel="canonical" href="https://agent-orchestrator.dev/quality/">
<link rel="alternate" hreflang="en" href="https://agent-orchestrator.dev/quality/#english">
QS-GN-007 · Jede öffentliche Seite mit eigenem Titel und passenden Metadaten beschreibenallgemein · Code · Standardmäßig deaktiviert

Suchrelevante HTML-Seiten erhalten aussagekräftige Titel, die zu ihrem sichtbaren Zweck und ihrer Sprache passen. Vorhandene Beschreibungen müssen den Inhalt der jeweiligen Seite korrekt zusammenfassen.

Warum das wichtig ist
Unterschiedliche Titel helfen bei der Auswahl zwischen Informationen zu Reviews, Kriterien und Modellen. Kopierte oder irreführende Metadaten versprechen den falschen Inhalt. Suchmaschinen können andere Titelverknüpfungen und Snippets erzeugen; selbst verfasste Metadaten bestimmen deren genaue Darstellung nicht. Primärquellen: [Google: Titelverknüpfungen](https://developers.google.com/search/docs/appearance/title-link) und [Such-Snippets](https://developers.google.com/search/docs/appearance/snippet).
So erkennen wir das Problem
Ausgelieferte oder gerenderte Metadaten zusammen mit dem Hauptinhalt prüfen. Einen fehlenden oder leeren Titel, nachweislich irreführende Sprache oder Zweckbeschreibung oder identische Standardtexte auf wesentlich unterschiedlichen geprüften Seiten konkret belegen. Eine fehlende Beschreibung als Verbesserungsmöglichkeit behandeln, sofern keine ausdrückliche Projektanforderung daraus einen Fehler macht. Nicht jeden wiederholten Markennamen beanstanden, keine willkürlichen Zeichenlimits oder Keyword-Dichten verlangen und keine Rankingverbesserung versprechen. Eine Quellvorlage ohne Titel ist kein ausreichender Beleg, wenn ein bekannter Build- oder Rendering-Schritt ihn ergänzt. Private Dashboards und JSON-API-Antworten liegen außerhalb dieser Regel.

Schweregrad: mittel. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

<!-- Criteria page; main content explains rule evidence and limits. -->
<title>Review-Kriterien und Qualitätsmetriken | Quality Studio</title>
<meta name="description" content="Wie Quality Studio Regeln, Messwerte und Review-Evidenz erklärt und welche Grenzen die Ergebnisse haben.">
<h1>Qualität nachvollziehbar prüfen</h1>

Problematisches Beispiel

<!-- Every generated page receives this unrelated template metadata. -->
<title>Home</title>
<meta name="description" content="Buy shoes at the best price.">
<h1>Code review models and token costs</h1>
QS-GN-008 · Für die Suche vorgesehene öffentliche Seiten über echte Links auffindbar machenallgemein · Code · Standardmäßig deaktiviert

Suchrelevante Seiten mit aussagekräftigen Links verbinden, die zu den beabsichtigten Zielen führen. Wenn das Projekt eine Sitemap veröffentlicht, müssen deren Einträge zu den kanonischen öffentlichen Routen passen.

Warum das wichtig ist
Crawler und Besucher brauchen einen Weg zum Inhalt. Echte Linkziele ermöglichen außerdem das Öffnen in einem neuen Tab und Navigation ohne eigens programmierten Klick-Handler. Eine Sitemap kann die Auffindbarkeit unterstützen, ersetzt aber weder benutzbare Navigation noch garantiert sie eine Indexierung. Primärquellen: [Google: crawlbare Links](https://developers.google.com/search/docs/crawling-indexing/links-crawlable) und [Zweck einer Sitemap](https://developers.google.com/search/docs/crawling-indexing/sitemaps/overview).
So erkennen wir das Problem
Bei für die Öffentlichkeit vorgesehenem HTML die gerenderten Links auf nutzbare href-Ziele prüfen und einen nachweislich defekten oder unzugänglichen Navigationsweg beschreiben. Eine Router-Direktive im Framework-Quellcode kann einen gültigen href erzeugen; deshalb vor einem Finding ihre Ausgabe prüfen. Bei einer vorhandenen Sitemap die Einträge mit bekannten kanonischen Routen und dem ausdrücklichen Indexierungsziel vergleichen; den konkreten veralteten, privaten oder nicht vorhandenen Eintrag benennen. Eine fehlende Sitemap ist kein allgemeingültiger Verstoß, besonders bei einer kleinen, gut verlinkten Website. Buttons für Aktionen statt Navigation sind zulässig. Keine feste Linkanzahl verlangen und aus einem Review einzelner Dateien nicht ableiten, dass nicht untersuchte Seiten verwaist seien.

Schweregrad: mittel. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

<nav aria-label="Quality Studio">
  <a href="/quality/reviews/">Reviews verstehen</a>
  <a href="/quality/criteria/">Kriterien und Metriken</a>
  <a href="/quality/models/">Modelle vergleichen</a>
</nav>

Problematisches Beispiel

<!-- The only route to the criteria page exists inside a click handler. -->
<span onclick="location.href='/quality/criteria/'">Click here</span>
QS-NG-001 · Design-Tokens statt Rohwerten verwendenAngular · Code · Standardmäßig aktiviert

Komponentenstyles müssen für Farbe, Abstände, Radien und Typografie die zentralen benutzerdefinierten Eigenschaften der Design-Tokens referenzieren, etwa `--studio-*`, `--space-*`, `--font-*` und `--syntax-*`, die einmalig in `frontend/src/app/shared/styles/tokens.css` definiert sind. Hinterlege keine Hex-Farben, rohen Pixelwerte oder einmaligen Schriftgrößen direkt in der eigenen `.css`-Datei einer Komponente.

Warum das wichtig ist
Eine einzige Token-Quelle ermöglicht der gesamten Anwendung den Theme-Wechsel, etwa hell/dunkel über `data-theme`, und hält sie visuell konsistent, ohne jeden Feature-Ordner durchsuchen zu müssen. Jeder von einer Komponente neu erfundene Rohwert ist ein Wert, den das Token-System und spätere Theme-Änderungen weder sehen noch gemeinsam anpassen können.
So erkennen wir das Problem
Die lokale PostCSS-Typografieprüfung meldet literale Schriftgrößen und `--studio-font-size-*`-Tokens unterhalb des festgelegten Minimums von 11 px. Relative Größen erfordern eine Prüfung der gerenderten Darstellung. Lies die `.css`-Datei der Komponente auf literale Farben (`#rrggbb`, `rgb(`, Farbnamen), rohe `px`/`rem`-Längen für padding, margin, gap, `border-radius` und `font-size` sowie auf von Hand ausgeschriebene Schatten. Ein Wert ist ein Verstoß, wenn ein entsprechendes `--studio-*`-, `--space-*`- oder `--font-*`-Token in `frontend/src/app/shared/styles/tokens.css` existiert. `0`, `1px`-Haarlinien und Prozent-/`fr`-Layoutwerte sind keine Verstöße.

Schweregrad: mittel. Deterministische Zuordnungen: quality-architecture/minimum-font-size

Beispiele

Passendes Beispiel

/* frontend/src/app/features/reviews/review-panel/review-panel.css */
.severity {
  padding: var(--studio-space-1) var(--studio-space-2);
  border-radius: var(--studio-radius-badge);
  background: var(--studio-bg-elevated);
  font-size: var(--studio-font-size-label);
}
.severity.critical { color: var(--studio-severity-critical); }

Problematisches Beispiel

/* a new feature invents its own palette and spacing instead of reusing tokens */
.priority-tag {
  padding: 4px 8px;
  border-radius: 4px;
  background: #eef1f5;
  font-size: 11px;
  color: #991b1b;
}
QS-NG-002 · Standardkomponenten und gemeinsame Grundbausteine wiederverwendenAngular · Code · Standardmäßig aktiviert

Prüfe vor dem Hinzufügen eines neuen Markup-Musters für Badges, Panels oder Bedienelemente, ob in `frontend/src/app/shared/styles/primitives.css` und `frontend/src/app/shared/ui/` bereits ein gemeinsamer Grundbaustein, etwa `.severity`, `.pane` oder `.pane-header`, oder eine wiederverwendbare Standalone-Komponente existiert. Erweitere oder verwende sie wieder, statt eine parallele Einzelimplementierung mit eigenem Markup und Styling zu schreiben.

Warum das wichtig ist
`frontend/src/app/shared/styles/primitives.css` dokumentiert dies bereits als explizite Konvention: „Gemeinsame Workbench-Grundbausteine, die in Shell-Bereichen wie Explorer, Editor und ReviewPanel wiederverwendet werden.“ Duplizierte Einzelbausteine entwickeln sich im Lauf der Zeit auseinander, etwa bei Abständen, Zuständen und Barrierefreiheit, und verdoppeln den Wartungsumfang. Genau dieses Fehlermuster können Design-Tokens allein nicht verhindern: Eine Komponente kann Tokens korrekt verwenden und trotzdem ein bereits vorhandenes Muster neu erfinden.
So erkennen wir das Problem
Vergleiche Markup und Klassennamen der Komponente mit den in `frontend/src/app/shared/styles/primitives.css` deklarierten gemeinsamen Grundbausteinen (`.studio-button`, `.studio-field`, `.studio-table-head`, `.studio-badge`, `.pane`) und den gemeinsamen UI-Komponenten. Ein neues Element, dessen Klassenliste und Struktur einen vorhandenen Grundbaustein unter anderem Namen duplizieren, ist ein Verstoß; ein tatsächlich neues visuelles Muster ist keiner.

Schweregrad: mittel. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

<!-- frontend/src/app/features/reviews/review-panel/review-panel.html: reuses the shared .severity primitive -->
<span class="severity" [class]="'severity ' + finding.severity">{{ finding.severity }}</span>

Problematisches Beispiel

<!-- a new feature reimplements its own severity chip instead of reusing .severity -->
<span class="status-chip" [ngStyle]="{ background: severityColor(finding.severity) }">
  {{ finding.severity }}
</span>
QS-NG-003 · Komponenten auf ihre Aufgabe konzentrieren und festgelegte Feature-Grenzen einhaltenAngular · Code · Standardmäßig aktiviert

Gruppiere Angular-Code nach der festgelegten Zuständigkeit: Infrastruktur und Verträge in core, wiederverwendbare Darstellung und reine Hilfsfunktionen in shared, Produktverhalten in features und die Zusammenstellung der Anwendung in shell. Folge dem eigenen Architekturvertrag des Repositorys, wenn dieser einen anderen Aufbau festlegt. Halte Komponenten auf ihre Aufgabe konzentriert und lege Implementierung, Template, Styles und Tests beieinander ab. Lagere kleinere Komponenten mit expliziten Inputs und Outputs aus, wenn eine Funktion komplex wird; eine Funktion darf mehrere zusammenarbeitende Komponenten enthalten.

Warum das wichtig ist
Flache Komponentenordner und weit gefasste Anwendungskomponenten verschleiern Zuständigkeiten. Feature-Dienste, die in gemeinsame Darstellung einfließen, oder untere Schichten, die Feature-Komponenten importieren, erzeugen Abhängigkeitszyklen und machen sonst wiederverwendbare Bedienelemente von der gesamten Anwendung abhängig. Die Zusammensetzung von Komponenten sollte Zuständigkeiten deutlicher machen, statt eine große Komponente pro Funktion zu erzwingen.
So erkennen wir das Problem
Vergleiche Zuständigkeit und Importrichtung mit der deklarierten Architektur. `frontend/lint/architecture.config.mjs` von Quality Studio erlaubt core die Nutzung von core und reinen gemeinsamen Hilfsfunktionen. shared verwendet shared und core-Modelle; features verwenden core, shared und ihr eigenes Feature; shell setzt Features zusammen. Beanstande umgekehrte Abhängigkeiten, nicht deklarierte Imports zwischen Features und in einer Komponente angesammelte unabhängige Aufgaben. Beanstande keinen dokumentierten alternativen Aufbau und keine auf ihre Aufgabe konzentrierte Kindkomponente allein deshalb, weil ein Feature mehrere Komponenten enthält.

Schweregrad: mittel. Deterministische Zuordnungen: quality-architecture/layer-imports

Beispiele

Passendes Beispiel

frontend/src/app/
  core/api/quality-api.ts
  shared/ui/empty-state/empty-state.ts
  features/code/editor/editor.ts
  features/code/container-view/container-view.ts
  shell/workbench/workbench.ts

Problematisches Beispiel

// shared/ui/status.ts: reusable presentation now owns a product feature dependency.
import { Editor } from '../../features/code/editor/editor';
QS-NG-004 · Templates deklarativ halten; Listenausdrücke immer mit track versehenAngular · Code · Standardmäßig aktiviert

Templates verwenden die eingebauten Kontrollflussblöcke (`@for`, `@if`, `@empty`) mit einem expliziten `track`-Ausdruck an jedem `@for` und rufen direkt im Template nur einfache, nebenwirkungsfreie Lesezugriffe auf, etwa Signals oder einfache Felder. Nicht triviale Ableitungen gehören in ein `computed()` oder eine benannte Methode der Komponente, nicht als Inline-Ausdruck ins Template.

Warum das wichtig ist
`track` ermöglicht Angular, DOM-Knoten über erneute Rendervorgänge hinweg wiederzuverwenden, statt eine Liste bei jeder Änderung abzubauen und neu aufzubauen. Fehlt es, fällt die Verfolgung stillschweigend auf Objektidentität zurück und hebt den Vorteil von `OnPush` auf. Ableitungen im Template sind für Unit-Tests unsichtbar und werden bei jeder Prüfung neu ausgewertet, während ein `computed()` sowohl testbar ist als auch sein Ergebnis zwischenspeichert.
So erkennen wir das Problem
Suche im Template nach `@for`-Blöcken ohne `track`-Ausdruck, nach `*ngFor` in neuem Code und nach Template-Ausdrücken, die Methoden mit Argumenten aufrufen, per Index auf Arrays zugreifen, filtern, sortieren oder inline Objekte aufbauen. Bindungen, die ein Signal, ein `computed()` oder ein einfaches Feld lesen, sind in Ordnung.

Schweregrad: mittel. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

<!-- frontend/src/app/review-panel/review-panel.html -->
@for (finding of visibleFindings(); track finding.fingerprint ?? finding.id) {
  <button class="finding-card" (click)="selectFindingLocation(finding)">...</button>
} @empty {
  <div class="empty-findings">No findings match these filters.</div>
}

Problematisches Beispiel

<!-- missing track, and re-sorting inline on every check -->
@for (finding of findings().slice().sort((a, b) => a.severity.localeCompare(b.severity))) {
  <button class="finding-card">...</button>
}
QS-NG-005 · Standardmäßig OnPush mit Signal-Zustand verwendenAngular · Code · Standardmäßig aktiviert

Jede Komponente setzt `changeDetection: ChangeDetectionStrategy.OnPush` und steuert ihr Template über `signal`/`computed`/`input`/`output`, nicht über mutierte einfache Felder oder manuelle Subscriptions, die außerhalb der Angular-Auslöser für Änderungserkennung in Komponenteneigenschaften schreiben.

Warum das wichtig ist
Jede Feature-Komponente in `frontend/src/app` deklariert bereits `OnPush` und liest Zustand über Signals. Dadurch bleiben `explorer`, `review-panel` und `usage-history` bei großen Bäumen und Ergebnismengen schnell. Eine Komponente, die zu `Default` wechselt oder ein Feld innerhalb eines manuellen `subscribe()` verändert, führt stillschweigend erneute Prüfungen ganzer Teilbäume ein. Unter benachbarten `OnPush`-Komponenten kann sie sogar überhaupt nicht gerendert werden, wenn sie darauf vertraut, dass die allgemeine Änderungserkennung ihre Mutationen aufgreift.
So erkennen wir das Problem
Prüfe den `@Component`-Dekorator auf `changeDetection: ChangeDetectionStrategy.OnPush`. Suche anschließend nach Zustand, der außerhalb von Signals geschrieben wird: einfache veränderliche Felder, denen in `subscribe()`-Callbacks, `setTimeout`- oder Ereignishandlern Werte zugewiesen werden, oder `ChangeDetectorRef.detectChanges()`-Aufrufe, die nur solche Mutationen sichtbar machen sollen.

Schweregrad: mittel. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// frontend/src/app/review-panel/review-panel.ts
@Component({
  selector: 'app-review-panel',
  changeDetection: ChangeDetectionStrategy.OnPush,
  // ...
})
export class ReviewPanel {
  readonly severityFilter = signal<string>('all');
  readonly visibleFindings = computed(() => /* derive from signals */ []);
}

Problematisches Beispiel

@Component({ selector: 'app-widget' /* no changeDetection: OnPush */ })
export class Widget implements OnInit {
  findings: Finding[] = [];
  ngOnInit() {
    this.api.findings$.subscribe(value => { this.findings = value; }); // mutates a plain field
  }
}
QS-NG-006 · postMessage an eine bekannte Origin adressieren und nur deklarierte Felder sendenAngular · Sicherheit · Standardmäßig aktiviert

`postMessage` benennt die erwartete Empfänger-Origin aus einer vertrauenswürdigen Konfiguration oder einem validierten Handshake bei der Einbettung; `'*'` ist keine Ziel-Origin. Die Nutzlast enthält ausschließlich die im Vertrag deklarierten Felder, niemals ein vollständiges `location.href`. Jeder Listener für eingehende `message`-Ereignisse prüft `event.origin`, bevor er `event.data` liest, und validiert die Struktur der gelesenen Daten.

Warum das wichtig ist
`reportUrlPreviewNavigation` typisiert seine Ziel-Origin als Literal `'*'`, sodass kein Aufrufer eine echte Origin übergeben kann. `app.ts` reicht sie direkt an `window.parent.postMessage` weiter: Jede Origin, die die Vorschau einbetten kann, erhält ihre Navigationsnachrichten. Die Nutzlast verschärft das Problem: `url.href` enthält Origin, Fragment, Benutzerinformationen und alle bereits vorhandenen Abfrageparameter. Dadurch gelangt ein bereits in der Adressleiste stehendes Token zusammen mit den drei tatsächlich im Vertrag deklarierten Feldern an diesen unbekannten übergeordneten Frame.
So erkennen wir das Problem
Suche nach `postMessage` mit `'*'` oder einer Variablen, die nie mit einer Positivliste verglichen wird, nach Nutzlasten aus `location.href`, `document.URL` oder einem vollständigen `URL`-Objekt und nach `addEventListener('message', ...)`-Handlern, die `event.data` lesen, bevor sie `event.origin` prüfen. Ein dedizierter Web-Worker-Kanal hat keine Origin zu prüfen; seine Nachrichtenstruktur muss trotzdem validiert werden.

Schweregrad: hoch. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

const parentOrigin = environment.embedOrigin;            // trusted configuration, not the message
if (parentOrigin) {
  window.parent.postMessage(
    { type: 'qs.url-preview.navigate', repo, path, kind },   // only the declared fields
    parentOrigin);
}

Problematisches Beispiel

// frontend/src/app/url-preview-embed.ts: any embedding origin receives this, href and all
postToParent({ type: 'qs.url-preview.navigate', url: environment.href }, '*');
QS-NG-007 · Text als Text darstellen; Angulars Sanitizer niemals umgehenAngular · Sicherheit · Standardmäßig aktiviert

Inhalt, der nicht aus dem eigenen Template dieser Komponente stammt, etwa Repository-Quellcode, Modelltext, ein Finding-Titel oder irgendein abgerufener Inhalt, wird durch Interpolation oder Property-Binding dargestellt. Kein `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `document.write`, `eval` oder `DomSanitizer.bypassSecurityTrust*`. Ist Markup tatsächlich erforderlich, erzeuge Elemente aus einem geparsten Modell und nicht aus einer Zeichenfolge.

Warum das wichtig ist
Unter `frontend/src` gibt es derzeit weder `innerHTML` noch eine Umgehung des Sanitizers: Der Editor stellt jede Zeile als `{{ segment.text }}` innerhalb von spans dar, das Review-Panel rendert vom Modell verfasste Beschreibungen als `<p>{{ finding.description }}</p>`, und der HTML-Bericht codiert vor dem Schreiben mit `WebUtility.HtmlEncode`. Diese Eigenschaft sollte erhalten bleiben, denn bei den betroffenen Zeichenfolgen handelt es sich gerade um die nicht vertrauenswürdigen Inhalte: vom Repository kontrollierten Quellcode und vom Modell verfassten Text.
So erkennen wir das Problem
Suche in der Komponente und ihrem Template nach `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `bypassSecurityTrust`, `DomSanitizer`, `document.write`, `eval(` und `new Function(`. Eine Bindung über `[innerText]` oder `[textContent]` ist kein Verstoß, ebenso wenig `[attr.*]` für einen Wert, den der Sanitizer weiterhin prüft.

Schweregrad: hoch. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

<!-- frontend/src/app/editor/editor.html -->
<code>@for (segment of segmentedLine(row.number, row.text, row.findings); track $index) {
  <span [class]="segmentClass(segment)" [attr.aria-label]="segmentAriaLabel(segment, row.number)">{{ segment.text }}</span>
}</code>

Problematisches Beispiel

<!-- The description is model-authored prose; this hands it to the HTML parser. -->
<p [innerHTML]="sanitizer.bypassSecurityTrustHtml(finding.description)"></p>
QS-NG-008 · Zugangsdaten aus clientseitig gehaltenem Zustand heraushaltenAngular · Sicherheit · Standardmäßig aktiviert

`localStorage`, `sessionStorage`, IndexedDB, per Skript geschriebene Cookies, die URL und der Komponentenzustand enthalten ausschließlich nicht geheime Darstellungsdaten, etwa ein Theme, ein Layout oder einen ausgewählten Filter. Tokens, Zugangsdaten und Autorisierungsheader werden im Browser weder aufgebaut noch gespeichert oder protokolliert.

Warum das wichtig ist
Der Angular-Client speichert genau zwei Dinge, `'qs-theme'` und den Layout-Schlüssel, und erzeugt keinerlei `Authorization`-Header. Die Authentifizierung erfolgt über ein Bearer-Token und eine Client-ID, die `ApiSecurity` auf dem Server durchsetzt. Deshalb sind Informationen, die über den Browser abfließen, etwa durch ein gemeinsam genutztes Gerät, ein Konsolenlog oder einen einbettenden übergeordneten Frame, keine Zugangsdaten. Clientseitiger Speicher kann von jedem Skript derselben Origin gelesen werden und überdauert die Sitzung. Ein dort abgelegtes Token bleibt daher länger bestehen als der Anlass seiner Ausstellung.
So erkennen wir das Problem
Prüfe jeden `localStorage`/`sessionStorage`-Schlüssel und jeden Wert, der einem Header, einem Abfrageparameter oder einem protokollierten Objekt zugewiesen wird, auf Namen wie token, key, secret, password oder authorization. Suche außerdem nach Zugangsdaten, die über einen Routenparameter oder ein Fragment eintreffen. Eine nicht geheime Repository-ID oder Ansichtspräferenz im Speicher ist kein Verstoß.

Schweregrad: hoch. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// frontend/src/app/app.ts
localStorage.setItem('qs-theme', theme);          // presentation state only
localStorage.setItem(LAYOUT_STORAGE_KEY, JSON.stringify(sizes));

Problematisches Beispiel

localStorage.setItem('qs-api-token', token);      // readable by any script on this origin, forever
this.http.get(url, { headers: { Authorization: `Bearer ${token}` } });
QS-NG-009 · URL-Bestandteile codieren und niemals zu einer URL aus Daten navigierenAngular · Sicherheit · Standardmäßig aktiviert

Pfadsegmente werden mit `encodeURIComponent` und Abfragewerte mit `HttpParams` oder `URLSearchParams` verarbeitet. Eine URL wird niemals durch Aneinanderhängen von Rohwerten zusammengesetzt. Ein Navigationsziel, etwa `[href]`, `router.navigateByUrl`, `window.open` oder eine Zuweisung an `location`, ist eine von dieser Anwendung berechnete Route derselben Origin, niemals eine URL aus einer Antwort, einem Abfrageparameter oder einer Nachricht.

Warum das wichtig ist
`quality-api.ts` escapet jede ID, die es in einen Pfad einfügt, und übergibt jeden Wert als Parameter. So bleibt eine Repository- oder Finding-ID mit Schrägstrich oder kaufmännischem Und ein Segment, statt ein neuer Pfad oder Parameter zu werden, und jede Anfrage bleibt relativ und auf derselben Origin. Der Navigationsteil macht aus einem Formatierungsfehler eine offene Weiterleitung: Ein aus Daten übernommenes Linkziel sendet den Nutzer samt Referrer dorthin, wo diese Daten es vorgeben.
So erkennen wir das Problem
Suche nach Template-Zeichenfolgen, die eine ID oder einen Nutzerwert ohne `encodeURIComponent` in eine URL interpolieren, nach Abfragezeichenfolgen, die mit `+` oder Interpolation statt mit `HttpParams` entstehen, und nach `[href]`, `window.open`, `location.href =` oder `navigateByUrl`, die an einen Wert aus einer Antwort, einem Routenparameter oder einer Nachricht gebunden sind. Eine aus Konstanten gebildete relative URL ist kein Verstoß.

Schweregrad: mittel. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// frontend/src/app/quality-api.ts
private repositoryApiBase(): string {
  return this.legacyApi ? '/api' : `/api/repos/${encodeURIComponent(repositoryId)}`;
}
const file = await firstValueFrom(
  this.http.get<FileDocument>(`${this.repositoryApiBase()}/file`, { params: { path } }));

Problematisches Beispiel

// An id with a slash becomes a new path segment; the link target comes from the response.
this.http.get(`/api/repos/${repositoryId}/file?path=` + path);
window.open(response.externalUrl, '_blank');
QS-NG-010 · Template-Sammlungen in computed Signals statt in Template-Aufrufen ableitenAngular · Performance · Standardmäßig aktiviert

Eine Sammlung, die ein Template durchläuft oder deren Größe es ermittelt, ist ein `computed()`-Signal. Ein Template-Ausdruck liest ein Signal oder ein einfaches Feld. Er ruft keine Methode auf, die filtert, abbildet, sortiert, Ausschnitte erzeugt, Reihenfolgen umkehrt oder anderweitig Speicher anlegt. Dieselbe Ableitung wird außerdem nicht zweimal in einem Template geschrieben.

Warum das wichtig ist
`review-panel.ts` und `review-actions.ts` stellen `scopeRuns`, `comparableRuns`, `runFindings`, `filteredModels` und `fileCount` bereits als `computed()` bereit. Sie werden daher bei geänderten Eingaben neu berechnet, nicht bei jeder Prüfung. Wo das nicht eingehalten wurde, sind die Kosten sichtbar: `review-actions.html` ruft `runFiles(run, states)` sechsmal pro Zyklus der Änderungserkennung auf, zweimal pro Zeile, einmal für die Längenprüfung und einmal für die Schleife. Jeder Aufruf liefert ein neues Array. Dadurch gleicht `track file.path` mit zuvor unbekannten Objekten ab, und die Liste wird neu aufgebaut.
So erkennen wir das Problem
Lies jeden `{{ }}`-, `@if`- und `@for`-Ausdruck: Ein Methodenaufruf mit Argumenten, `.filter(`, `.map(`, `.slice(`, `.sort(`, `.reverse(`, ein Array- oder Objektliteral oder derselbe Aufruf zweimal in einem Template ist ein Verstoß. Das Lesen eines Signals, eines `computed()` oder eines einfachen Felds ist keiner, ebenso wenig ein reiner Formatierungsaufruf für einen Skalar.

Schweregrad: mittel. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// frontend/src/app/review-panel/review-panel.ts
readonly scopeRuns = computed(() => this.api.runs().filter(run => run.scope === this.scope()));
readonly runFindings = computed(() => this.indexFindings(this.selectedRun()));

Problematisches Beispiel

<!-- runFiles() allocates a new array on every call, and this calls it twice per line -->
@if (runFiles(run, states).length) {
  @for (file of runFiles(run, states); track file.path) { <li>{{ file.path }}</li> }
}
QS-NG-011 · Lange Listen und Dateien in einem begrenzten Ausschnitt darstellenAngular · Performance · Standardmäßig aktiviert

Eine Ansicht von Daten, deren Umfang das Repository bestimmt, etwa ein Dateibaum, eine Quelldatei, eine Finding-Liste oder ein Laufverlauf, rendert einen festen Zeilenausschnitt mit kleinem zusätzlichem Vorlauf außerhalb des sichtbaren Bereichs. Seine Größe richtet sich nach dem sichtbaren Bereich und nicht nach der Datenmenge. Indexiere Daten einmal bei ihrem Eintreffen, statt sie pro gerenderter Zeile zu durchsuchen.

Warum das wichtig ist
`explorer.ts` flacht den Baum in einem `computed()` ab und schneidet daraus einen Ausschnitt aus. Der Editor hält unabhängig von der Dateilänge ein Fenster von 80 Zeilen einschließlich des zusätzlichen Vorlaufs vor. Damit bleibt der Übergang in einem Repository mit 3.450 erfassten Dateien unter 100 ms. Die Alternative ist nicht nur um einen konstanten Betrag langsamer: DOM-Knoten, Bindungen und Arbeit pro Zeile wachsen mit der Datenmenge. Schon das erste problematische Repository macht so einen nutzbaren Bereich unbenutzbar.
So erkennen wir das Problem
Suche bei jedem `@for` über Daten, deren Umfang das Repository bestimmt, nach einem aus Scrollposition und Zeilenhöhe berechneten Ausschnitt. Prüfe außerdem, ob Ausdrücke pro Zeile eine Sammlung durchsuchen oder filtern, statt einen einmal aufgebauten Index zu lesen. Eine durch eine kleine Konstante begrenzte Liste, etwa Review-Arten, Schweregrade oder Adapter, ist kein Verstoß.

Schweregrad: mittel. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// frontend/src/app/explorer/explorer.ts
readonly treeRows = computed(() => flattenTree(this.api.tree(), this.expanded()));
readonly visibleRows = computed(() =>
  this.filteredRows().slice(start, start + count).map((node, i) => ({ node, top: (start + i) * ROW_HEIGHT })));

Problematisches Beispiel

<!-- Every row of every file, and a scan of all findings per row -->
@for (row of allLines(); track row.number) {
  <span>{{ findings().filter(f => f.line === row.number).length }}</span>
}
QS-NG-012 · Zusätzliche Größe aus dem initialen Bundle heraushaltenAngular · Performance · Standardmäßig aktiviert

Eine Funktion, die auf dem ersten Bildschirm nicht benötigt wird, lädt als verzögerter Chunk über `@defer` oder eine Lazy Route statt über einen sofort geladenen Import. Eine neue Abhängigkeit wird vor ihrer Aufnahme gegen die Produktionsbudgets in `frontend/angular.json` abgewogen. Komponentenstylesheets bleiben innerhalb des Budgets pro Stylesheet, statt immer weitere einmalige Blöcke anzusammeln.

Warum das wichtig ist
Der Produktionsbuild meldet einen Fehler, keine Warnung, wenn das initiale Bundle seine Grenze überschreitet: Ein Build ist bereits mit 494,94 kB am Fehlerbudget von 480 kB gescheitert. Der sofortige Import einer selten verwendeten Funktion verschlechtert die Anwendung daher nicht allmählich, sondern stoppt den Build für die Person, die als Nächste committet. Die Angriffsmatrix zeigt die vorgesehene Form: ein verzögert geladener Chunk von 17 kB, der den ersten Bildschirm nichts kostet.
So erkennen wir das Problem
Prüfe, ob eine neu importierte Komponente, Bibliothek oder Symbolsammlung von der initialen Route aus erreichbar ist und ob eine umfangreiche Funktion mit `@defer` oder einer Lazy Route deklariert wird. Vergleiche die zusätzliche Stylesheet-Größe mit dem `anyComponentStyle`-Budget in `frontend/angular.json`. Dort stehen die verbindlichen Zahlen, nicht in möglicherweise veralteten Beschreibungstexten.

Schweregrad: mittel. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

<!-- A rarely opened, heavy pane costs the first screen nothing -->
@defer (on interaction) {
  <app-attack-coverage />
} @placeholder {
  <button class="pane-header">Attack coverage</button>
}

Problematisches Beispiel

// Eagerly imported into the shell, so it is in the initial bundle for everyone
import { AttackCoverage } from './attack-coverage/attack-coverage';
import * as everything from 'some-charting-library';
QS-NG-013 · Aufwendige Arbeit in begrenzten, abbrechbaren Abschnitten außerhalb des Hauptthreads ausführenAngular · Performance · Standardmäßig aktiviert

Arbeit, deren Kosten mit den Repository-Daten wachsen, etwa das Tokenisieren einer Datei, das Ermitteln von Unterschieden oder das Indexieren, läuft in einem Worker. Sie beginnt nach dem ersten Zeichnen der Oberfläche, arbeitet in begrenzten Abschnitten und wird abgebrochen, wenn sich ihr Prüfobjekt ändert. Pro Prüfobjekt läuft höchstens ein solcher Auftrag gleichzeitig; ein überholter Auftrag wird abgebrochen und nicht abgewartet.

Warum das wichtig ist
Die Syntaxhervorhebung ganzer Dateien auf dem Hauptthread ist in dieser Codebasis aus einem messbaren Grund untersagt: Die Tokenisierung läuft in einem abbrechbaren Worker mit genau einem gleichzeitigen Auftrag, in Abschnitten von 200 Zeilen und mit einer Obergrenze von 200 kB. Dadurch bleibt das Öffnen einer großen Datei innerhalb des Übergangsbudgets von unter 100 ms. Ohne Aufteilung und Abbruchmöglichkeiten reiht das Durchblättern von fünf Dateien fünf vollständige Tokenisierungen ein, und der Bereich reagiert auf die sechste nicht mehr.
So erkennen wir das Problem
Suche nach Schleifen zum Parsen, Tokenisieren oder Ermitteln von Unterschieden über ganze Dateiinhalte auf dem Hauptthread, nach `await` für solche Arbeit direkt in der Initialisierung einer Komponente und nach Worker-Aufrufen ohne Abbruch bei geänderten Eingaben. Arbeit, die durch eine kleine Konstante begrenzt ist oder einmalig beim Start ausgeführt wird, ist kein Verstoß.

Schweregrad: mittel. Deterministische Zuordnungen: Bewertung durch den Reviewer; keine deterministische Zuordnung.

Beispiele

Passendes Beispiel

// One job per subject: the previous one is cancelled rather than awaited.
this.pending?.cancel();
this.pending = this.highlighter.tokenize(path, text, { chunkLines: 200, maxBytes: 200_000 });

Problematisches Beispiel

ngOnInit() {
  // Whole-file tokenization on the main thread, on every open, uncancellable
  this.tokens = tokenizeEveryLine(this.file.text);
}

Qualitätsdomänen: Was vorhanden und was geplant ist

Der Domänenkatalog verbindet Qualitätsfragen mit Belegen und Grenzen. Der Umsetzungsstand ist ausdrücklich angegeben: Die sechs vorhandenen Produktmetriken stehen bereit, benannte Review-Regeln benötigen ein passendes Review, geplante Prüfungen sind Leitlinien und keine ausgeführten Messungen.

13 Domänen · 23 Prüfungen · Domänenkatalog 1.0.0

Architektur

Klare Zuständigkeiten machen Änderungsfolgen verständlicher.

Belege
Architekturvertrag des Repositorys, Quellcodeaufbau und Abhängigkeitsrichtung.
Grenzen
Verzeichnisprüfungen belegen keinen inhaltlichen Zusammenhalt von Komponenten.

Festgelegte Grenzen und Zuständigkeiten

Verfügbare Review-Regel · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Änderungen sollen zur festgelegten Architektur passen.
Belege
Vertrag gemeinsam mit geänderten Pfaden, Imports und Dienstregistrierungen lesen.
So ist das Ergebnis zu verstehen
Vorhandene Regeln unterstützen konkrete Findings zu Verträgen und Abhängigkeiten.
Grenzen
Ein abweichender dokumentierter Aufbau ist allein kein Fehler. Es wird kein Architekturpunktwert berechnet.

Dokumentierte Anwendbarkeit: Geltungsbereich: Projekt. Keine Bedingung für Eigenschaften

Korrektheit

Ein Review soll beobachtbare Fehler erklären.

Belege
Auslöser, Zustandsübergänge, Fundstellen und reproduziertes Verhalten.
Grenzen
Eine Review-Bewertung ist ein Urteil, kein Korrektheitsbeweis.

Verhalten und gleichzeitige Zustandsänderungen

Verfügbare Review-Regel · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Fehler benötigen einen plausiblen Auslöser und eine konkrete Folge.
Belege
Aufrufer, Abbruch, veraltete Antworten und passende Regressionsfälle nachvollziehen.
So ist das Ergebnis zu verstehen
Die verlinkten Regeln leiten zu quellcodegestützten Findings an.
Grenzen
Ungeprüfte Annahmen bleiben unsicher; es entsteht kein automatisches Korrektheitsurteil.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Keine Bedingung für Eigenschaften

Gespeicherte und effektive Review-Bewertung

Vorhandene Produktmetrik · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Die angezeigte Bewertung benötigt ihren Review- und Einstufungskontext.
Belege
Gespeicherter Review-Punktwert, Finding-Zustände und Sicherheitsurteil.
So ist das Ergebnis zu verstehen
Die bestehende Bewertungsdefinition beschreibt Gewichte, Obergrenzen und Rundung.
Grenzen
Die Einstufung kann den Wert ohne Codeänderung verändern. Er ist keine gemessene Wahrscheinlichkeit.

Dokumentierte Anwendbarkeit: Geltungsbereich: Projekt. Keine Bedingung für Eigenschaften

Tests

Tests und gespeicherte Reviews liefern unterschiedliche Nachweise.

Belege
Verhaltensprüfungen, Abdeckungsberichte und Review-Dokumente.
Grenzen
Anzahlen zeigen nicht, ob wichtiges Verhalten durch Assertions geprüft wurde.

Unabhängige Verhaltenstests

Verfügbare Review-Regel · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Ein Regressionstest soll beim nachgewiesenen Problem fehlschlagen.
Belege
Testeinstellungen, isolierte Testumgebungen und Ergebnisprüfungen.
So ist das Ergebnis zu verstehen
Vorhandene Regeln beurteilen Testabsicht und Unabhängigkeit.
Grenzen
Eine höhere Testanzahl allein ist kein stärkerer Nachweis.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Keine Bedingung für Eigenschaften

Gemeldete Test-Zeilenabdeckung

Vorhandene Produktmetrik · Prüfmethode: Messung

Warum das wichtig ist
Ausgeführte Zeilen helfen, prüfenswerte Lücken zu erkennen.
Belege
Unterstützte Abdeckungsberichte und ihr Quellumfang.
So ist das Ergebnis zu verstehen
Die bestehende Metrik meldet durch Tests erreichte ausführbare Zeilen.
Grenzen
Fehlende Berichte sind nicht verfügbar. Zeilenausführung belegt weder Assertions noch Zweigabdeckung.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Keine Bedingung für Eigenschaften

Gespeicherte Review-Abdeckung

Vorhandene Produktmetrik · Prüfmethode: Messung

Warum das wichtig ist
Review-Nachweise sollen ihren Abdeckungsumfang sichtbar machen.
Belege
Inventar reviewfähiger Dateien und zugeordnete Review-Dokumente.
So ist das Ergebnis zu verstehen
Die bestehende Metrik zählt Dateien mit einem Dokument beliebiger Review-Art.
Grenzen
Veraltete Dokumente zählen mit; dies misst weder Testabdeckung noch Aktualität.

Dokumentierte Anwendbarkeit: Geltungsbereich: Projekt. Keine Bedingung für Eigenschaften

Sicherheit

Eingaben und Prozessfähigkeiten überschreiten Vertrauensgrenzen.

Belege
Eingabevalidierung, Pfadbegrenzung, Prozessargumente und Ausgabestellen.
Grenzen
Diese Regeln ersetzen weder eine ASVS-Prüfung noch eine vollständige Sicherheitsbewertung.

Eingabe- und Ausführungsgrenzen

Verfügbare Review-Regel · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Nicht vertrauenswürdige Daten dürfen keine Ausführungs- oder Dateisystemrechte erhalten.
Belege
Anfrage- und Repository-Daten bis zu Parsing-, Dateisystem-, Prozess- und DOM-Operationen verfolgen.
So ist das Ergebnis zu verstehen
Vorhandene Code- und Sicherheitsregeln decken diese konkreten Muster ab.
Grenzen
Berechtigungsdesign und Erreichbarkeit im Betrieb benötigen weiterhin eine kontextbezogene Prüfung.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Keine Bedingung für Eigenschaften

Datenschutz

Personenbezogene Daten benötigen klare Zwecke und kontrollierte Verarbeitung.

Belege
Datenflüsse, Browserspeicher, Logs und Aufbewahrungsentscheidungen.
Grenzen
Regeln zum Umgang mit Geheimnissen decken nur einen Teil des Datenschutzes ab. Es entsteht kein Compliance-Punktwert.

Offenlegung durch Geheimnisse und Clientzustand

Verfügbare Review-Regel · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Gespeicherte Ausgaben und Browserzustand können sensible Werte offenlegen.
Belege
Werte bis in Logs, Antworten, Dokumente und Clientspeicher verfolgen.
So ist das Ergebnis zu verstehen
Die verlinkten Regeln behandeln gezielt Geheimnisse und Zugangsdaten im Speicher.
Grenzen
Sie belegen weder Zweckbindung noch vollständige Löschung oder Rechtskonformität.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Mindestens eine davon: personal-data, authenticated

Lebenszyklus personenbezogener Daten prüfen

Geplante Prüfung · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Erhebung, Aufbewahrung und Löschung benötigen eine durchgängige Beschreibung.
Belege
Geplante Nachweise: Dateninventar, Zweck, Aufbewahrung und Tests der Löschpfade.
So ist das Ergebnis zu verstehen
Dieser Katalog beschreibt eine künftige Prüfung; es ist keine eigene Implementierung verknüpft.
Grenzen
Es gibt keine automatische Datenschutzmetrik oder Vollständigkeitsbehauptung.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Alle davon: personal-data

Zahlungen

Wiederholungen und Teilausfälle dürfen finanzielle Wirkungen nicht verdoppeln.

Belege
Zahlungszustände, Idempotenznachweise und Abgleichfälle.
Grenzen
Es gibt keine eigene implementierte Zahlungsprüfung oder Zahlungsbewertung.

Zahlungswirkungen und sichere Wiederholung

Geplante Prüfung · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Eine wiederholte Anfrage benötigt eine definierte Beziehung zur ursprünglichen Zahlung.
Belege
Geplante Nachweise: Providervertrag, Idempotenzschlüssel, Beträge, Währungen und Tests doppelter Zustellung.
So ist das Ergebnis zu verstehen
Providerspezifische Semantik muss vor der Implementierung einer Prüfung bewertet werden.
Grenzen
Die Stripe-Quelle veranschaulicht einen Providervertrag, keine allgemeine Zahlungsgarantie.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Alle davon: payment-api

Webhook-Authentizität und Zustellreihenfolge

Geplante Prüfung · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Ein Callback muss authentifiziert sein, bevor doppelte oder verspätete Zustellung den Zahlungszustand verändert.
Belege
Geplante Nachweise: Signaturprüfung am ursprünglichen Inhalt, Umgang mit Schlüsselrotation, Ereignisidentität, doppelte Zustellung und Tests vertauschter Zustandsübergänge.
So ist das Ergebnis zu verstehen
Den Signatur-, Wiederholungs- und Reihenfolgevertrag des gewählten Providers prüfen; wiederholte Ereignisse ohne doppelte Wirkung und verspätete Ereignisse ohne Zustandsrückschritt testen.
Grenzen
Der Stripe-Vertrag ist ein Beispiel und kein universelles Webhook-Protokoll. Dieser Eintrag implementiert weder Providerintegrationstests noch Live-Zahlungen oder einen Authentizitätsscanner.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Alle davon: payment-api

Zuverlässigkeit

Arbeit soll bei ausfallenden Abhängigkeiten begrenzt bleiben.

Belege
Abbruch, Zeitlimits, Cache-Schlüssel, Fehlerbehebung und Betriebsbeobachtungen.
Grenzen
Ein Code-Review misst keine Verfügbarkeit im Produktivbetrieb.

Begrenzte Wartezeiten und Ressourcennutzung

Verfügbare Review-Regel · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Eine hängende Operation soll unabhängige Arbeit nicht unbegrenzt aufhalten.
Belege
Abbruch, Zuständigkeit für Zeitlimits, Cache-Grenzen und Warteschlangenverhalten prüfen.
So ist das Ergebnis zu verstehen
Vorhandene Regeln unterstützen Findings zu diesem Verhalten im Quellcode.
Grenzen
Ein erfolgreicher Test oder begrenzter Aufruf belegt kein Service-Level-Ziel.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Keine Bedingung für Eigenschaften

Beobachtungen zu Service-Level-Zielen

Geplante Prüfung · Prüfmethode: Messung

Warum das wichtig ist
Betriebliche Zuverlässigkeit benötigt Beobachtungen über ein definiertes Zeitfenster.
Belege
Geplante Nachweise: Anfrageergebnisse, Latenzverteilungen und dokumentierte Beobachtungszeiträume.
So ist das Ergebnis zu verstehen
Dieser Katalog liefert derzeit keine Metrik zu betrieblicher Verfügbarkeit oder Latenz.
Grenzen
Eine künftige Messung benötigt Definitionen für Arbeitslast und Aggregation.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Mindestens eine davon: persistent-data, realtime

Performance

Interaktive Arbeit benötigt eine definierte Arbeitslast und Ressourcengrenze.

Belege
Gerenderte Szenarien, Ablaufdaten, Bundle-Ausgaben und Feldbeobachtungen.
Grenzen
Quellcodemuster und Laborläufe belegen nicht das Erlebnis von Nutzern im Produktivbetrieb.

Begrenzte Darstellung und Hauptthread-Arbeit

Verfügbare Review-Regel · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Arbeit über Repository-Daten soll sich nicht pro dargestellter Zeile oder Anfrage vervielfachen.
Belege
Darstellungsfenster, abgeleiteten Zustand, Worker-Abbruch und wiederholte Scans prüfen.
So ist das Ergebnis zu verstehen
Verlinkte Regeln beschreiben Quellcodemuster; realistische Ablaufmessungen belegen beobachtetes Verhalten.
Grenzen
Diese Prüfungen implementieren keine Erfassung von Web Vitals.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Keine Bedingung für Eigenschaften

Core Web Vitals im Feld

Geplante Prüfung · Prüfmethode: Messung

Warum das wichtig ist
Feldbeobachtungen zeigen Laden, Interaktion und visuelle Stabilität bei tatsächlichen Besuchen.
Belege
Geplante Nachweise: LCP-, INP- und CLS-Beobachtungen am 75. Perzentil mit Gerätekategorie, Erhebungszeitraum und verfügbarer Stichprobenabdeckung.
So ist das Ergebnis zu verstehen
Die referenzierten guten Schwellenwerte sind LCP ≤ 2500 ms, INP ≤ 200 ms und CLS ≤ 0,1. Jede Metrik getrennt für ihre angegebene Gerätekategorie und ihren Zeitraum vergleichen. Hier ist keine Erfassung implementiert.
Grenzen
Fehlende Felddaten sind nicht verfügbar, niemals null oder bestanden. Laborergebnisse dürfen Feldbeobachtungen nicht stillschweigend ersetzen; es wird kein kombinierter Quality-Studio-Performancepunktwert definiert.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Alle davon: html-ui

Review-Priorisierung

Gespeicherte Nachweise helfen bei der Auswahl der nächsten Untersuchung.

Belege
Gespeicherte Bewertungen, Findings, Abdeckung und Änderungshistorie.
Grenzen
Diese Metriken messen keine Laufzeitperformance und sagen keine Ausfälle voraus.

Priorität der Risikoansicht

Vorhandene Produktmetrik · Prüfmethode: Heuristik

Warum das wichtig ist
Review-Aufwand kann an der bestehenden Risikorangfolge ausgerichtet werden.
Belege
Gespeicherte Code-Bewertung, Zeilenabdeckung und Änderungshistorie.
So ist das Ergebnis zu verstehen
Gewichte und Verhalten bei fehlenden Daten stehen in der bestehenden Metrikdefinition.
Grenzen
Dies ist weder eine Ausfallwahrscheinlichkeit noch eine gemessene Antwortzeit.

Dokumentierte Anwendbarkeit: Geltungsbereich: Projekt. Keine Bedingung für Eigenschaften

Gespeicherte Finding-Dichte

Vorhandene Produktmetrik · Prüfmethode: Messung

Warum das wichtig ist
Die Dateigröße gibt Finding-Anzahlen einen definierten Bezugswert.
Belege
Ungelöste gespeicherte Findings und Zeilenanzahlen der Dateien.
So ist das Ergebnis zu verstehen
Die bestehende KLOC-Metrik meldet die Dichte über gespeicherte Review-Arten hinweg.
Grenzen
Die Dichte hängt von Review-Umfang und Aktualität ab; sie ist kein Ausführungsaufwand.

Dokumentierte Anwendbarkeit: Geltungsbereich: Projekt. Keine Bedingung für Eigenschaften

Hotspot-Priorität des Dashboards

Vorhandene Produktmetrik · Prüfmethode: Heuristik

Warum das wichtig ist
Die Änderungshistorie kann Review-Kandidaten ordnen helfen.
Belege
Änderungsanzahlen, Finding-Dichte und gespeicherte Code-Bewertung.
So ist das Ergebnis zu verstehen
Die bestehende Hotspot-Definition und ihre explizite Annahme bei fehlender Bewertung verwenden.
Grenzen
Ein niedriger Rang bedeutet weder Sicherheit noch Aktualität oder schnelle Ausführung.

Dokumentierte Anwendbarkeit: Geltungsbereich: Projekt. Keine Bedingung für Eigenschaften

Barrierefreiheit

Menschen benötigen bedienbare Elemente für unterschiedliche Eingaben und Hilfstechnologien.

Belege
Tastaturwege, Fokusverhalten, Semantik, Beschriftungen und gerenderter Kontrast.
Grenzen
Dieser Katalog enthält keine vollständige Barrierefreiheitsprüfung oder WCAG-Bewertung.

Tastatur- und Semantikprüfung

Geplante Prüfung · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Sichtbare Interaktion benötigt auch sinnvolles Tastaturverhalten und Semantik.
Belege
Geplante Nachweise: Fokusreihenfolge, Namen, Aufklappzustände, Kontrast und Szenarien mit Hilfstechnologien.
So ist das Ergebnis zu verstehen
Die WCAG-Referenz leitet eine künftige eigene Prüfung an; vorhandene UI-Tests bleiben separate Nachweise.
Grenzen
Native Elemente und eine Schriftgrößenuntergrenze allein belegen keine Barrierefreiheitskonformität.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Alle davon: html-ui

Auffindbarkeit in Suchmaschinen

Öffentliche Seiten benötigen bewusst gesetzte Crawling- und Indexierungssignale.

Belege
Gerenderte Seiten, Antwortheader, Canonical- und Sprachlinks, Titel, Links und Sitemap.
Grenzen
Opt-in-Review-Regeln sagen keine Rankings voraus und berechnen keinen SEO-Punktwert.

Suchsignale öffentlicher Seiten

Verfügbare Review-Regel · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Für Suchmaschinen bestimmte Seiten benötigen konsistente Identitäten und auffindbare Links.
Belege
Gerendertes HTML und HTTP-Nachweise mit den vier verlinkten Opt-in-Regeln prüfen.
So ist das Ergebnis zu verstehen
Die Regelaktivierung bleibt im Regelkatalog und in den Repository-Überschreibungen.
Grenzen
Dieser Selektor dient nur der Dokumentation. Hier sind weder automatisches Crawling noch Indexierungsurteil oder SEO-Messung implementiert.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Alle davon: public-facing, html-ui, seo-relevant

Lokalisierung

Sprachwechsel sollen Bedeutung und Navigation erhalten.

Belege
Übersetzte Inhalte, Dokumentsprache, Sprachrouten und Browserpräferenzen.
Grenzen
Sprachmetadaten belegen weder Übersetzungsqualität noch vollständige Sprachabdeckung.

Gleichwertige Inhalte und Bedienung je Sprache

Geplante Prüfung · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Unterstützte Sprachen sollen die vorgesehenen Inhalte und Bedienelemente anbieten.
Belege
Geplante Nachweise: Schlüsselinventare, Sprachwechsel, Textausdehnung und Formatierungsfälle.
So ist das Ergebnis zu verstehen
Dieser Eintrag implementiert keine eigene Lokalisierungsregel oder Abdeckungsmetrik.
Grenzen
Gleiche Schlüsselanzahlen belegen keine inhaltliche Gleichwertigkeit der Übersetzungen.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Alle davon: localized

Öffentliche Sprach- und Canonical-Links

Verfügbare Review-Regel · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Sprachvarianten für Suchmaschinen benötigen eine konsistente Seitenidentität.
Belege
Canonical- und Sprachalternativangaben für zusammengehörige Seiten.
So ist das Ergebnis zu verstehen
Die verlinkte Opt-in-SEO-Regel deckt diesen begrenzten Lokalisierungsaspekt ab.
Grenzen
Sie prüft keine Anwendungsübersetzungen, Zahlenformate oder Nutzerpräferenzen.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Alle davon: public-facing, html-ui, seo-relevant, localized

Betrieb

Eine bereitstellbare Komponente benötigt klare Erwartungen an Start und Wiederherstellung.

Belege
Startdefinitionen, Bereitschaftsnachweise, Bereitstellungsprotokolle und Wiederherstellungsübungen.
Grenzen
Projektdeklarationen autorisieren keine Ausführung und belegen keine Betriebsbereitschaft.

Nachweise zu Start, Bereitschaft und Wiederherstellung

Geplante Prüfung · Prüfmethode: Bewertung im Review

Warum das wichtig ist
Ein laufender Prozess und ein für Nutzer bereiter Dienst sind unterschiedliche Beobachtungen.
Belege
Geplante Nachweise: versionierter Startvertrag, Health-Ziel, begrenzter Start, Zuständigkeit für Stop und Wiederherstellungsprotokolle.
So ist das Ergebnis zu verstehen
Dieser Eintrag hält künftige Review-Leitlinien fest; er startet keine Dienste und liest kein YAML.
Grenzen
Kubernetes-Prüfungen veranschaulichen Lebenszyklusunterschiede; sie verlangen keine Nutzung von Kubernetes.

Dokumentierte Anwendbarkeit: Geltungsbereich: Komponente. Alle davon: deployable

Zentrale Domänenquelle · 1.0.0

Quellstand: a9c25aa · Regelkatalog 1.5.0 · Methodik 1.0.0
Zentral gepflegter Regelpool · Zentral gepflegte Methodik