@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.
Files changed (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +335 -0
  3. package/USAT.md +596 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +438 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/config.d.ts +7 -0
  9. package/dist/config.d.ts.map +1 -0
  10. package/dist/config.js +72 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/detect/index.d.ts +58 -0
  13. package/dist/detect/index.d.ts.map +1 -0
  14. package/dist/detect/index.js +329 -0
  15. package/dist/detect/index.js.map +1 -0
  16. package/dist/engine/audit.d.ts +21 -0
  17. package/dist/engine/audit.d.ts.map +1 -0
  18. package/dist/engine/audit.js +295 -0
  19. package/dist/engine/audit.js.map +1 -0
  20. package/dist/engine/diff.d.ts +24 -0
  21. package/dist/engine/diff.d.ts.map +1 -0
  22. package/dist/engine/diff.js +136 -0
  23. package/dist/engine/diff.js.map +1 -0
  24. package/dist/engine/evaluate.d.ts +19 -0
  25. package/dist/engine/evaluate.d.ts.map +1 -0
  26. package/dist/engine/evaluate.js +377 -0
  27. package/dist/engine/evaluate.js.map +1 -0
  28. package/dist/engine/gate.d.ts +19 -0
  29. package/dist/engine/gate.d.ts.map +1 -0
  30. package/dist/engine/gate.js +47 -0
  31. package/dist/engine/gate.js.map +1 -0
  32. package/dist/engine/loader.d.ts +22 -0
  33. package/dist/engine/loader.d.ts.map +1 -0
  34. package/dist/engine/loader.js +360 -0
  35. package/dist/engine/loader.js.map +1 -0
  36. package/dist/engine/maturity.d.ts +20 -0
  37. package/dist/engine/maturity.d.ts.map +1 -0
  38. package/dist/engine/maturity.js +157 -0
  39. package/dist/engine/maturity.js.map +1 -0
  40. package/dist/engine/score.d.ts +19 -0
  41. package/dist/engine/score.d.ts.map +1 -0
  42. package/dist/engine/score.js +141 -0
  43. package/dist/engine/score.js.map +1 -0
  44. package/dist/engine/sections.d.ts +14 -0
  45. package/dist/engine/sections.d.ts.map +1 -0
  46. package/dist/engine/sections.js +50 -0
  47. package/dist/engine/sections.js.map +1 -0
  48. package/dist/index.d.ts +23 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +17 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/report/markdown.d.ts +16 -0
  53. package/dist/report/markdown.d.ts.map +1 -0
  54. package/dist/report/markdown.js +488 -0
  55. package/dist/report/markdown.js.map +1 -0
  56. package/dist/types.d.ts +282 -0
  57. package/dist/types.d.ts.map +1 -0
  58. package/dist/types.js +16 -0
  59. package/dist/types.js.map +1 -0
  60. package/dist/util/glob.d.ts +11 -0
  61. package/dist/util/glob.d.ts.map +1 -0
  62. package/dist/util/glob.js +86 -0
  63. package/dist/util/glob.js.map +1 -0
  64. package/dist/util/project.d.ts +72 -0
  65. package/dist/util/project.d.ts.map +1 -0
  66. package/dist/util/project.js +586 -0
  67. package/dist/util/project.js.map +1 -0
  68. package/dist/util/yaml.d.ts +28 -0
  69. package/dist/util/yaml.d.ts.map +1 -0
  70. package/dist/util/yaml.js +48 -0
  71. package/dist/util/yaml.js.map +1 -0
  72. package/docs/ARCHITECTURE.md +119 -0
  73. package/docs/README.md +27 -0
  74. package/docs/adr/0001-record-architecture-decisions.md +35 -0
  75. package/docs/adr/0002-typescript-engine-over-python.md +49 -0
  76. package/docs/adr/0003-rules-are-data-not-code.md +50 -0
  77. package/docs/adr/0004-markdown-only-output.md +41 -0
  78. package/docs/adr/0005-maturity-dampens-severity.md +56 -0
  79. package/docs/adr/0006-severity-status-two-axes.md +48 -0
  80. package/docs/adr/0007-suppressions-are-visible-and-expiring.md +46 -0
  81. package/docs/adr/0008-report-trailer-and-diff.md +48 -0
  82. package/docs/adr/0009-fail-closed-on-malformed-input.md +56 -0
  83. package/docs/adr/0010-detector-design.md +54 -0
  84. package/docs/adr/0011-coexist-with-deep-scanners.md +52 -0
  85. package/docs/adr/README.md +32 -0
  86. package/docs/agent-integration.md +129 -0
  87. package/docs/ci-integration.md +163 -0
  88. package/docs/concepts.md +196 -0
  89. package/docs/configuration.md +105 -0
  90. package/docs/detectors.md +186 -0
  91. package/docs/getting-started.md +99 -0
  92. package/docs/maturity-profiles.md +156 -0
  93. package/docs/reference/api.md +193 -0
  94. package/docs/reference/cli.md +412 -0
  95. package/docs/rule-packs.md +278 -0
  96. package/docs/standards-mapping.md +171 -0
  97. package/package.json +96 -0
  98. package/rules/core/architecture.yaml +190 -0
  99. package/rules/core/cicd.yaml +212 -0
  100. package/rules/core/code-quality.yaml +261 -0
  101. package/rules/core/dependencies.yaml +132 -0
  102. package/rules/core/documentation.yaml +119 -0
  103. package/rules/core/future-readiness.yaml +91 -0
  104. package/rules/core/release.yaml +131 -0
  105. package/rules/core/repo.yaml +303 -0
  106. package/rules/core/security.yaml +494 -0
  107. package/rules/core/supply-chain.yaml +357 -0
  108. package/rules/core/testing.yaml +260 -0
  109. package/rules/detectors.yaml +2255 -0
  110. package/rules/index.yaml +41 -0
  111. package/rules/profiles/maturity.yaml +135 -0
  112. package/rules/stacks/ai-era.yaml +193 -0
  113. package/rules/stacks/api-backend.yaml +121 -0
  114. package/rules/stacks/cli.yaml +105 -0
  115. package/rules/stacks/compliance.yaml +157 -0
  116. package/rules/stacks/containers.yaml +124 -0
  117. package/rules/stacks/data.yaml +165 -0
  118. package/rules/stacks/go.yaml +134 -0
  119. package/rules/stacks/iac.yaml +128 -0
  120. package/rules/stacks/jvm.yaml +150 -0
  121. package/rules/stacks/ml-ai.yaml +140 -0
  122. package/rules/stacks/mobile.yaml +121 -0
  123. package/rules/stacks/node-typescript.yaml +166 -0
  124. package/rules/stacks/python.yaml +175 -0
  125. package/rules/stacks/rust.yaml +129 -0
  126. package/rules/stacks/solidity.yaml +139 -0
  127. package/rules/stacks/web-frontend.yaml +159 -0
  128. package/templates/AGENTS.audit.md +135 -0
  129. package/templates/AUDIT_REPORT.md +155 -0
@@ -0,0 +1,128 @@
1
+ id: stacks/iac
2
+ title: Infrastructure as Code & Cloud
3
+ section: S15
4
+ section_title: Platform-Specific
5
+ description: 'Activated when Terraform/OpenTofu, Pulumi, Ansible, or Kubernetes manifests are detected.'
6
+ version: '1.0'
7
+ skip_when:
8
+ all:
9
+ - { fact: 'has:terraform', op: 'absent' }
10
+ - { fact: 'has:kubernetes', op: 'absent' }
11
+ - { fact: 'project:iac', op: 'absent' }
12
+
13
+ rules:
14
+ - id: IAC-001
15
+ title: No public exposure of storage buckets or databases
16
+ section: S15
17
+ section_title: Platform-Specific
18
+ severity: CRITICAL
19
+ class: security
20
+ applies_when: { fact: 'has:terraform' }
21
+ check:
22
+ kind: grep_wrong
23
+ pattern: '(acl\s*=\s*[''"]public-read|publicly_accessible\s*=\s*true|0\.0\.0\.0/0|cidr_blocks\s*=\s*\[\s*[''"]0\.0\.0\.0/0|Effect\s*=\s*[''"]Allow[\s\S]{0,120}Principal\s*=\s*[''"]\*)'
24
+ include: ['**/*.tf', '**/*.tfvars', '**/*.{yaml,yml,json}']
25
+ exclude: ['**/node_modules/**']
26
+ why: 'This is the single most common cause of cloud data breaches, and it is one line of config.'
27
+ remediation: 'Default to private; allowlist specific principals/CIDRs; add an automated check (Checkov/tfsec) in CI.'
28
+ references: ['CWE-284', 'CIS-AWS-2.1.5', 'ASVS-14.1.3']
29
+
30
+ - id: IAC-002
31
+ title: Encryption at rest is enabled
32
+ section: S15
33
+ section_title: Platform-Specific
34
+ severity: HIGH
35
+ class: security
36
+ applies_when: { fact: 'has:terraform' }
37
+ check:
38
+ kind: grep_present
39
+ pattern: '(encrypted\s*=\s*true|storage_encrypted\s*=\s*true|sse_algorithm|kms_key_id|enable_disk_encryption|customer_managed_key)'
40
+ include: ['**/*.tf', '**/*.tfvars', '**/*.{yaml,yml}']
41
+ exclude: ['**/node_modules/**']
42
+ why: 'Unencrypted volumes and buckets fail every compliance framework you will ever be asked about.'
43
+ remediation: 'Enable encryption with a customer-managed KMS key on all storage, databases, and queues.'
44
+ references: ['CIS-AWS-2.2.1', 'ASVS-8.1.1', 'GDPR-Art.32']
45
+
46
+ - id: IAC-003
47
+ title: IaC is validated and scanned before apply
48
+ section: S15
49
+ section_title: Platform-Specific
50
+ severity: MEDIUM
51
+ class: supply-chain
52
+ applies_when: { fact: 'has:ci' }
53
+ check:
54
+ kind: grep_present
55
+ pattern: '(terraform validate|terraform plan|tflint|checkov|tfsec|trivy config|kubeconform|kubeval|pulumi preview|ansible-lint)'
56
+ include:
57
+ [
58
+ '.github/workflows/*.yml',
59
+ '.github/workflows/*.yaml',
60
+ '.gitlab-ci.yml',
61
+ 'Makefile',
62
+ 'justfile',
63
+ '.pre-commit-config.yaml',
64
+ ]
65
+ flags: i
66
+ why: '`terraform apply` on unreviewed HCL is a production change with no diff shown to anybody.'
67
+ remediation: 'Run validate → plan → policy scan on every PR; require approval before apply.'
68
+ references: ['NIST-SSDF-PO.5', 'CIS']
69
+
70
+ - id: IAC-004
71
+ title: State is stored remotely and locked
72
+ section: S15
73
+ section_title: Platform-Specific
74
+ severity: HIGH
75
+ class: operations
76
+ applies_when: { fact: 'has:terraform' }
77
+ check:
78
+ kind: grep_present
79
+ pattern: '(backend\s+"(s3|azurerm|gcs|remote|http|cos)|dynamodb_table|use_lockfile|lock_table)'
80
+ include: ['**/*.tf', '**/backend.tf', '**/*.hcl']
81
+ why: 'Local state means a laptop is the source of truth for your infrastructure, and concurrent applies corrupt it.'
82
+ remediation: 'Configure a remote backend with locking (S3 + DynamoDB, GCS, or Terraform Cloud).'
83
+ references: ['Terraform-Backends']
84
+
85
+ - id: IAC-005
86
+ title: IAM follows least privilege
87
+ section: S15
88
+ section_title: Platform-Specific
89
+ severity: HIGH
90
+ class: security
91
+ applies_when: { fact: 'has:terraform' }
92
+ check:
93
+ kind: grep_absent
94
+ pattern: '(Action\s*=\s*[''"]\*[''"]|actions\s*=\s*\[\s*[''"]\*[''"]|Resource\s*=\s*[''"]\*[''"]\s*$)'
95
+ include: ['**/*.tf', '**/*.{yaml,yml,json}']
96
+ exclude: ['**/node_modules/**']
97
+ why: '`Action: "*"` on `Resource: "*"` is administrator access with a nicer YAML indent.'
98
+ remediation: 'Enumerate the exact actions and resources; separate read and write roles; review quarterly.'
99
+ references: ['CIS-AWS-1.16', 'ASVS-4.1.1']
100
+
101
+ - id: IAC-006
102
+ title: Cloud spend is tagged and budgeted
103
+ section: S15
104
+ section_title: Platform-Specific
105
+ severity: LOW
106
+ class: operations
107
+ applies_when: { fact: 'has:terraform' }
108
+ check: { kind: manual }
109
+ evidence: 'A required-tags policy (owner, service, environment) and budget alerts at 50/80/100%.'
110
+ why: 'Untagged resources are how a forgotten load balancer costs $4,000 for eleven months.'
111
+ remediation: 'Enforce tags in policy (OPA/Sentinel); set per-environment budgets with alerts.'
112
+ references: ['FinOps', 'AWS-Well-Architected:Cost']
113
+
114
+ - id: IAC-007
115
+ title: Kubernetes manifests avoid privileged containers
116
+ section: S15
117
+ section_title: Platform-Specific
118
+ severity: HIGH
119
+ class: security
120
+ applies_when: { fact: 'has:kubernetes' }
121
+ check:
122
+ kind: grep_absent
123
+ pattern: '(privileged\s*:\s*true|hostPID\s*:\s*true|hostNetwork\s*:\s*true|allowPrivilegeEscalation\s*:\s*true|capabilities\s*:[\s\S]{0,60}add\s*:[\s\S]{0,40}\[\s*[''"]?ALL)'
124
+ include: ['**/*.{yaml,yml}']
125
+ exclude: ['**/node_modules/**']
126
+ why: 'A privileged container is root on the node. There is almost always a narrower capability that works.'
127
+ remediation: 'Drop ALL capabilities and add back only what is needed; use Pod Security Admission "restricted".'
128
+ references: ['CIS-K8s-5.2.1', 'NSA-K8s-Hardening']
@@ -0,0 +1,150 @@
1
+ id: stacks/jvm
2
+ title: Java / Kotlin / JVM
3
+ section: S15
4
+ section_title: Platform-Specific
5
+ description: 'Activated when Java, Kotlin, or Scala is detected.'
6
+ version: '1.0'
7
+ skip_when:
8
+ all:
9
+ - { fact: 'lang:java', op: 'absent' }
10
+ - { fact: 'lang:kotlin', op: 'absent' }
11
+ - { fact: 'lang:scala', op: 'absent' }
12
+
13
+ rules:
14
+ - id: JVM-001
15
+ title: Dependency versions are managed centrally
16
+ section: S15
17
+ section_title: Platform-Specific
18
+ severity: MEDIUM
19
+ class: supply-chain
20
+ check:
21
+ kind: grep_present
22
+ pattern: '(dependencyManagement|<dependencyManagement>|versions?\.toml|catalog|constraints\s*\{|dependencyResolutionManagement)'
23
+ include:
24
+ [
25
+ 'pom.xml',
26
+ 'build.gradle*',
27
+ 'settings.gradle*',
28
+ 'build.gradle.kts',
29
+ 'gradle/libs.versions.toml',
30
+ ]
31
+ flags: i
32
+ why: 'Without a BOM or version catalog, five modules drift into five versions of the same library.'
33
+ remediation: 'Maven: dependencyManagement. Gradle: a version catalog in gradle/libs.versions.toml.'
34
+ references: ['Maven-BOM', 'Gradle-Version-Catalogs']
35
+
36
+ - id: JVM-002
37
+ title: No hardcoded credentials in configuration
38
+ section: S15
39
+ section_title: Platform-Specific
40
+ severity: CRITICAL
41
+ class: security
42
+ check:
43
+ kind: grep_absent
44
+ pattern: '(password|passwd|secret|token|api[_-]?key)\s*[:=]\s*[''"]?(?!\$\{|#\{|\{\{)[A-Za-z0-9/+_-]{8,}'
45
+ include:
46
+ ['**/*.{properties,yml,yaml,xml,conf}', '**/application*.yml', '**/application*.properties']
47
+ exclude: ['**/src/test/**', '**/test/**']
48
+ flags: i
49
+ why: 'application.properties is committed. Anything in it is public.'
50
+ remediation: 'Externalise to environment variables or a secret manager; reference with ${ENV_VAR}.'
51
+ references: ['CWE-798', 'ASVS-14.3.2']
52
+
53
+ - id: JVM-003
54
+ title: Exception handling does not swallow stack traces
55
+ section: S15
56
+ section_title: Platform-Specific
57
+ severity: MEDIUM
58
+ class: correctness
59
+ check:
60
+ kind: grep_wrong
61
+ pattern: '(catch\s*\(\s*\w+\s+\w+\s*\)\s*\{\s*\}|printStackTrace\s*\(\s*\)|e\.printStackTrace)'
62
+ include: ['**/*.{java,kt,scala}']
63
+ exclude: ['**/src/test/**', '**/test/**']
64
+ why: 'printStackTrace writes to stderr, which usually goes nowhere useful, and an empty catch loses the error entirely.'
65
+ remediation: 'Log with a logger at the right level, including the context; rethrow or handle.'
66
+ references: ['CWE-390', 'ASVS-7.4.1']
67
+
68
+ - id: JVM-004
69
+ title: Parameterised SQL / JPA criteria instead of concatenation
70
+ section: S15
71
+ section_title: Platform-Specific
72
+ severity: CRITICAL
73
+ class: security
74
+ applies_when: { fact: 'has:database' }
75
+ check:
76
+ kind: grep_absent
77
+ pattern: '(createQuery\(\s*[''"][^''"]*[''"]\s*\+|createNativeQuery\(\s*.*\+|Statement\.execute(Query)?\s*\(\s*.*\+|\+?\s*[''"]\s*(SELECT|INSERT|UPDATE|DELETE)[^''"]*[''"]\s*\+)'
78
+ include: ['**/*.{java,kt}']
79
+ exclude: ['**/src/test/**', '**/test/**']
80
+ flags: i
81
+ why: 'String-concatenated JPQL is exactly as injectable as raw SQL.'
82
+ remediation: 'Use named parameters (:name) or the Criteria API. Never build a query string from input.'
83
+ references: ['CWE-89', 'ASVS-5.3.4']
84
+
85
+ - id: JVM-005
86
+ title: Nullability is explicit
87
+ section: S15
88
+ section_title: Platform-Specific
89
+ severity: LOW
90
+ class: correctness
91
+ applies_when: { fact: 'lang:kotlin' }
92
+ check: { kind: manual }
93
+ evidence: 'Count of `!!` force-unwraps and platform types crossing into Kotlin from Java.'
94
+ why: 'Every !! is a NullPointerException with the compiler silenced.'
95
+ remediation: 'Prefer ?: with a default, requireNotNull with a message, or make the Java API annotated.'
96
+ references: ['Kotlin-Null-Safety']
97
+
98
+ - id: JVM-006
99
+ title: Static analysis runs in CI
100
+ section: S15
101
+ section_title: Platform-Specific
102
+ severity: MEDIUM
103
+ class: supply-chain
104
+ applies_when: { fact: 'has:ci' }
105
+ check:
106
+ kind: grep_present
107
+ pattern: '(spotbugs|pmd|checkstyle|errorprone|detekt|ktlint|sonar|snyk)'
108
+ include:
109
+ [
110
+ 'pom.xml',
111
+ 'build.gradle*',
112
+ '.github/workflows/*.yml',
113
+ '.github/workflows/*.yaml',
114
+ '.gitlab-ci.yml',
115
+ ]
116
+ flags: i
117
+ why: 'SpotBugs and Error Prone find real bugs, not just style nits, and they are free.'
118
+ remediation: 'Add SpotBugs/ErrorProne (Java) or detekt/ktlint (Kotlin) and fail on new high-priority findings.'
119
+ references: ['OpenSSF-Scorecard:SAST']
120
+
121
+ - id: JVM-007
122
+ title: Insecure deserialisation is avoided
123
+ section: S15
124
+ section_title: Platform-Specific
125
+ severity: HIGH
126
+ class: security
127
+ check:
128
+ kind: grep_absent
129
+ pattern: '(ObjectInputStream|readObject\s*\(|XMLDecoder|enableDefaultTyping|activateDefaultTyping|readValue\s*\(\s*.*,\s*Object\.class)'
130
+ include: ['**/*.{java,kt}']
131
+ exclude: ['**/src/test/**', '**/test/**']
132
+ why: 'Java deserialisation gadget chains are one of the most reliable RCE vectors in existence.'
133
+ remediation: 'Use JSON with an explicit schema and no polymorphic default typing; validate before binding.'
134
+ references: ['CWE-502', 'ASVS-5.5.1']
135
+
136
+ - id: JVM-008
137
+ title: JVM/container memory limits are respected
138
+ section: S15
139
+ section_title: Platform-Specific
140
+ severity: LOW
141
+ class: performance
142
+ applies_when: { fact: 'has:containers' }
143
+ check:
144
+ kind: grep_present
145
+ pattern: '(MaxRAMPercentage|UseContainerSupport|XX:\+UseContainerSupport|Xmx)'
146
+ include:
147
+ ['Dockerfile*', '**/*.sh', 'docker-compose*.yml', '**/*.{yaml,yml}', 'JAVA_TOOL_OPTIONS']
148
+ why: 'Without it the JVM sizes its heap from the host, not the cgroup, and gets OOM-killed.'
149
+ remediation: 'Set -XX:MaxRAMPercentage=75 (and leave the rest for off-heap).'
150
+ references: ['JVM-Container-Support']
@@ -0,0 +1,140 @@
1
+ id: stacks/ml-ai
2
+ title: ML / AI Systems
3
+ section: S15
4
+ section_title: Platform-Specific
5
+ description: 'Activated when PyTorch/TensorFlow/JAX, notebooks, or model artefacts are detected.'
6
+ version: '1.0'
7
+ skip_when: { fact: 'project:ml', op: 'absent' }
8
+
9
+ rules:
10
+ - id: ML-001
11
+ title: Training data is versioned
12
+ section: S15
13
+ section_title: Platform-Specific
14
+ severity: MEDIUM
15
+ class: maintainability
16
+ check:
17
+ kind: grep_present
18
+ pattern: '(dvc|lfs|lakefs|wandb\.Artifact|mlflow|dataset.*version|\.dvc/|data/raw)'
19
+ include: ['**/*.{py,yaml,yml,toml,md}', '.gitattributes', '.dvc/config']
20
+ exclude: ['**/node_modules/**']
21
+ flags: i
22
+ why: 'A model you cannot reproduce from its data is a result you cannot defend or debug.'
23
+ remediation: 'Version datasets with DVC or Git LFS; record the exact snapshot hash with every training run.'
24
+ references: ['DVC', 'Google-Rules-of-ML']
25
+
26
+ - id: ML-002
27
+ title: Model artefacts are versioned and registered
28
+ section: S15
29
+ section_title: Platform-Specific
30
+ severity: MEDIUM
31
+ class: operations
32
+ check: { kind: manual }
33
+ evidence: 'A model registry (MLflow, Weights & Biases, SageMaker) mapping deployed model → training run → data snapshot.'
34
+ why: 'When production degrades, the first question is "which model is running and what was it trained on?"'
35
+ remediation: 'Register every candidate model with metadata; deploy by registry version, never by copying files.'
36
+ references: ['MLflow', 'Google-Rules-of-ML']
37
+
38
+ - id: ML-003
39
+ title: Training is reproducible (seeded and pinned)
40
+ section: S15
41
+ section_title: Platform-Specific
42
+ severity: MEDIUM
43
+ class: correctness
44
+ check:
45
+ kind: grep_present
46
+ pattern: '(random_seed|manual_seed|set_seed|seed\s*=\s*\d+|deterministic\s*=\s*True|torch\.use_deterministic)'
47
+ include: ['**/*.py', '**/*.ipynb', '**/*.{yaml,yml}']
48
+ exclude: ['**/node_modules/**']
49
+ flags: i
50
+ why: 'Unseeded training means you cannot tell an improvement from noise.'
51
+ remediation: 'Seed every RNG (python, numpy, torch), pin the library versions, and record the environment.'
52
+ references: ['PyTorch-Reproducibility']
53
+
54
+ - id: ML-004
55
+ title: Model/data drift is monitored in production
56
+ section: S15
57
+ section_title: Platform-Specific
58
+ severity: MEDIUM
59
+ class: operations
60
+ applies_when: { fact: 'maturity:production' }
61
+ check: { kind: manual }
62
+ evidence: 'Input distribution monitoring and a performance/label-feedback loop with alert thresholds.'
63
+ why: 'Models do not crash when they degrade. They just get quietly worse, and nobody notices for a quarter.'
64
+ remediation: 'Log input feature distributions and prediction confidence; alert on drift beyond a threshold.'
65
+ references: ['Google-Rules-of-ML', 'NIST-AI-RMF']
66
+
67
+ - id: ML-005
68
+ title: Predictions are logged for audit
69
+ section: S15
70
+ section_title: Platform-Specific
71
+ severity: MEDIUM
72
+ class: compliance
73
+ applies_when: { fact: 'maturity:production' }
74
+ check: { kind: manual }
75
+ evidence: 'Per-request log of model version, input features (or a hash), output, and latency, retained per policy.'
76
+ why: 'You cannot investigate a harmful prediction, a bias complaint, or a regression without the record.'
77
+ remediation: 'Log predictions with a request ID and model version; keep PII out or pseudonymised.'
78
+ references: ['NIST-AI-RMF', 'EU-AI-Act-Art.12']
79
+
80
+ - id: ML-006
81
+ title: Bias/fairness has been evaluated
82
+ section: S15
83
+ section_title: Platform-Specific
84
+ severity: MEDIUM
85
+ class: compliance
86
+ check: { kind: manual }
87
+ evidence: 'Evaluation sliced across relevant protected or sensitive attributes, with the results recorded.'
88
+ why: 'Aggregate accuracy hides per-group failure. "95% accurate" can still mean one group is systematically wrong.'
89
+ remediation: 'Slice metrics by relevant cohorts before launch; document known limitations in a model card.'
90
+ references: ['NIST-AI-RMF', 'EU-AI-Act']
91
+
92
+ - id: ML-007
93
+ title: Notebooks are not the deployment path
94
+ section: S15
95
+ section_title: Platform-Specific
96
+ severity: MEDIUM
97
+ class: maintainability
98
+ check: { kind: manual }
99
+ evidence: "Whether training/inference runs from scripts or pipelines, or still from someone's notebook."
100
+ why: '"Run cells 1 through 14, then the one I added" is not a reproducible pipeline.'
101
+ remediation: 'Extract to versioned scripts and a pipeline definition; keep notebooks for exploration only.'
102
+ references: ['Google-Rules-of-ML']
103
+
104
+ - id: ML-008
105
+ title: Inference cost and latency are tracked
106
+ section: S15
107
+ section_title: Platform-Specific
108
+ severity: LOW
109
+ class: performance
110
+ applies_when: { fact: 'maturity:production' }
111
+ check: { kind: manual }
112
+ evidence: 'p50/p95 inference latency and cost per 1k predictions (GPU hours or API spend).'
113
+ why: 'LLM and GPU inference bills are the fastest-growing line item in most 2026 stacks.'
114
+ remediation: 'Instrument per-request token/compute cost; set budgets; cache and batch where possible.'
115
+ references: ['FinOps', 'OWASP-LLM06:2026']
116
+
117
+ - id: ML-009
118
+ title: Model files are not committed to git
119
+ section: S15
120
+ section_title: Platform-Specific
121
+ severity: MEDIUM
122
+ class: maintainability
123
+ check:
124
+ kind: tracked_absent
125
+ patterns:
126
+ [
127
+ '**/*.pt',
128
+ '**/*.pth',
129
+ '**/*.ckpt',
130
+ '**/*.h5',
131
+ '**/*.safetensors',
132
+ '**/*.onnx',
133
+ '**/*.bin',
134
+ '**/*.pb',
135
+ '**/checkpoints/**',
136
+ '**/*.gguf',
137
+ ]
138
+ why: 'Model weights are hundreds of megabytes to tens of gigabytes. Git will remember every version, forever.'
139
+ remediation: 'Store weights in a model registry or object storage (DVC, S3, Hugging Face Hub) and reference by digest.'
140
+ references: ['DVC', 'OpenSSF-Scorecard:Binary-Artifacts']
@@ -0,0 +1,121 @@
1
+ id: stacks/mobile
2
+ title: Mobile (iOS / Android / Cross-platform)
3
+ section: S15
4
+ section_title: Platform-Specific
5
+ description: 'Activated when iOS, Android, React Native, Expo, or Flutter is detected.'
6
+ version: '1.0'
7
+ skip_when:
8
+ all:
9
+ - { fact: 'platform:mobile', op: 'absent' }
10
+ - { fact: 'platform:ios', op: 'absent' }
11
+ - { fact: 'platform:android', op: 'absent' }
12
+
13
+ rules:
14
+ - id: MOB-001
15
+ title: Crash reporting is integrated
16
+ section: S15
17
+ section_title: Platform-Specific
18
+ severity: HIGH
19
+ class: operations
20
+ check:
21
+ kind: grep_present
22
+ pattern: '(Crashlytics|Sentry|BugSnag|Instabug|@sentry/react-native|firebase/crashlytics|crashReporting)'
23
+ include: ['**/*.{ts,tsx,js,json,plist,xml,gradle,kts,swift,kt,dart,yaml,yml}']
24
+ exclude: ['**/node_modules/**', '**/Pods/**']
25
+ flags: i
26
+ why: "On mobile you cannot watch the user's screen. Without crash reporting, your bug reports are one-star reviews."
27
+ remediation: 'Add Crashlytics or Sentry with symbol/dSYM upload so stack traces are readable.'
28
+ references: ['DORA:Monitoring']
29
+
30
+ - id: MOB-002
31
+ title: No hardcoded API secrets in app bundles
32
+ section: S15
33
+ section_title: Platform-Specific
34
+ severity: CRITICAL
35
+ class: security
36
+ check:
37
+ kind: grep_absent
38
+ pattern: '(api[_-]?key|secret|token|password)\s*[:=]\s*[''"][A-Za-z0-9._-]{16,}[''"]'
39
+ include: ['**/*.{swift,kt,java,dart,ts,tsx,js,plist,xml,gradle,json}']
40
+ exclude:
41
+ ['**/node_modules/**', '**/Pods/**', '**/*Test*', '**/android/build/**', '**/ios/Pods/**']
42
+ flags: i
43
+ why: 'Anything in an app bundle is extractable in about four minutes with apktool or Hopper.'
44
+ remediation: 'Keep secrets server-side; use attestation (App Check / DeviceCheck) and short-lived tokens.'
45
+ references: ['OWASP-MASVS-STORAGE-1', 'CWE-798']
46
+
47
+ - id: MOB-003
48
+ title: Sensitive data is not stored insecurely on device
49
+ section: S15
50
+ section_title: Platform-Specific
51
+ severity: HIGH
52
+ class: security
53
+ check: { kind: manual }
54
+ evidence: 'Where tokens/PII live: Keychain/Keystore/EncryptedSharedPreferences, not AsyncStorage, UserDefaults, or a plaintext SQLite file.'
55
+ why: 'On a rooted or backed-up device, plaintext local storage is a data breach with extra steps.'
56
+ remediation: 'Use Keychain (iOS) / Keystore-backed EncryptedSharedPreferences (Android); never AsyncStorage for secrets.'
57
+ references: ['OWASP-MASVS-STORAGE-2', 'CWE-311']
58
+
59
+ - id: MOB-004
60
+ title: Network security configuration is explicit
61
+ section: S15
62
+ section_title: Platform-Specific
63
+ severity: MEDIUM
64
+ class: security
65
+ check: { kind: manual }
66
+ evidence: 'ATS settings (iOS Info.plist) and network-security-config / cleartextTrafficPermitted (Android).'
67
+ why: 'Both platforms default to permissive in older templates, which means plaintext HTTP and MITM.'
68
+ remediation: 'Disallow cleartext traffic; pin certificates only if you can commit to a rotation process.'
69
+ references: ['OWASP-MASVS-NETWORK-1', 'CWE-319']
70
+
71
+ - id: MOB-005
72
+ title: Permissions are requested minimally and with rationale
73
+ section: S15
74
+ section_title: Platform-Specific
75
+ severity: MEDIUM
76
+ class: compliance
77
+ check: { kind: manual }
78
+ evidence: 'Declared permissions vs. permissions actually used; where and when each is requested.'
79
+ why: 'Over-broad permissions are the fastest route to an app-store rejection and a privacy headline.'
80
+ remediation: 'Request at the moment of use with an explanation; drop anything unused.'
81
+ references: ['OWASP-MASVS-PLATFORM-1', 'App-Store-Review']
82
+
83
+ - id: MOB-006
84
+ title: Offline behaviour is handled
85
+ section: S15
86
+ section_title: Platform-Specific
87
+ severity: MEDIUM
88
+ class: correctness
89
+ check: { kind: manual }
90
+ evidence: 'Behaviour when the network is unavailable mid-action: queued, retried, or failed with a clear message?'
91
+ why: 'Mobile networks drop constantly. An app that assumes connectivity corrupts data.'
92
+ remediation: 'Queue mutations locally and reconcile on reconnect; show a clear offline state.'
93
+ references: ['OWASP-MASVS']
94
+
95
+ - id: MOB-007
96
+ title: Privacy manifests / data collection disclosure are present
97
+ section: S15
98
+ section_title: Platform-Specific
99
+ severity: HIGH
100
+ class: compliance
101
+ applies_when:
102
+ any:
103
+ - { fact: 'platform:ios' }
104
+ - { fact: 'platform:android' }
105
+ check: { kind: manual }
106
+ evidence: 'PrivacyInfo.xcprivacy (Apple) and the Play Data Safety declaration, both matching what the app actually collects.'
107
+ why: 'Apple rejects apps with missing or inaccurate privacy manifests. The declaration is also legally binding.'
108
+ remediation: 'Enumerate every SDK that collects data, then fill in the manifest to match reality.'
109
+ references: ['Apple-Privacy-Manifest', 'Play-Data-Safety']
110
+
111
+ - id: MOB-008
112
+ title: Tested on multiple screen sizes and OS versions
113
+ section: S15
114
+ section_title: Platform-Specific
115
+ severity: MEDIUM
116
+ class: correctness
117
+ check: { kind: manual }
118
+ evidence: 'Device matrix you test against, and whether the min-supported OS version is still patched upstream.'
119
+ why: '"Works on my iPhone 15 Pro" is not a test matrix.'
120
+ remediation: 'Test at least: smallest supported phone, a tablet, one dark-mode run, and the oldest supported OS.'
121
+ references: ['OWASP-MASVS']