@xenos1996/usat 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +335 -0
- package/USAT.md +596 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +438 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +72 -0
- package/dist/config.js.map +1 -0
- package/dist/detect/index.d.ts +58 -0
- package/dist/detect/index.d.ts.map +1 -0
- package/dist/detect/index.js +329 -0
- package/dist/detect/index.js.map +1 -0
- package/dist/engine/audit.d.ts +21 -0
- package/dist/engine/audit.d.ts.map +1 -0
- package/dist/engine/audit.js +295 -0
- package/dist/engine/audit.js.map +1 -0
- package/dist/engine/diff.d.ts +24 -0
- package/dist/engine/diff.d.ts.map +1 -0
- package/dist/engine/diff.js +136 -0
- package/dist/engine/diff.js.map +1 -0
- package/dist/engine/evaluate.d.ts +19 -0
- package/dist/engine/evaluate.d.ts.map +1 -0
- package/dist/engine/evaluate.js +377 -0
- package/dist/engine/evaluate.js.map +1 -0
- package/dist/engine/gate.d.ts +19 -0
- package/dist/engine/gate.d.ts.map +1 -0
- package/dist/engine/gate.js +47 -0
- package/dist/engine/gate.js.map +1 -0
- package/dist/engine/loader.d.ts +22 -0
- package/dist/engine/loader.d.ts.map +1 -0
- package/dist/engine/loader.js +360 -0
- package/dist/engine/loader.js.map +1 -0
- package/dist/engine/maturity.d.ts +20 -0
- package/dist/engine/maturity.d.ts.map +1 -0
- package/dist/engine/maturity.js +157 -0
- package/dist/engine/maturity.js.map +1 -0
- package/dist/engine/score.d.ts +19 -0
- package/dist/engine/score.d.ts.map +1 -0
- package/dist/engine/score.js +141 -0
- package/dist/engine/score.js.map +1 -0
- package/dist/engine/sections.d.ts +14 -0
- package/dist/engine/sections.d.ts.map +1 -0
- package/dist/engine/sections.js +50 -0
- package/dist/engine/sections.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/report/markdown.d.ts +16 -0
- package/dist/report/markdown.d.ts.map +1 -0
- package/dist/report/markdown.js +488 -0
- package/dist/report/markdown.js.map +1 -0
- package/dist/types.d.ts +282 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/dist/util/glob.d.ts +11 -0
- package/dist/util/glob.d.ts.map +1 -0
- package/dist/util/glob.js +86 -0
- package/dist/util/glob.js.map +1 -0
- package/dist/util/project.d.ts +72 -0
- package/dist/util/project.d.ts.map +1 -0
- package/dist/util/project.js +586 -0
- package/dist/util/project.js.map +1 -0
- package/dist/util/yaml.d.ts +28 -0
- package/dist/util/yaml.d.ts.map +1 -0
- package/dist/util/yaml.js +48 -0
- package/dist/util/yaml.js.map +1 -0
- package/docs/ARCHITECTURE.md +119 -0
- package/docs/README.md +27 -0
- package/docs/adr/0001-record-architecture-decisions.md +35 -0
- package/docs/adr/0002-typescript-engine-over-python.md +49 -0
- package/docs/adr/0003-rules-are-data-not-code.md +50 -0
- package/docs/adr/0004-markdown-only-output.md +41 -0
- package/docs/adr/0005-maturity-dampens-severity.md +56 -0
- package/docs/adr/0006-severity-status-two-axes.md +48 -0
- package/docs/adr/0007-suppressions-are-visible-and-expiring.md +46 -0
- package/docs/adr/0008-report-trailer-and-diff.md +48 -0
- package/docs/adr/0009-fail-closed-on-malformed-input.md +56 -0
- package/docs/adr/0010-detector-design.md +54 -0
- package/docs/adr/0011-coexist-with-deep-scanners.md +52 -0
- package/docs/adr/README.md +32 -0
- package/docs/agent-integration.md +129 -0
- package/docs/ci-integration.md +163 -0
- package/docs/concepts.md +196 -0
- package/docs/configuration.md +105 -0
- package/docs/detectors.md +186 -0
- package/docs/getting-started.md +99 -0
- package/docs/maturity-profiles.md +156 -0
- package/docs/reference/api.md +193 -0
- package/docs/reference/cli.md +412 -0
- package/docs/rule-packs.md +278 -0
- package/docs/standards-mapping.md +171 -0
- package/package.json +96 -0
- package/rules/core/architecture.yaml +190 -0
- package/rules/core/cicd.yaml +212 -0
- package/rules/core/code-quality.yaml +261 -0
- package/rules/core/dependencies.yaml +132 -0
- package/rules/core/documentation.yaml +119 -0
- package/rules/core/future-readiness.yaml +91 -0
- package/rules/core/release.yaml +131 -0
- package/rules/core/repo.yaml +303 -0
- package/rules/core/security.yaml +494 -0
- package/rules/core/supply-chain.yaml +357 -0
- package/rules/core/testing.yaml +260 -0
- package/rules/detectors.yaml +2255 -0
- package/rules/index.yaml +41 -0
- package/rules/profiles/maturity.yaml +135 -0
- package/rules/stacks/ai-era.yaml +193 -0
- package/rules/stacks/api-backend.yaml +121 -0
- package/rules/stacks/cli.yaml +105 -0
- package/rules/stacks/compliance.yaml +157 -0
- package/rules/stacks/containers.yaml +124 -0
- package/rules/stacks/data.yaml +165 -0
- package/rules/stacks/go.yaml +134 -0
- package/rules/stacks/iac.yaml +128 -0
- package/rules/stacks/jvm.yaml +150 -0
- package/rules/stacks/ml-ai.yaml +140 -0
- package/rules/stacks/mobile.yaml +121 -0
- package/rules/stacks/node-typescript.yaml +166 -0
- package/rules/stacks/python.yaml +175 -0
- package/rules/stacks/rust.yaml +129 -0
- package/rules/stacks/solidity.yaml +139 -0
- package/rules/stacks/web-frontend.yaml +159 -0
- package/templates/AGENTS.audit.md +135 -0
- package/templates/AUDIT_REPORT.md +155 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Configuration — `.usat.yaml`
|
|
2
|
+
|
|
3
|
+
Generate one with `usat init`. Every field is optional; a missing file is fine.
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
version: 1
|
|
7
|
+
|
|
8
|
+
# ── Lifecycle stage ────────────────────────────────────────────────────────
|
|
9
|
+
# Override auto-detection: prototype | mvp | beta | production | legacy
|
|
10
|
+
maturity: production
|
|
11
|
+
|
|
12
|
+
# ── Rule packs ─────────────────────────────────────────────────────────────
|
|
13
|
+
include: [stacks/solidity] # force on, regardless of detection
|
|
14
|
+
exclude: [stacks/mobile] # force off
|
|
15
|
+
|
|
16
|
+
# ── Per-rule overrides ─────────────────────────────────────────────────────
|
|
17
|
+
rules:
|
|
18
|
+
DOC-003:
|
|
19
|
+
severity: LOW
|
|
20
|
+
reason: 'Docs live in Notion, not the repo (decision: ADR-014)'
|
|
21
|
+
SEC-042:
|
|
22
|
+
disabled: true
|
|
23
|
+
reason: 'No user-facing auth in this worker'
|
|
24
|
+
CQ-010:
|
|
25
|
+
weight: 4
|
|
26
|
+
|
|
27
|
+
# ── Accepted risk ──────────────────────────────────────────────────────────
|
|
28
|
+
suppressions:
|
|
29
|
+
- rule: PERF-005
|
|
30
|
+
reason: 'Known N+1 in the admin panel; 40 rows max.'
|
|
31
|
+
until: '2026-12-31'
|
|
32
|
+
|
|
33
|
+
# ── Indexing ───────────────────────────────────────────────────────────────
|
|
34
|
+
ignore: # extra globs, on top of .gitignore + USAT defaults
|
|
35
|
+
- 'generated/**'
|
|
36
|
+
- 'vendor/**'
|
|
37
|
+
|
|
38
|
+
# ── Facts ──────────────────────────────────────────────────────────────────
|
|
39
|
+
facts: # assert what detection could not infer
|
|
40
|
+
- 'has:database'
|
|
41
|
+
- 'platform:server'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Field reference
|
|
45
|
+
|
|
46
|
+
| Field | Type | Effect |
|
|
47
|
+
| --------------------- | ----------------------- | ------------------------------------------------------------------------------ |
|
|
48
|
+
| `version` | `1` | Schema version |
|
|
49
|
+
| `maturity` | stage | Overrides auto-detection; changes dampening and the expected band |
|
|
50
|
+
| `include` | pack ids | Force packs on even when `skip_when` says no |
|
|
51
|
+
| `exclude` | pack ids | Force packs off |
|
|
52
|
+
| `rules.<id>.severity` | severity | Re-grade one rule (must be on the ladder, else ignored with a warning) |
|
|
53
|
+
| `rules.<id>.weight` | number | Change how much it moves the score (finite, ≥ 0, else ignored with a warning) |
|
|
54
|
+
| `rules.<id>.disabled` | bool | Skip entirely (still listed as ➖ SKIPPED) |
|
|
55
|
+
| `rules.<id>.reason` | string | **Required in practice** — an override with no reason is an unaudited decision |
|
|
56
|
+
| `suppressions[]` | `{rule, reason, until}` | Excluded from the score, listed under Accepted Risk |
|
|
57
|
+
| `ignore` | globs | Extra paths to keep out of the index |
|
|
58
|
+
| `facts` | fact strings | Assert detection facts manually (`ns:value`) |
|
|
59
|
+
| `sections` | section ids | Restrict the report to these sections |
|
|
60
|
+
|
|
61
|
+
## What is indexed
|
|
62
|
+
|
|
63
|
+
The file index skips, in order:
|
|
64
|
+
|
|
65
|
+
1. `.git/`
|
|
66
|
+
2. `DEFAULT_IGNORES` — `node_modules/`, `dist/`, `build/`, `.next/`, `venv/`, `target/`, media, archives, binaries
|
|
67
|
+
3. Your `.gitignore`
|
|
68
|
+
4. `ignore:` from `.usat.yaml`
|
|
69
|
+
|
|
70
|
+
Lockfiles **are** indexed (a rule asking "is a lockfile committed?" has to see them)
|
|
71
|
+
but are never grepped — they are huge and full of false positives. Files over 2 MB
|
|
72
|
+
and anything with a NUL byte in the first 8 KB are skipped as binary.
|
|
73
|
+
|
|
74
|
+
Build-output checks therefore use `tracked_absent`, which asks git what is tracked
|
|
75
|
+
rather than what is on disk:
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
- id: REPO-007
|
|
79
|
+
check:
|
|
80
|
+
kind: tracked_absent
|
|
81
|
+
patterns: ['dist/**', 'build/**', 'node_modules/**']
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Rules of thumb
|
|
85
|
+
|
|
86
|
+
**Prefer `facts:` over `ignore:`.** If detection is wrong, say what is true:
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
facts: ['has:database', 'orm:prisma']
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Prefer a suppression over disabling a rule.** A suppression stays visible in the
|
|
93
|
+
report under Accepted Risk; a disabled rule disappears and takes its knowledge with it.
|
|
94
|
+
|
|
95
|
+
**Waivers expire — and expiry fails closed.** An `until` date in the past, or
|
|
96
|
+
one that cannot be parsed as a date, excludes the suppression with a warning
|
|
97
|
+
and the finding reports normally. Use unambiguous ISO dates (`2026-12-31`).
|
|
98
|
+
An audit must never silently honour dead risk acceptances (see ADR-0007).
|
|
99
|
+
|
|
100
|
+
**`include` forces packs on wholesale.** A force-loaded pack applies all its
|
|
101
|
+
rules regardless of depth and `applies_when` — that is the documented
|
|
102
|
+
contract, and the operator is responsible for what they force on.
|
|
103
|
+
|
|
104
|
+
**Keep `.usat.yaml` in the repo.** It is the audit trail for every exception you
|
|
105
|
+
have taken. A reviewer should be able to read it and understand what the team decided.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Detectors — the fact catalogue
|
|
2
|
+
|
|
3
|
+
Detection is entirely declarative: [`rules/detectors.yaml`](../rules/detectors.yaml)
|
|
4
|
+
maps file patterns to **facts**, and rule packs select themselves on those facts.
|
|
5
|
+
Add a signal and every pack that cares starts applying — no TypeScript required.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
usat detect . # what USAT thinks your project is
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## How a detector works
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
- fact: 'fw:next'
|
|
15
|
+
category: framework
|
|
16
|
+
title: 'Next.js'
|
|
17
|
+
match:
|
|
18
|
+
any_of:
|
|
19
|
+
- file_exists: ['next.config.js', 'next.config.mjs', 'next.config.ts']
|
|
20
|
+
- manifest: { file: 'package.json', key: 'dependencies.next' }
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
| Primitive | Meaning |
|
|
24
|
+
| ---------------------------------- | ------------------------------------------------ |
|
|
25
|
+
| `any_file: [glob]` | at least one file matches |
|
|
26
|
+
| `file_exists: [glob]` | path present |
|
|
27
|
+
| `dir_exists: [path]` | directory present |
|
|
28
|
+
| `content: {include, pattern}` | regex found in matching files |
|
|
29
|
+
| `manifest: {file, key, contains?}` | dotted key resolves (JSON, or TOML/YAML section) |
|
|
30
|
+
| `metric: {name, min\|max}` | numeric fact comparison |
|
|
31
|
+
| `any_of` / `all_of` | boolean composition |
|
|
32
|
+
| `implies: [fact]` | derived — all listed facts must hold |
|
|
33
|
+
|
|
34
|
+
Several detectors may emit the same fact; the fact is set if **any** of them fires
|
|
35
|
+
(OR semantics). `implies` chains resolve to a fixed point (bounded by
|
|
36
|
+
detector count + 1 passes, early exit), so chains of any length resolve
|
|
37
|
+
regardless of declaration order.
|
|
38
|
+
|
|
39
|
+
Request-serving frameworks imply their platform: `fw:express` …
|
|
40
|
+
`fw:sveltekit` (24 frameworks) each derive `platform:server`, so a bare
|
|
41
|
+
framework app with no Dockerfile still gets the platform-gated rules
|
|
42
|
+
(SEC-003 et al.). Task queues, static builders, and desktop/mobile packs
|
|
43
|
+
deliberately imply nothing — see the comment block in
|
|
44
|
+
`rules/detectors.yaml`.
|
|
45
|
+
|
|
46
|
+
Manifest queries are section-scoped where sections exist: a dotted key
|
|
47
|
+
(`tool.poetry.dependencies`) matches a full header segment
|
|
48
|
+
(case-insensitive; `[dev-dependencies]` is not `[dependencies]`), the leaf
|
|
49
|
+
matches on word boundaries, and `contains` is checked against the matched
|
|
50
|
+
block — never the whole file. Flat files with no headers keep whole-text
|
|
51
|
+
search. See ADR-0010.
|
|
52
|
+
|
|
53
|
+
## Prose, tests, and examples are excluded from `content` scans
|
|
54
|
+
|
|
55
|
+
A repo whose README mentions Postgres does not have a Postgres dependency. Every
|
|
56
|
+
`content:` scan automatically skips:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
**/*.md **/*.mdx **/*.rst README* docs/** doc/** examples/**
|
|
60
|
+
example/** templates/** fixtures/** __mocks__/** test/** tests/**
|
|
61
|
+
__tests__/** spec/** *.test.* *.spec.* *.stories.* test_*
|
|
62
|
+
*_test.* LICENSE* CHANGELOG*
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Deliberately **not** excluded: `**/*.txt` — `requirements.txt` is a
|
|
66
|
+
manifest, not prose. This list mirrors `CONTENT_EXCLUDES` in
|
|
67
|
+
`src/detect/index.ts`; if they drift, the code wins and this doc is a bug —
|
|
68
|
+
file it.
|
|
69
|
+
|
|
70
|
+
Everything else in the tree is fair game. If a project's own config comments are
|
|
71
|
+
still producing noise, add the file to `ignore:` in `.usat.yaml` — that is what
|
|
72
|
+
[`../.usat.yaml`](../.usat.yaml) does with `rules/`.
|
|
73
|
+
|
|
74
|
+
## Fact namespaces
|
|
75
|
+
|
|
76
|
+
### `lang:*` — language
|
|
77
|
+
|
|
78
|
+
`typescript` `javascript` `python` `go` `rust` `java` `kotlin` `swift` `ruby` `php`
|
|
79
|
+
`csharp` `cpp` `scala` `dart` `elixir` `lua` `zig` `solidity` `shell` `haskell`
|
|
80
|
+
|
|
81
|
+
### `pm:*` — package manager
|
|
82
|
+
|
|
83
|
+
`node` `npm` `pnpm` `yarn` `bun` `pip` `poetry` `uv` `pipenv` `cargo` `go-mod`
|
|
84
|
+
`maven` `gradle` `composer` `nuget` `swiftpm` `pub` `mix` `foundry` `hardhat`
|
|
85
|
+
|
|
86
|
+
### `fw:*` — framework
|
|
87
|
+
|
|
88
|
+
Web: `react` `vue` `angular` `svelte` `solid` `next` `nuxt` `remix` `sveltekit`
|
|
89
|
+
`astro` `vite` `tailwind`
|
|
90
|
+
Node backend: `express` `fastify` `nest` `hono` `koa`
|
|
91
|
+
Python: `django` `flask` `fastapi` `celery` `airflow` `streamlit`
|
|
92
|
+
Go: `gin` `echo` `fiber` `chi` · Rust: `axum` `actix` `rocket`
|
|
93
|
+
JVM: `spring` `quarkus` · Ruby: `rails` · PHP: `laravel` `symfony` · .NET: `dotnet`
|
|
94
|
+
Mobile/desktop: `react-native` `expo` `flutter` `electron` `tauri`
|
|
95
|
+
|
|
96
|
+
### `project:*` — archetype
|
|
97
|
+
|
|
98
|
+
`library` `cli` `monorepo` `api` `ml` `blockchain` `iac` `data-pipeline` `game` `docs`
|
|
99
|
+
|
|
100
|
+
### `platform:*` — runtime
|
|
101
|
+
|
|
102
|
+
`web` `server` `desktop` `mobile` `ios` `android` `evm` `embedded`
|
|
103
|
+
|
|
104
|
+
### `db:*` — datastore
|
|
105
|
+
|
|
106
|
+
`postgres` `mysql` `sqlite` `mongodb` `redis` `clickhouse` `elasticsearch` `vector`
|
|
107
|
+
`dynamodb` · plus `has:database`
|
|
108
|
+
|
|
109
|
+
### `orm:*`
|
|
110
|
+
|
|
111
|
+
`prisma` `drizzle` `typeorm` `sequelize` `mongoose` `sqlalchemy` `gorm` `sqlx`
|
|
112
|
+
`diesel` `hibernate` `activerecord`
|
|
113
|
+
|
|
114
|
+
### `auth:*`
|
|
115
|
+
|
|
116
|
+
`jwt` `oauth` `session` `apikey` `passport` `nextauth` `supabase` `firebase` `auth0`
|
|
117
|
+
|
|
118
|
+
### `api:*` · `obs:*`
|
|
119
|
+
|
|
120
|
+
`api:graphql` `api:grpc` `api:trpc` `api:websocket`
|
|
121
|
+
`obs:sentry` `obs:otel` `obs:prometheus` `obs:datadog` `obs:grafana` · `has:monitoring`
|
|
122
|
+
|
|
123
|
+
### `test:*`
|
|
124
|
+
|
|
125
|
+
`vitest` `jest` `mocha` `pytest` `gotest` `cargotest` `junit` `rspec` `phpunit`
|
|
126
|
+
`playwright` `cypress` · plus `has:tests` `has:e2e` `has:perf-tests`
|
|
127
|
+
|
|
128
|
+
### `ci:*` · `infra:*`
|
|
129
|
+
|
|
130
|
+
`ci:github-actions` `ci:gitlab` `ci:jenkins` `ci:circleci` `ci:buildkite` `ci:azure`
|
|
131
|
+
· `has:ci`
|
|
132
|
+
|
|
133
|
+
### `ai:*`
|
|
134
|
+
|
|
135
|
+
`ai:llm-sdk` `ai:prompts` `ai:agents` `ai:mcp` `ai:rag` `ai:evals`
|
|
136
|
+
|
|
137
|
+
### `doc:*`
|
|
138
|
+
|
|
139
|
+
`readme` `license` `contributing` `code-of-conduct` `security-policy` `changelog`
|
|
140
|
+
`architecture` `adr` `api-reference` `runbook` `onboarding` `env-example`
|
|
141
|
+
`gitignore` `codeowners` `pr-template` `issue-templates` `agents`
|
|
142
|
+
|
|
143
|
+
### `has:*` — cross-cutting capabilities
|
|
144
|
+
|
|
145
|
+
`has:lint` `has:format` `has:typecheck` `has:precommit` `has:dependabot`
|
|
146
|
+
`has:renovate` `has:changesets` `has:release-automation` `has:editorconfig`
|
|
147
|
+
`has:containers` `has:kubernetes` `has:terraform` `has:serverless` `has:sbom`
|
|
148
|
+
`has:secrets-scan` `has:sast` `has:dep-scan` `has:codecov` `has:monitoring`
|
|
149
|
+
`has:crypto` `has:file-upload` `has:payments` `has:pii` `has:csp` `has:rate-limit`
|
|
150
|
+
`has:database` `has:tests` `has:e2e` `has:perf-tests` `has:ci`
|
|
151
|
+
|
|
152
|
+
### `practice:*`
|
|
153
|
+
|
|
154
|
+
`conventional-commits` `codeowners` `signed-commits` `branch-protection-doc`
|
|
155
|
+
`pinned-versions` `i18n` `feature-flags` `queue` `cache`
|
|
156
|
+
`monorepo-tool-turborepo` `monorepo-tool-nx`
|
|
157
|
+
|
|
158
|
+
### `maturity:*` and metrics
|
|
159
|
+
|
|
160
|
+
`maturity:prototype` `maturity:mvp` `maturity:beta` `maturity:production` `maturity:legacy`
|
|
161
|
+
|
|
162
|
+
Metrics, referenced as `metric:<name>` in predicates:
|
|
163
|
+
|
|
164
|
+
| Metric | Source |
|
|
165
|
+
| --------------------- | --------------------------- |
|
|
166
|
+
| `commits` | `git rev-list --count HEAD` |
|
|
167
|
+
| `contributors` | `git shortlog -sn --all` |
|
|
168
|
+
| `tags` | `git tag --list` |
|
|
169
|
+
| `branches` | `git branch --list` |
|
|
170
|
+
| `files` | indexed file count |
|
|
171
|
+
| `daysSinceLastCommit` | `git log -1 --format=%ct` |
|
|
172
|
+
|
|
173
|
+
## Adding a detector
|
|
174
|
+
|
|
175
|
+
1. Add an entry to `rules/detectors.yaml` with a clear `fact:` name.
|
|
176
|
+
2. Use `manifest:` where possible — it is far more reliable than a content regex
|
|
177
|
+
(`dependencies.hono` beats grepping for `hono` in source).
|
|
178
|
+
3. Verify against a project that uses the technology and one that does not:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
usat detect ~/code/uses-hono
|
|
182
|
+
usat detect ~/code/does-not
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
4. If detection is right but a project still eludes it, users can assert facts in
|
|
186
|
+
`.usat.yaml` — but if you find yourself doing that often, the detector is wrong.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
## Install
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx @xenos1996/usat audit . # zero-install
|
|
7
|
+
npm i -g @xenos1996/usat # or globally
|
|
8
|
+
npm i -D @xenos1996/usat # or per project, so CI and laptops agree
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Node 20+. No network access is needed at audit time, and USAT never
|
|
12
|
+
uploads anything — it reads files and writes one Markdown file.
|
|
13
|
+
|
|
14
|
+
## Your first audit
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
cd ~/code/my-project
|
|
18
|
+
usat detect . # 1. check what it thinks you are
|
|
19
|
+
usat audit . # 2. audit it
|
|
20
|
+
open AUDIT.md
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Start with `detect`. If the detected facts are wrong, the report will be wrong —
|
|
24
|
+
and fixing it takes one line of config:
|
|
25
|
+
|
|
26
|
+
```yaml
|
|
27
|
+
# .usat.yaml
|
|
28
|
+
facts: ['has:database'] # assert anything detection missed
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Reading the report
|
|
32
|
+
|
|
33
|
+
**1 · Look at the band, not the number.**
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
Overall Health Score: 71.4/100
|
|
37
|
+
Expected band for Beta / Growing: 60–85 — within the expected band 👍
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
A 55/100 on a prototype is healthy. The same 55/100 on a production service is a
|
|
41
|
+
problem. USAT tells you which you are looking at.
|
|
42
|
+
|
|
43
|
+
**2 · Read Immediate Action Required.** CRITICAL and security-HIGH findings, with
|
|
44
|
+
location and fix. Everything else waits.
|
|
45
|
+
|
|
46
|
+
**3 · Check confidence before celebrating.**
|
|
47
|
+
|
|
48
|
+
| Dimension | Score | Confidence |
|
|
49
|
+
| ------------------ | ----- | ---------- |
|
|
50
|
+
| S10 · Dependencies | 10/10 | 25% |
|
|
51
|
+
|
|
52
|
+
A 10/10 at 25% confidence means one of four applicable rules could be checked
|
|
53
|
+
automatically. The other three are in the judgement queue. Sections where
|
|
54
|
+
_nothing_ could be verified say **"— not verified"** and are excluded from the
|
|
55
|
+
total rather than quietly scoring 10.
|
|
56
|
+
|
|
57
|
+
**4 · Work the judgement queue.** These are the checks grep cannot settle. Take them
|
|
58
|
+
to an agent or a reviewer. Every row says what to look for and what evidence to
|
|
59
|
+
record. **Do not mark anything ✅ without evidence.**
|
|
60
|
+
|
|
61
|
+
**5 · Ship the roadmap.** SPRINT 0 → SPRINT 1 → SPRINT 2 → BACKLOG, generated from
|
|
62
|
+
severity. `DEFERRED` lists what the maturity profile says you should deliberately
|
|
63
|
+
ignore _at this stage_.
|
|
64
|
+
|
|
65
|
+
## Common flags
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
usat audit . --depth deep # include deep-only rules (cycles, duplication, complexity)
|
|
69
|
+
usat audit . --profile production # grade against the full bar regardless of age
|
|
70
|
+
usat audit . --fail-on high # exit 1 on HIGH+ (for CI gates)
|
|
71
|
+
usat audit . --out reports/2026-09.md # dated reports, so you can diff them later
|
|
72
|
+
usat audit . --allow-commands # run shell checks (npm audit, depcheck, dpdm)
|
|
73
|
+
usat audit . --include stacks/solidity # force a pack on
|
|
74
|
+
usat rules --section S2 # list rules
|
|
75
|
+
usat explain SEC-001 # everything about one rule
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`--allow-commands` is **off by default** because it shells out. Those rules appear
|
|
79
|
+
as ❓ NEEDS REVIEW until you enable it — which is the point: they are real checks,
|
|
80
|
+
they just need your permission to run.
|
|
81
|
+
|
|
82
|
+
## Compare two audits
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
usat audit . --out reports/2026-06.md
|
|
86
|
+
# ... three months of work ...
|
|
87
|
+
usat audit . --out reports/2026-09.md
|
|
88
|
+
usat diff reports/2026-06.md reports/2026-09.md
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The diff reads the YAML trailer embedded in every report — so a Markdown report is
|
|
92
|
+
still the only artefact you need to keep.
|
|
93
|
+
|
|
94
|
+
## Next steps
|
|
95
|
+
|
|
96
|
+
- [Concepts](concepts.md) — how severity, status, and scoring actually work
|
|
97
|
+
- [Configuration](configuration.md) — `.usat.yaml` reference
|
|
98
|
+
- [Rule packs](rule-packs.md) — write your own rules
|
|
99
|
+
- [Agent integration](agent-integration.md) — drive USAT from Claude, Cursor, Codex
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Maturity profiles
|
|
2
|
+
|
|
3
|
+
The same repository deserves a different report at age three days and age three years.
|
|
4
|
+
Grading a prototype against a production bar produces a wall of noise nobody reads;
|
|
5
|
+
grading production with prototype standards produces a false all-clear.
|
|
6
|
+
|
|
7
|
+
USAT detects a lifecycle stage and **dampens** severity to match.
|
|
8
|
+
|
|
9
|
+
## How the stage is detected
|
|
10
|
+
|
|
11
|
+
A score out of 7.5, from signals that are hard to fake:
|
|
12
|
+
|
|
13
|
+
| Signal | Points |
|
|
14
|
+
| ---------------------- | ------ |
|
|
15
|
+
| Tests present | +1 |
|
|
16
|
+
| CI configured | +1 |
|
|
17
|
+
| CHANGELOG maintained | +1 |
|
|
18
|
+
| Release tags published | +1 |
|
|
19
|
+
| ≥50 commits | +1 |
|
|
20
|
+
| SECURITY.md published | +1 |
|
|
21
|
+
| Containerised | +0.5 |
|
|
22
|
+
| Monitoring configured | +0.5 |
|
|
23
|
+
| CONTRIBUTING guide | +0.5 |
|
|
24
|
+
|
|
25
|
+
Then:
|
|
26
|
+
|
|
27
|
+
| Condition | Stage |
|
|
28
|
+
| ----------------------------- | -------------- |
|
|
29
|
+
| No commits for ~18 months | **legacy** |
|
|
30
|
+
| ~12 months idle **and** no CI | **legacy** |
|
|
31
|
+
| Score ≥ 7 **and** tags exist | **production** |
|
|
32
|
+
| Score ≥ 5 | **beta** |
|
|
33
|
+
| Score ≥ 2.5 | **mvp** |
|
|
34
|
+
| Otherwise | **prototype** |
|
|
35
|
+
|
|
36
|
+
A `0.x` version with no tags is never promoted to _production_ — it downgrades to
|
|
37
|
+
_beta_ with the reason recorded in the report.
|
|
38
|
+
|
|
39
|
+
Boundary semantics, stated exactly because auditors must not hedge: staleness
|
|
40
|
+
is exclusive (`> 365` and `> 540` days — exactly-365-days-stale counts as
|
|
41
|
+
fresh); production needs score ≥ 7 **and** at least one tag (7.0 with zero
|
|
42
|
+
tags is beta); prerelease detection reads the **root** `package.json` only,
|
|
43
|
+
so monorepos and polyglot version files (`pyproject.toml`, `Cargo.toml`,
|
|
44
|
+
`go.mod`) should assert maturity explicitly via `facts:` or `maturity:` if
|
|
45
|
+
the auto-detection misreads them.
|
|
46
|
+
|
|
47
|
+
Every signal and the resulting score are printed in the report appendix, so the
|
|
48
|
+
classification can be argued with.
|
|
49
|
+
|
|
50
|
+
## Dampening
|
|
51
|
+
|
|
52
|
+
Steps move down the ladder `FUTURE < LOW < MEDIUM < HIGH < CRITICAL`.
|
|
53
|
+
|
|
54
|
+
| Class | 🌱 Prototype | 🚀 MVP | 🧪 Beta | 🏭 Production | 🏚️ Legacy |
|
|
55
|
+
| ----------------- | ------------ | ------ | ------- | ------------- | --------- |
|
|
56
|
+
| `security` | −1 | −0 | −0 | −0 | −0 |
|
|
57
|
+
| `supply-chain` | −1 | −1 | −0 | −0 | −0 |
|
|
58
|
+
| `correctness` | −1 | −1 | −0 | −0 | −0 |
|
|
59
|
+
| `maintainability` | −2 | −1 | −1 | −0 | −1 |
|
|
60
|
+
| `operations` | −2 | −1 | −1 | −0 | −0 |
|
|
61
|
+
| `performance` | −2 | −1 | −1 | −0 | −1 |
|
|
62
|
+
| `compliance` | −2 | −1 | −1 | −0 | −0 |
|
|
63
|
+
| `documentation` | −2 | −2 | −1 | −0 | −1 |
|
|
64
|
+
| `style` | −2 | −2 | −1 | −0 | −1 |
|
|
65
|
+
|
|
66
|
+
> ### 🔴 CRITICAL is never dampened, at any stage.
|
|
67
|
+
>
|
|
68
|
+
> A leaked credential in a weekend prototype is still a leaked credential.
|
|
69
|
+
> A committed `.env` in an MVP is still a committed `.env`.
|
|
70
|
+
> Everything else is negotiable with the calendar. This one is not, and it is
|
|
71
|
+
> enforced in code (`src/engine/maturity.ts`), not just documented here.
|
|
72
|
+
|
|
73
|
+
Every dampened finding says so in the report, so a growing prototype can see what
|
|
74
|
+
will climb on its own:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
- 🚫 **LICENSE present** `REPO-005`
|
|
78
|
+
- 🪶 Downgraded HIGH → LOW by the Prototype / Spike profile
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Expected score bands
|
|
82
|
+
|
|
83
|
+
| Stage | Band | Reading |
|
|
84
|
+
| ------------- | ----- | ---------------------------------------------------------------------------- |
|
|
85
|
+
| 🌱 Prototype | 30–65 | "Above band" here means you started with good hygiene, not that you are done |
|
|
86
|
+
| 🚀 MVP | 45–75 | Real users, real data — the security basics must exist |
|
|
87
|
+
| 🧪 Beta | 60–85 | Close the process gaps before they compound |
|
|
88
|
+
| 🏭 Production | 75–95 | Full bar; nothing dampened |
|
|
89
|
+
| 🏚️ Legacy | 40–70 | Graded on risk containment, not modernisation ambition |
|
|
90
|
+
|
|
91
|
+
The report always prints the score **and** the band and a verdict:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
Overall Health Score: 71.4/100
|
|
95
|
+
Expected band for Beta / Growing: 60–85 — within the expected band 👍
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Per-stage guidance
|
|
99
|
+
|
|
100
|
+
Each profile in [`rules/profiles/maturity.yaml`](../rules/profiles/maturity.yaml)
|
|
101
|
+
carries a `focus` list (what actually matters now) and a `defer` list (what to
|
|
102
|
+
deliberately ignore). Both are rendered in the report's roadmap:
|
|
103
|
+
|
|
104
|
+
### 🌱 Prototype
|
|
105
|
+
|
|
106
|
+
**Focus:** secret hygiene and `.gitignore` · a README that says what this is and how
|
|
107
|
+
to run it · one smoke test so the happy path is known-good.
|
|
108
|
+
**Defer:** Kubernetes, multi-region, formal ADRs, 80% coverage gates.
|
|
109
|
+
|
|
110
|
+
### 🚀 MVP
|
|
111
|
+
|
|
112
|
+
**Focus:** input validation and output encoding on every user-facing path · automated
|
|
113
|
+
backup plus a restore you have actually performed · CI running lint + tests ·
|
|
114
|
+
dependency scanning before the graph grows.
|
|
115
|
+
**Defer:** formal threat model, multi-region, chaos engineering, SBOM/VEX.
|
|
116
|
+
|
|
117
|
+
### 🧪 Beta
|
|
118
|
+
|
|
119
|
+
**Focus:** branch protection with mandatory review · structured logging with
|
|
120
|
+
correlation IDs and real alerting · integration tests around critical flows ·
|
|
121
|
+
documented rollback.
|
|
122
|
+
**Defer:** full DR site, formal verification, mutation testing.
|
|
123
|
+
|
|
124
|
+
### 🏭 Production
|
|
125
|
+
|
|
126
|
+
**Focus:** close every HIGH · provenance/attestation on release artifacts (SLSA) ·
|
|
127
|
+
tested DR with a real RTO/RPO · observability that answers "is it broken?" in under
|
|
128
|
+
five minutes.
|
|
129
|
+
|
|
130
|
+
### 🏚️ Legacy
|
|
131
|
+
|
|
132
|
+
**Focus:** inventory EOL dependencies with a replacement date · confirm backups
|
|
133
|
+
restore and that someone still knows how · monitoring, alerting, and a named owner per
|
|
134
|
+
service · document well enough to hand over or retire.
|
|
135
|
+
**Defer:** rewrites without a strangler plan; framework migrations for their own sake.
|
|
136
|
+
|
|
137
|
+
## Overriding
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
usat audit . --profile production # grade against the full bar
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
# .usat.yaml
|
|
145
|
+
maturity: production
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Use `--profile production` on a younger project when you want the
|
|
149
|
+
_"what would it take to ship this?"_ view. Expect the score to drop and the CRITICAL
|
|
150
|
+
list to stay exactly the same length — that is the point.
|
|
151
|
+
|
|
152
|
+
## Customising
|
|
153
|
+
|
|
154
|
+
Edit [`rules/profiles/maturity.yaml`](../rules/profiles/maturity.yaml) — no code
|
|
155
|
+
changes needed. Add a stage, change the dampening table, adjust the expected bands, or
|
|
156
|
+
rewrite the focus/defer lists to match how your organisation actually ships.
|