@dailephd/my-dev-kit-lab 0.3.3 → 0.4.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 (150) hide show
  1. package/README.md +46 -269
  2. package/dist/scripts/security/validate.js +177 -70
  3. package/dist/src/audits/codeRot/detectors/architectureDriftDetector.js +5 -2
  4. package/dist/src/audits/codeRot/detectors/securityValidationAssumptionRotDetector.js +5 -2
  5. package/dist/src/audits/core/collectSourceFacts.js +21 -10
  6. package/dist/src/mobile/android/audit/androidFinding.js +68 -0
  7. package/dist/src/mobile/android/audit/checkResultBuilder.js +97 -0
  8. package/dist/src/mobile/android/audit/deepLinkAudit.js +183 -0
  9. package/dist/src/mobile/android/audit/exportedComponentAudit.js +183 -0
  10. package/dist/src/mobile/android/audit/intentFilterAudit.js +128 -0
  11. package/dist/src/mobile/android/audit/permissionAudit.js +142 -0
  12. package/dist/src/mobile/android/detect/androidTargetDetection.js +18 -0
  13. package/dist/src/mobile/android/detect/buildFileEvidence.js +36 -0
  14. package/dist/src/mobile/android/detect/classify.js +150 -0
  15. package/dist/src/mobile/android/detect/detectAndroidProject.js +252 -0
  16. package/dist/src/mobile/android/detect/settingsModules.js +60 -0
  17. package/dist/src/mobile/android/detect/sourceEvidence.js +92 -0
  18. package/dist/src/mobile/android/detect/traversal.js +79 -0
  19. package/dist/src/mobile/android/detect/versionCatalogEvidence.js +37 -0
  20. package/dist/src/mobile/android/detection.js +28 -0
  21. package/dist/src/mobile/android/gradle/gradleMetadataCheckResult.js +120 -0
  22. package/dist/src/mobile/android/gradle/moduleMetadataExtractor.js +137 -0
  23. package/dist/src/mobile/android/gradle/pluginVersionExtractor.js +40 -0
  24. package/dist/src/mobile/android/gradle/readAndroidGradleMetadata.js +210 -0
  25. package/dist/src/mobile/android/gradle/releaseMetadataSummary.js +64 -0
  26. package/dist/src/mobile/android/gradle/settingsMetadata.js +20 -0
  27. package/dist/src/mobile/android/gradle/types.js +1 -0
  28. package/dist/src/mobile/android/gradle/validate/executor.js +9 -0
  29. package/dist/src/mobile/android/gradle/validate/operationCheckResult.js +99 -0
  30. package/dist/src/mobile/android/gradle/validate/operations.js +68 -0
  31. package/dist/src/mobile/android/gradle/validate/planner.js +49 -0
  32. package/dist/src/mobile/android/gradle/validate/runOptionalGradleValidation.js +113 -0
  33. package/dist/src/mobile/android/gradle/validate/targetMutation.js +45 -0
  34. package/dist/src/mobile/android/gradle/validate/taskListParser.js +24 -0
  35. package/dist/src/mobile/android/gradle/versionCatalogMetadata.js +94 -0
  36. package/dist/src/mobile/android/gradle/wrapperMetadata.js +32 -0
  37. package/dist/src/mobile/android/manifest/parseAndroidManifest.js +341 -0
  38. package/dist/src/mobile/android/manifest/types.js +29 -0
  39. package/dist/src/mobile/android/manifest/xml/parseXml.js +313 -0
  40. package/dist/src/mobile/android/profile.js +29 -0
  41. package/dist/src/mobile/android/report/model.js +104 -0
  42. package/dist/src/mobile/android/report/renderAndroidReport.js +223 -0
  43. package/dist/src/mobile/android/report/writeAndroidReportFiles.js +23 -0
  44. package/dist/src/mobile/android/targetMetadata.js +27 -0
  45. package/dist/src/mobile/android/validate/androidVerdict.js +248 -0
  46. package/dist/src/mobile/android/validate/detectionAndImmutabilityCheckResults.js +177 -0
  47. package/dist/src/mobile/android/validate/playReadinessChecklist.js +145 -0
  48. package/dist/src/mobile/android/validate/validateAndroidTarget.js +172 -0
  49. package/dist/src/mobile/android/validation/checkResult.js +51 -0
  50. package/dist/src/mobile/android/validation/result.js +25 -0
  51. package/dist/src/mobile/index.js +42 -0
  52. package/dist/src/mobile/types.js +27 -0
  53. package/dist/src/securityValidation/attackScenarios/attackProfile.js +2 -0
  54. package/dist/src/securityValidation/attackScenarios/profiles/androidProfile.js +20 -0
  55. package/dist/src/securityValidation/validate/cliOptions.js +46 -2
  56. package/package.json +2 -1
  57. package/tests/fixtures/android/compose-app/app/build.gradle.kts +21 -0
  58. package/tests/fixtures/android/compose-app/app/src/main/AndroidManifest.xml +22 -0
  59. package/tests/fixtures/android/compose-app/app/src/main/java/com/example/composeapp/MainActivity.kt +11 -0
  60. package/tests/fixtures/android/compose-app/build.gradle.kts +4 -0
  61. package/tests/fixtures/android/compose-app/gradle/wrapper/gradle-wrapper.properties +4 -0
  62. package/tests/fixtures/android/compose-app/settings.gradle.kts +2 -0
  63. package/tests/fixtures/android/gradle-metadata-fixtures/catalog-only-plugin/build.gradle.kts +3 -0
  64. package/tests/fixtures/android/gradle-metadata-fixtures/catalog-only-plugin/gradle/libs.versions.toml +5 -0
  65. package/tests/fixtures/android/gradle-metadata-fixtures/comment-safety.gradle.kts +21 -0
  66. package/tests/fixtures/android/gradle-metadata-fixtures/dynamic-values.gradle.kts +17 -0
  67. package/tests/fixtures/android/gradle-metadata-fixtures/groovy-literal.gradle +23 -0
  68. package/tests/fixtures/android/gradle-metadata-fixtures/wrapper-malformed.properties +4 -0
  69. package/tests/fixtures/android/gradle-metadata-fixtures/wrapper-missing-url.properties +3 -0
  70. package/tests/fixtures/android/gradle-metadata-fixtures/wrapper-with-checksum.properties +5 -0
  71. package/tests/fixtures/android/java-app/app/build.gradle +16 -0
  72. package/tests/fixtures/android/java-app/app/src/main/AndroidManifest.xml +19 -0
  73. package/tests/fixtures/android/java-app/app/src/main/java/com/example/javaapp/MainActivity.java +4 -0
  74. package/tests/fixtures/android/java-app/build.gradle +3 -0
  75. package/tests/fixtures/android/java-app/gradle/wrapper/gradle-wrapper.properties +4 -0
  76. package/tests/fixtures/android/java-app/settings.gradle +2 -0
  77. package/tests/fixtures/android/library/build.gradle.kts +3 -0
  78. package/tests/fixtures/android/library/gradle/wrapper/gradle-wrapper.properties +4 -0
  79. package/tests/fixtures/android/library/mylibrary/build.gradle.kts +13 -0
  80. package/tests/fixtures/android/library/mylibrary/src/main/AndroidManifest.xml +2 -0
  81. package/tests/fixtures/android/library/settings.gradle.kts +2 -0
  82. package/tests/fixtures/android/manifest-audit-fixtures/incomplete-intent-filter.xml +14 -0
  83. package/tests/fixtures/android/manifest-audit-fixtures/malformed-exported.xml +10 -0
  84. package/tests/fixtures/android/manifest-audit-fixtures/malformed.xml +5 -0
  85. package/tests/fixtures/android/manifest-audit-fixtures/missing-application.xml +7 -0
  86. package/tests/fixtures/android/manifest-audit-fixtures/missing-component-name.xml +11 -0
  87. package/tests/fixtures/android/manifest-audit-fixtures/permission-coverage.xml +29 -0
  88. package/tests/fixtures/android/manifest-audit-fixtures/risky-components.xml +62 -0
  89. package/tests/fixtures/android/manifest-audit-fixtures/risky-deep-links.xml +45 -0
  90. package/tests/fixtures/android/manifest-audit-fixtures/unresolved-placeholder.xml +14 -0
  91. package/tests/fixtures/android/manifest-audit-fixtures/unusual-namespace-prefix.xml +11 -0
  92. package/tests/fixtures/android/multi-module/app/build.gradle.kts +22 -0
  93. package/tests/fixtures/android/multi-module/app/src/androidTest/java/com/example/multimodule/app/ExampleInstrumentedTest.kt +3 -0
  94. package/tests/fixtures/android/multi-module/app/src/main/AndroidManifest.xml +11 -0
  95. package/tests/fixtures/android/multi-module/app/src/test/java/com/example/multimodule/app/ExampleUnitTest.kt +3 -0
  96. package/tests/fixtures/android/multi-module/build.gradle.kts +5 -0
  97. package/tests/fixtures/android/multi-module/core/build.gradle.kts +14 -0
  98. package/tests/fixtures/android/multi-module/core/src/main/AndroidManifest.xml +2 -0
  99. package/tests/fixtures/android/multi-module/feature-login/build.gradle.kts +18 -0
  100. package/tests/fixtures/android/multi-module/feature-login/src/main/AndroidManifest.xml +2 -0
  101. package/tests/fixtures/android/multi-module/gradle/libs.versions.toml +8 -0
  102. package/tests/fixtures/android/multi-module/gradle/wrapper/gradle-wrapper.properties +4 -0
  103. package/tests/fixtures/android/multi-module/settings.gradle.kts +4 -0
  104. package/tests/fixtures/android/non-android-gradle/build.gradle.kts +3 -0
  105. package/tests/fixtures/android/non-android-gradle/core/build.gradle.kts +3 -0
  106. package/tests/fixtures/android/non-android-gradle/core/src/main/java/com/example/core/Core.java +4 -0
  107. package/tests/fixtures/android/non-android-gradle/gradle/wrapper/gradle-wrapper.properties +4 -0
  108. package/tests/fixtures/android/non-android-gradle/settings.gradle.kts +2 -0
  109. package/tests/fixtures/android/partial/app/build.gradle.kts +7 -0
  110. package/tests/fixtures/android/partial/settings.gradle.kts +2 -0
  111. package/tests/fixtures/android/xml-view-app/app/build.gradle.kts +17 -0
  112. package/tests/fixtures/android/xml-view-app/app/src/main/AndroidManifest.xml +31 -0
  113. package/tests/fixtures/android/xml-view-app/app/src/main/kotlin/com/example/xmlviewapp/MainActivity.kt +7 -0
  114. package/tests/fixtures/android/xml-view-app/app/src/main/res/layout/activity_main.xml +4 -0
  115. package/tests/fixtures/android/xml-view-app/build.gradle.kts +4 -0
  116. package/tests/fixtures/android/xml-view-app/gradle/wrapper/gradle-wrapper.properties +4 -0
  117. package/tests/fixtures/android/xml-view-app/settings.gradle.kts +2 -0
  118. package/tests/fixtures/audits/mixed-language/mixed-docs-claims/README.md +16 -0
  119. package/tests/fixtures/audits/mixed-language/mixed-docs-claims/build.gradle.kts +4 -0
  120. package/tests/fixtures/audits/mixed-language/mixed-docs-claims/src/main/java/com/example/Widget.java +7 -0
  121. package/tests/fixtures/audits/mixed-language/mixed-docs-claims/src/main/kotlin/com/example/Gadget.kt +7 -0
  122. package/tests/fixtures/audits/mixed-language/mixed-generated-vendor/README.md +10 -0
  123. package/tests/fixtures/audits/mixed-language/mixed-generated-vendor/build/Main.class +1 -0
  124. package/tests/fixtures/audits/mixed-language/mixed-generated-vendor/dist/index.js +1 -0
  125. package/tests/fixtures/audits/mixed-language/mixed-generated-vendor/generated/schema.ts +1 -0
  126. package/tests/fixtures/audits/mixed-language/mixed-generated-vendor/src/index.ts +3 -0
  127. package/tests/fixtures/audits/mixed-language/mixed-generated-vendor/vendor/lib.py +2 -0
  128. package/tests/fixtures/audits/mixed-language/mixed-java-kotlin/README.md +10 -0
  129. package/tests/fixtures/audits/mixed-language/mixed-java-kotlin/build.gradle.kts +8 -0
  130. package/tests/fixtures/audits/mixed-language/mixed-java-kotlin/settings.gradle.kts +1 -0
  131. package/tests/fixtures/audits/mixed-language/mixed-java-kotlin/src/main/java/com/example/FooService.java +7 -0
  132. package/tests/fixtures/audits/mixed-language/mixed-java-kotlin/src/main/kotlin/com/example/BarService.kt +7 -0
  133. package/tests/fixtures/audits/mixed-language/mixed-java-kotlin/src/test/java/com/example/FooServiceTest.java +8 -0
  134. package/tests/fixtures/audits/mixed-language/mixed-java-kotlin/src/test/kotlin/com/example/BarServiceTest.kt +8 -0
  135. package/tests/fixtures/audits/mixed-language/mixed-python-ts/README.md +8 -0
  136. package/tests/fixtures/audits/mixed-language/mixed-python-ts/src/app.py +7 -0
  137. package/tests/fixtures/audits/mixed-language/mixed-python-ts/src/index.ts +3 -0
  138. package/tests/fixtures/audits/mixed-language/mixed-python-ts/tests/test_app.py +5 -0
  139. package/tests/fixtures/audits/mixed-language/mixed-ts-js/README.md +7 -0
  140. package/tests/fixtures/audits/mixed-language/mixed-ts-js/src/helper.js +5 -0
  141. package/tests/fixtures/audits/mixed-language/mixed-ts-js/src/index.ts +3 -0
  142. package/tests/fixtures/audits/mixed-language/mixed-ts-js/tests/index.sample.ts +5 -0
  143. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/README.md +10 -0
  144. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/build.gradle.kts +4 -0
  145. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/pytests/test_app.py +5 -0
  146. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/src/app.py +2 -0
  147. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/src/helper.js +5 -0
  148. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/src/index.ts +3 -0
  149. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/src/main/java/com/example/Multiplier.java +7 -0
  150. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/src/main/kotlin/com/example/Divider.kt +7 -0
@@ -0,0 +1,177 @@
1
+ // ---------------------------------------------------------------------------
2
+ // v0.4.0 Batch 5 — the three required check families that no prior batch
3
+ // produced an AndroidCheckResult for: project detection, manifest parsing
4
+ // (as distinct from the audits that consume parsed manifests), and target
5
+ // immutability evidence (agents.txt Batch 5 section 10.1).
6
+ // ---------------------------------------------------------------------------
7
+ export function buildAndroidDetectionCheckResult(detection) {
8
+ const sourcePaths = [...detection.gradleSettingsFiles, ...detection.rootBuildFiles].sort((a, b) => a.localeCompare(b));
9
+ if (detection.projectKind === "non-android") {
10
+ return {
11
+ id: "android-project-detection",
12
+ category: "android-detection",
13
+ title: "Android project detection",
14
+ status: "unsupported",
15
+ requirementLevel: "optional",
16
+ ran: true,
17
+ skipped: true,
18
+ skipInfo: {
19
+ checkId: "android-project-detection",
20
+ reason: "Target was not detected as an Android project.",
21
+ requirementLevel: "optional",
22
+ missingCapability: "android-evidence",
23
+ verdictImpact: "does not apply to a non-Android target",
24
+ recommendedNextAction: "Confirm the target is an Android Gradle project.",
25
+ },
26
+ evidence: detection.evidence,
27
+ findings: [],
28
+ warnings: detection.warnings,
29
+ errors: [],
30
+ sourcePaths,
31
+ confidence: detection.confidence,
32
+ environmentRequirements: [],
33
+ };
34
+ }
35
+ const isPartial = detection.projectKind === "partial" || detection.projectKind === "unknown" || detection.partialOrUnsupportedStructure;
36
+ return {
37
+ id: "android-project-detection",
38
+ category: "android-detection",
39
+ title: "Android project detection",
40
+ status: isPartial ? "inconclusive" : "passed",
41
+ requirementLevel: "required",
42
+ ran: true,
43
+ skipped: false,
44
+ evidence: detection.evidence,
45
+ findings: [],
46
+ warnings: detection.warnings,
47
+ errors: [],
48
+ sourcePaths,
49
+ confidence: detection.confidence,
50
+ environmentRequirements: [],
51
+ };
52
+ }
53
+ const MALFORMED_PREFIX = "Malformed XML:";
54
+ export function buildAndroidManifestParsingCheckResult(detection, manifests) {
55
+ const sourcePaths = manifests.map((m) => m.manifestPath);
56
+ if (detection.projectKind === "non-android") {
57
+ return {
58
+ id: "android-manifest-parsing",
59
+ category: "android-manifest",
60
+ title: "Android manifest parsing",
61
+ status: "unsupported",
62
+ requirementLevel: "optional",
63
+ ran: false,
64
+ skipped: true,
65
+ skipInfo: {
66
+ checkId: "android-manifest-parsing",
67
+ reason: "Target was not detected as an Android project.",
68
+ requirementLevel: "optional",
69
+ missingCapability: "android-detection",
70
+ verdictImpact: "does not apply to a non-Android target",
71
+ recommendedNextAction: "Confirm the target is an Android Gradle project.",
72
+ },
73
+ evidence: [],
74
+ findings: [],
75
+ warnings: [],
76
+ errors: [],
77
+ sourcePaths: [],
78
+ confidence: "unknown",
79
+ environmentRequirements: [],
80
+ };
81
+ }
82
+ if (manifests.length === 0) {
83
+ return {
84
+ id: "android-manifest-parsing",
85
+ category: "android-manifest",
86
+ title: "Android manifest parsing",
87
+ status: "inconclusive",
88
+ requirementLevel: "required",
89
+ ran: true,
90
+ skipped: false,
91
+ evidence: [],
92
+ findings: [],
93
+ warnings: ["No AndroidManifest.xml was found for this target."],
94
+ errors: [],
95
+ sourcePaths: [],
96
+ confidence: "unknown",
97
+ environmentRequirements: [],
98
+ };
99
+ }
100
+ const malformed = manifests.filter((m) => m.manifest.parseWarnings.some((w) => w.startsWith(MALFORMED_PREFIX)));
101
+ const allMalformed = malformed.length === manifests.length;
102
+ const warnings = manifests.flatMap((m) => m.manifest.parseWarnings);
103
+ if (allMalformed) {
104
+ return {
105
+ id: "android-manifest-parsing",
106
+ category: "android-manifest",
107
+ title: "Android manifest parsing",
108
+ status: "error",
109
+ requirementLevel: "required",
110
+ ran: true,
111
+ skipped: false,
112
+ evidence: [],
113
+ findings: [],
114
+ warnings,
115
+ errors: malformed.flatMap((m) => m.manifest.parseWarnings.filter((w) => w.startsWith(MALFORMED_PREFIX))),
116
+ sourcePaths,
117
+ confidence: "unknown",
118
+ environmentRequirements: [],
119
+ };
120
+ }
121
+ const confidence = malformed.length > 0 ? "medium" : "high";
122
+ return {
123
+ id: "android-manifest-parsing",
124
+ category: "android-manifest",
125
+ title: "Android manifest parsing",
126
+ status: "passed",
127
+ requirementLevel: "required",
128
+ ran: true,
129
+ skipped: false,
130
+ evidence: [`${manifests.length} manifest(s) parsed independently (not merged)`],
131
+ findings: [],
132
+ warnings,
133
+ errors: [],
134
+ sourcePaths,
135
+ confidence,
136
+ environmentRequirements: [],
137
+ };
138
+ }
139
+ export function buildAndroidTargetImmutabilityCheckResult(mutation) {
140
+ if (!mutation.comparable) {
141
+ return {
142
+ id: "android-target-immutability",
143
+ category: "android-target-immutability",
144
+ title: "Android target immutability",
145
+ status: "inconclusive",
146
+ requirementLevel: "optional",
147
+ ran: true,
148
+ skipped: false,
149
+ evidence: [],
150
+ findings: [],
151
+ warnings: [`Target mutation evidence unavailable: ${mutation.reason ?? "unknown reason"}`],
152
+ errors: [],
153
+ sourcePaths: [],
154
+ confidence: "unknown",
155
+ environmentRequirements: [],
156
+ targetModificationObserved: undefined,
157
+ };
158
+ }
159
+ const unexpected = mutation.unexpectedChanges.length > 0;
160
+ return {
161
+ id: "android-target-immutability",
162
+ category: "android-target-immutability",
163
+ title: "Android target immutability",
164
+ status: unexpected ? "failed" : "passed",
165
+ requirementLevel: "optional",
166
+ ran: true,
167
+ skipped: false,
168
+ evidence: mutation.expectedGeneratedChanges.map((p) => `generatedOutput=${p}`),
169
+ findings: [],
170
+ warnings: unexpected ? [`Unexpected target change(s): ${mutation.unexpectedChanges.join(", ")}`] : [],
171
+ errors: [],
172
+ sourcePaths: [],
173
+ confidence: "high",
174
+ environmentRequirements: [],
175
+ targetModificationObserved: unexpected,
176
+ };
177
+ }
@@ -0,0 +1,145 @@
1
+ // ---------------------------------------------------------------------------
2
+ // v0.4.0 Batch 5 — bounded Play-readiness checklist placeholders (agents.txt
3
+ // Batch 5 section 14).
4
+ //
5
+ // Informational and planning-oriented only. Never claims Google Play
6
+ // approval, compliance, upload readiness, or publication readiness — no
7
+ // network access, no live policy lookup. Manual/policy placeholder items are
8
+ // always "manual-check-required" regardless of local evidence.
9
+ // ---------------------------------------------------------------------------
10
+ export const ANDROID_PLAY_READINESS_ITEM_STATUSES = [
11
+ "confirmed",
12
+ "missing",
13
+ "unresolved",
14
+ "manual-check-required",
15
+ "not-applicable",
16
+ ];
17
+ const MANUAL_POLICY_ITEMS = [
18
+ { id: "privacy-policy-required", title: "Privacy policy", detail: "Privacy policy requirement must be assessed manually." },
19
+ { id: "play-data-safety-declaration", title: "Data Safety declaration", detail: "Google Play Data Safety declaration must be completed manually." },
20
+ {
21
+ id: "sensitive-permission-justification",
22
+ title: "Sensitive permission justification",
23
+ detail: "Sensitive or restricted permission declarations may require additional forms or justification.",
24
+ },
25
+ { id: "store-listing-content", title: "Store listing content", detail: "Store listing content must be prepared manually." },
26
+ { id: "release-notes", title: "Release notes", detail: "Release notes must be prepared." },
27
+ { id: "app-access-instructions", title: "App access instructions", detail: "App access instructions may be required." },
28
+ { id: "content-rating", title: "Content rating", detail: "Content rating must be completed." },
29
+ {
30
+ id: "target-audience-content-declarations",
31
+ title: "Target audience and content",
32
+ detail: "Target audience and content declarations must be completed.",
33
+ },
34
+ { id: "ads-declaration", title: "Ads declaration", detail: "Ads declaration may be required." },
35
+ {
36
+ id: "testing-track-account-requirements",
37
+ title: "Testing track and account requirements",
38
+ detail: "Testing-track and account requirements must be checked separately.",
39
+ },
40
+ {
41
+ id: "current-play-policy-review",
42
+ title: "Current Google Play policy review",
43
+ detail: "Current Google Play policies must be reviewed at publication time. This tool performs no live policy lookup.",
44
+ },
45
+ ];
46
+ function itemsForLibrary() {
47
+ const applicationItemIds = [
48
+ "app-module-identified",
49
+ "application-id-resolved",
50
+ "namespace-resolved",
51
+ "version-code-resolved",
52
+ "version-name-resolved",
53
+ "min-sdk-resolved",
54
+ "target-sdk-resolved",
55
+ "compile-sdk-resolved",
56
+ "target-sdk-policy-check",
57
+ "launcher-activity-identified",
58
+ "application-label-present",
59
+ "application-icon-present",
60
+ "adaptive-round-icon-evidence",
61
+ "permissions-summary-available",
62
+ ];
63
+ const items = applicationItemIds.map((id) => ({
64
+ id,
65
+ title: id,
66
+ status: "not-applicable",
67
+ detail: "Google Play application readiness does not apply directly to a library artifact.",
68
+ }));
69
+ for (const policyItem of MANUAL_POLICY_ITEMS) {
70
+ items.push({ ...policyItem, status: "not-applicable" });
71
+ }
72
+ return items;
73
+ }
74
+ export function buildAndroidPlayReadinessChecklist(input) {
75
+ if (input.isNonAndroid) {
76
+ return {
77
+ applicable: false,
78
+ note: "This target was not detected as an Android project. Play-readiness checklist items are not applicable.",
79
+ items: itemsForLibrary().map((item) => ({ ...item, detail: "Target was not detected as an Android project." })),
80
+ };
81
+ }
82
+ if (input.isLibraryOnly) {
83
+ return {
84
+ applicable: false,
85
+ note: "This target is a pure Android library. Play-readiness checklist items are not applicable to a library artifact.",
86
+ items: itemsForLibrary(),
87
+ };
88
+ }
89
+ const { releaseMetadata, manifests } = input;
90
+ const hasLauncher = manifests.some((m) => Boolean(m.launcherActivityName));
91
+ const hasLabel = manifests.some((m) => Boolean(m.application.label));
92
+ const hasIcon = manifests.some((m) => Boolean(m.application.iconRef));
93
+ const hasAdaptiveIconEvidence = manifests.some((m) => Boolean(m.application.adaptiveIconEvidence));
94
+ const permissionsAvailable = manifests.length > 0;
95
+ const resolvedOrManual = (value, unresolvedFieldName) => {
96
+ if (value !== undefined && value !== null && value !== "")
97
+ return "confirmed";
98
+ const wasAttempted = releaseMetadata.unresolvedFields.some((f) => f.startsWith(`${unresolvedFieldName}:`));
99
+ return wasAttempted ? "unresolved" : "missing";
100
+ };
101
+ const items = [
102
+ {
103
+ id: "app-module-identified",
104
+ title: "Application module identified",
105
+ status: releaseMetadata.applicationModulePath ? "confirmed" : "missing",
106
+ detail: releaseMetadata.applicationModuleSelectionNote,
107
+ },
108
+ { id: "application-id-resolved", title: "applicationId resolved", status: resolvedOrManual(releaseMetadata.applicationId, "applicationId"), detail: releaseMetadata.applicationId ?? "Not statically resolved." },
109
+ { id: "namespace-resolved", title: "namespace resolved", status: resolvedOrManual(releaseMetadata.namespace, "namespace"), detail: releaseMetadata.namespace ?? "Not statically resolved." },
110
+ { id: "version-code-resolved", title: "versionCode resolved", status: resolvedOrManual(releaseMetadata.versionCode, "versionCode"), detail: String(releaseMetadata.versionCode ?? "Not statically resolved.") },
111
+ { id: "version-name-resolved", title: "versionName resolved", status: resolvedOrManual(releaseMetadata.versionName, "versionName"), detail: releaseMetadata.versionName ?? "Not statically resolved." },
112
+ { id: "min-sdk-resolved", title: "minSdk resolved", status: resolvedOrManual(releaseMetadata.minSdk, "minSdk"), detail: String(releaseMetadata.minSdk ?? "Not statically resolved.") },
113
+ { id: "target-sdk-resolved", title: "targetSdk resolved", status: resolvedOrManual(releaseMetadata.targetSdk, "targetSdk"), detail: String(releaseMetadata.targetSdk ?? "Not statically resolved.") },
114
+ { id: "compile-sdk-resolved", title: "compileSdk resolved", status: resolvedOrManual(releaseMetadata.compileSdk, "compileSdk"), detail: String(releaseMetadata.compileSdk ?? "Not statically resolved.") },
115
+ {
116
+ id: "target-sdk-policy-check",
117
+ title: "Current Google Play target SDK policy",
118
+ status: "manual-check-required",
119
+ detail: "Current Google Play target SDK policy must be checked separately — not evaluated by this tool.",
120
+ },
121
+ { id: "launcher-activity-identified", title: "Launcher activity identified", status: hasLauncher ? "confirmed" : "missing", detail: hasLauncher ? "Launcher activity found in a parsed manifest." : "No launcher activity evidence found." },
122
+ { id: "application-label-present", title: "Application label reference present", status: hasLabel ? "confirmed" : "missing", detail: hasLabel ? "android:label present." : "No android:label found on <application>." },
123
+ { id: "application-icon-present", title: "Application icon reference present", status: hasIcon ? "confirmed" : "missing", detail: hasIcon ? "android:icon present." : "No android:icon found on <application>." },
124
+ {
125
+ id: "adaptive-round-icon-evidence",
126
+ title: "Round/adaptive icon evidence",
127
+ status: hasAdaptiveIconEvidence ? "confirmed" : "unresolved",
128
+ detail: "Adaptive/round icon resources are not resolved by this tool; only direct manifest evidence, if any, is reflected here.",
129
+ },
130
+ {
131
+ id: "permissions-summary-available",
132
+ title: "Permissions summary available",
133
+ status: permissionsAvailable ? "confirmed" : "missing",
134
+ detail: "See the permissions audit section for the full declared-permission summary.",
135
+ },
136
+ ];
137
+ for (const policyItem of MANUAL_POLICY_ITEMS) {
138
+ items.push({ ...policyItem, status: "manual-check-required" });
139
+ }
140
+ return {
141
+ applicable: true,
142
+ note: "Bounded, local-evidence-only checklist. Confirms what static analysis found; every policy/manual item still requires human review before a Play Store submission. No live Google Play policy lookup was performed.",
143
+ items,
144
+ };
145
+ }
@@ -0,0 +1,172 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { resolveLocalProjectTarget } from "../../../core/localProjectTarget.js";
4
+ import { detectAndroidProject } from "../detect/detectAndroidProject.js";
5
+ import { createAndroidProfile } from "../profile.js";
6
+ import { createAndroidTargetMetadata } from "../targetMetadata.js";
7
+ import { parseAllAndroidManifests } from "../manifest/parseAndroidManifest.js";
8
+ import { auditAndroidPermissions } from "../audit/permissionAudit.js";
9
+ import { auditAndroidExportedComponents } from "../audit/exportedComponentAudit.js";
10
+ import { auditAndroidIntentFilters } from "../audit/intentFilterAudit.js";
11
+ import { auditAndroidDeepLinks } from "../audit/deepLinkAudit.js";
12
+ import { readAndroidGradleMetadata } from "../gradle/readAndroidGradleMetadata.js";
13
+ import { buildAndroidGradleMetadataCheckResult } from "../gradle/gradleMetadataCheckResult.js";
14
+ import { buildAndroidReleaseMetadataSummary } from "../gradle/releaseMetadataSummary.js";
15
+ import { runOptionalGradleValidation } from "../gradle/validate/runOptionalGradleValidation.js";
16
+ import { captureTargetSnapshot, buildTargetMutationReport } from "../gradle/validate/targetMutation.js";
17
+ import { buildAndroidPlayReadinessChecklist } from "./playReadinessChecklist.js";
18
+ import { calculateAndroidVerdict } from "./androidVerdict.js";
19
+ import { buildAndroidDetectionCheckResult, buildAndroidManifestParsingCheckResult, buildAndroidTargetImmutabilityCheckResult, } from "./detectionAndImmutabilityCheckResults.js";
20
+ import { ANDROID_VALIDATION_ARTIFACT_TYPE, ANDROID_VALIDATION_SCHEMA_VERSION, } from "../validation/result.js";
21
+ function readToolPackageMetadata(toolRoot) {
22
+ try {
23
+ const pkgRaw = fs.readFileSync(path.join(toolRoot, "package.json"), "utf8");
24
+ const pkg = JSON.parse(pkgRaw);
25
+ return { toolPackageName: pkg.name ?? "my-dev-kit-lab", toolPackageVersion: pkg.version ?? "unknown" };
26
+ }
27
+ catch {
28
+ return { toolPackageName: "my-dev-kit-lab", toolPackageVersion: "unknown" };
29
+ }
30
+ }
31
+ function hasGitDirectory(targetRoot) {
32
+ try {
33
+ return fs.existsSync(path.join(targetRoot, ".git"));
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ export async function validateAndroidTarget(options) {
40
+ const startedAt = new Date().toISOString();
41
+ const startedMs = Date.now();
42
+ // 1. Resolve the local target.
43
+ const local = resolveLocalProjectTarget(options.targetPath, options.toolRoot);
44
+ const resolvedTargetRoot = path.resolve(local.targetRoot);
45
+ const hasGit = local.hasGit || hasGitDirectory(resolvedTargetRoot);
46
+ // 2. Capture initial target snapshot (read-only).
47
+ const snapshotBefore = captureTargetSnapshot(resolvedTargetRoot, hasGit);
48
+ // 3. Detect and classify the Android project.
49
+ const detection = detectAndroidProject(resolvedTargetRoot);
50
+ // 5. Build Android target metadata / profile.
51
+ const androidProfile = createAndroidProfile({
52
+ detectionConfidence: detection.confidence,
53
+ evidence: detection.evidence,
54
+ unsupportedNotes: detection.warnings,
55
+ projectType: detection.projectKind,
56
+ projectSubtype: detection.uiToolkit,
57
+ supportedCapabilities: ["static-detection", "manifest-analysis", "gradle-metadata"],
58
+ });
59
+ const targetMetadata = createAndroidTargetMetadata({ local, androidProfile, detection });
60
+ // 6-7. Parse every discovered manifest independently (module/source-set
61
+ // association preserved by parseAllAndroidManifests itself).
62
+ const manifestEntries = parseAllAndroidManifests(resolvedTargetRoot, detection);
63
+ const manifests = manifestEntries.map((e) => e.manifest);
64
+ // 8-11. Four static audits — failure isolation: each returns its own
65
+ // AndroidCheckResult/findings independently of the others.
66
+ const permissionsCheck = auditAndroidPermissions(detection, manifestEntries);
67
+ const componentsCheck = auditAndroidExportedComponents(detection, manifestEntries);
68
+ const intentFiltersCheck = auditAndroidIntentFilters(detection, manifestEntries);
69
+ const deepLinksCheck = auditAndroidDeepLinks(detection, manifestEntries);
70
+ // 12-14. Static Gradle metadata + release metadata summary.
71
+ const gradle = readAndroidGradleMetadata(resolvedTargetRoot, detection);
72
+ const gradleMetadataCheck = buildAndroidGradleMetadataCheckResult(detection, gradle);
73
+ const releaseMetadata = buildAndroidReleaseMetadataSummary(detection, gradle);
74
+ // Detection + manifest-parsing checks (required families with no prior
75
+ // batch owner).
76
+ const detectionCheck = buildAndroidDetectionCheckResult(detection);
77
+ const manifestParsingCheck = buildAndroidManifestParsingCheckResult(detection, manifestEntries);
78
+ // 15-16. Optional Gradle operations — never run unless explicitly requested.
79
+ const requestedOperationIds = options.requestedGradleOperationIds ?? [];
80
+ const gradleOperationResults = requestedOperationIds.length > 0
81
+ ? await runOptionalGradleValidation({
82
+ enabled: true,
83
+ targetRoot: resolvedTargetRoot,
84
+ detection,
85
+ operationIds: requestedOperationIds,
86
+ executor: options.gradleExecutor,
87
+ allowWithoutTaskDiscovery: options.allowGradleWithoutTaskDiscovery,
88
+ platform: options.platform,
89
+ })
90
+ : [];
91
+ // 17. Target mutation evidence — captured regardless of whether Gradle ran,
92
+ // so the static-only path also proves it made no changes.
93
+ const snapshotAfter = captureTargetSnapshot(resolvedTargetRoot, hasGit);
94
+ const targetMutation = buildTargetMutationReport(snapshotBefore, snapshotAfter);
95
+ const immutabilityCheck = buildAndroidTargetImmutabilityCheckResult(targetMutation);
96
+ // 18. Assemble checks/findings in deterministic order (agents.txt section 10.3).
97
+ const checks = [
98
+ detectionCheck,
99
+ manifestParsingCheck,
100
+ permissionsCheck,
101
+ componentsCheck,
102
+ intentFiltersCheck,
103
+ deepLinksCheck,
104
+ gradleMetadataCheck,
105
+ ...gradleOperationResults,
106
+ immutabilityCheck,
107
+ ];
108
+ const findingsById = new Map();
109
+ for (const check of checks) {
110
+ for (const finding of check.findings) {
111
+ // Exact-duplicate dedupe by id only (agents.txt section 10.4) —
112
+ // distinct manifests/components/permissions/deep-links always produce
113
+ // distinct ids (see androidFinding.ts's content-derived id builder),
114
+ // so this never collapses genuinely different evidence.
115
+ if (!findingsById.has(finding.id)) {
116
+ findingsById.set(finding.id, finding);
117
+ }
118
+ }
119
+ }
120
+ const findings = [...findingsById.values()].sort((a, b) => a.id.localeCompare(b.id));
121
+ const skippedChecks = checks.flatMap((c) => (c.skipInfo ? [c.skipInfo] : []));
122
+ const warnings = checks.flatMap((c) => c.warnings);
123
+ const errors = checks.flatMap((c) => c.errors);
124
+ const environmentLimitations = [...new Set(checks.flatMap((c) => c.environmentRequirements))];
125
+ // 19. Play-readiness checklist placeholders.
126
+ const isLibraryOnly = detection.projectKind === "library";
127
+ const isNonAndroid = detection.projectKind === "non-android";
128
+ const playReadiness = buildAndroidPlayReadinessChecklist({ isLibraryOnly, isNonAndroid, releaseMetadata, manifests });
129
+ // 20. Verdict.
130
+ const multipleApplicationModules = detection.applicationModules.length > 1;
131
+ const { verdict, recommendedNextStep, reasons } = calculateAndroidVerdict({
132
+ checks,
133
+ findings,
134
+ detection,
135
+ playReadiness,
136
+ targetMutation,
137
+ multipleApplicationModules,
138
+ });
139
+ const finishedAt = new Date().toISOString();
140
+ const durationMs = Date.now() - startedMs;
141
+ const { toolPackageName, toolPackageVersion } = readToolPackageMetadata(options.toolRoot);
142
+ return {
143
+ schemaVersion: ANDROID_VALIDATION_SCHEMA_VERSION,
144
+ artifactType: ANDROID_VALIDATION_ARTIFACT_TYPE,
145
+ tool: { toolRoot: path.resolve(options.toolRoot), toolPackageName, toolPackageVersion },
146
+ target: targetMetadata,
147
+ profile: androidProfile,
148
+ detection,
149
+ manifests,
150
+ gradle,
151
+ checks,
152
+ findings,
153
+ skippedChecks,
154
+ warnings,
155
+ errors,
156
+ releaseMetadataSummary: {
157
+ available: Boolean(releaseMetadata.applicationModulePath),
158
+ note: releaseMetadata.applicationModuleSelectionNote,
159
+ },
160
+ releaseMetadata,
161
+ playReadinessSummary: { available: playReadiness.applicable, note: playReadiness.note },
162
+ playReadiness,
163
+ verdict,
164
+ verdictReasons: reasons,
165
+ recommendedNextStep,
166
+ targetMutationEvidence: targetMutation,
167
+ reportReferences: [],
168
+ startedAt,
169
+ finishedAt,
170
+ durationMs,
171
+ };
172
+ }
@@ -0,0 +1,51 @@
1
+ // ---------------------------------------------------------------------------
2
+ // v0.4.0 Batch 1 — Android check-result and skip-representation contracts.
3
+ //
4
+ // Reuses SecurityFinding and CommandExecutionResult from the existing
5
+ // security-validation types rather than duplicating a finding/command-result
6
+ // schema. Android needs a wider status vocabulary than SecurityCheckStatus
7
+ // ("passed"/"failed"/"skipped"/"warning") because static Android checks can
8
+ // be genuinely inconclusive or unsupported on a given project, and
9
+ // command-backed checks can error out before producing a verdict-relevant
10
+ // result — none of those outcomes may ever be normalized as "passed".
11
+ // ---------------------------------------------------------------------------
12
+ export const ANDROID_CHECK_STATUSES = [
13
+ "passed",
14
+ "failed",
15
+ "skipped",
16
+ "inconclusive",
17
+ "unsupported",
18
+ "not-run",
19
+ "error",
20
+ ];
21
+ // Statuses that must never be treated as a passing result by a verdict
22
+ // calculator. Defined once here so later batches share one source of truth.
23
+ export const ANDROID_NON_PASSING_STATUSES = [
24
+ "failed",
25
+ "skipped",
26
+ "inconclusive",
27
+ "unsupported",
28
+ "not-run",
29
+ "error",
30
+ ];
31
+ // v0.4.0 Batch 3 — added "android-intent-filters" (additive) so the intent-
32
+ // filter audit has its own category distinct from "android-components",
33
+ // which covers exported-component evidence generally.
34
+ // v0.4.0 Batch 5 — added "android-target-immutability" (additive) for the
35
+ // required target mutation/immutability check (agents.txt Batch 5 section
36
+ // 10.1).
37
+ export const ANDROID_CHECK_CATEGORIES = [
38
+ "android-detection",
39
+ "android-manifest",
40
+ "android-gradle",
41
+ "android-permissions",
42
+ "android-components",
43
+ "android-intent-filters",
44
+ "android-deep-links",
45
+ "android-release-metadata",
46
+ "android-play-readiness",
47
+ "android-target-immutability",
48
+ ];
49
+ export function isPassingAndroidStatus(status) {
50
+ return status === "passed";
51
+ }
@@ -0,0 +1,25 @@
1
+ // ---------------------------------------------------------------------------
2
+ // v0.4.0 Batch 1 — Android validation result contract.
3
+ // v0.4.0 Batch 5 — narrow additive inherited-contract corrections needed to
4
+ // carry a real verdict and rich release/Play-readiness data (see agents.txt
5
+ // Batch 5 section 6.6):
6
+ // - `verdict` widened from the single placeholder literal to
7
+ // `AndroidVerdictValue` (ReleaseVerdict | "not-calculated"), reusing the
8
+ // existing security-validation ReleaseVerdict vocabulary instead of
9
+ // inventing a second one (Batch 1's own comment anticipated this: "this
10
+ // type grows without being a breaking change — the placeholder value
11
+ // remains valid"). Every existing "not-calculated" literal stays valid.
12
+ // - `releaseMetadataSummary`/`playReadinessSummary` (the Batch 1 {available,
13
+ // note} placeholders) are UNCHANGED in shape — existing object literals
14
+ // using them remain valid — with new sibling fields `releaseMetadata`/
15
+ // `playReadiness` added for the actual structured data, avoiding a
16
+ // breaking retype of the original fields.
17
+ // - `verdictReasons` and `targetMutationEvidence` are new optional fields.
18
+ // ---------------------------------------------------------------------------
19
+ export const ANDROID_VALIDATION_SCHEMA_VERSION = "android-validation-v1";
20
+ export const ANDROID_VALIDATION_ARTIFACT_TYPE = "android-validation-result";
21
+ // A single placeholder value: this batch and detection/manifest/gradle
22
+ // batches must never assert release readiness. A concrete verdict policy is
23
+ // introduced by a later batch, at which point this type grows without being
24
+ // a breaking change (the placeholder value remains valid).
25
+ export const ANDROID_VERDICT_NOT_CALCULATED = "not-calculated";
@@ -0,0 +1,42 @@
1
+ export { MOBILE_PLATFORMS, MOBILE_CONFIDENCE_LEVELS, MOBILE_VALIDATION_CAPABILITIES } from "./types.js";
2
+ export { ANDROID_PLATFORM, ANDROID_PROFILE_ID, createAndroidProfile, isAndroidProfile } from "./android/profile.js";
3
+ export { ANDROID_PROJECT_KINDS, ANDROID_UI_TOOLKITS } from "./android/detection.js";
4
+ export { detectAndroidProject } from "./android/detect/detectAndroidProject.js";
5
+ export { detectAndroidTarget } from "./android/detect/androidTargetDetection.js";
6
+ export { parseDeclaredModules } from "./android/detect/settingsModules.js";
7
+ export { extractBuildFileEvidence } from "./android/detect/buildFileEvidence.js";
8
+ export { extractVersionCatalogEvidence } from "./android/detect/versionCatalogEvidence.js";
9
+ export { classifyModule, classifyModuleUiToolkit, combineProjectKind, combineProjectUiToolkit, combineProjectConfidence, } from "./android/detect/classify.js";
10
+ export { createAndroidTargetMetadata } from "./android/targetMetadata.js";
11
+ export { parseAndroidManifestSource, parseAndroidManifestFile, parseAllAndroidManifests, } from "./android/manifest/parseAndroidManifest.js";
12
+ export { parseXmlDocument, findChildren, getAttribute, ANDROID_NAMESPACE_URI } from "./android/manifest/xml/parseXml.js";
13
+ export { parseWrapperProperties } from "./android/gradle/wrapperMetadata.js";
14
+ export { parseVersionCatalog, resolveCatalogVersion, accessorSuffixToCatalogAlias } from "./android/gradle/versionCatalogMetadata.js";
15
+ export { extractModuleGradleMetadata } from "./android/gradle/moduleMetadataExtractor.js";
16
+ export { resolvePluginVersion } from "./android/gradle/pluginVersionExtractor.js";
17
+ export { parseSettingsMetadata } from "./android/gradle/settingsMetadata.js";
18
+ export { readAndroidGradleMetadata } from "./android/gradle/readAndroidGradleMetadata.js";
19
+ export { buildAndroidGradleMetadataCheckResult } from "./android/gradle/gradleMetadataCheckResult.js";
20
+ export { buildAndroidReleaseMetadataSummary } from "./android/gradle/releaseMetadataSummary.js";
21
+ export { ALLOWLISTED_OPERATION_IDS, isAllowlistedOperationId, GRADLE_OPERATIONS } from "./android/gradle/validate/operations.js";
22
+ export { buildGradleCommandPlan } from "./android/gradle/validate/planner.js";
23
+ export { createRealGradleCommandExecutor } from "./android/gradle/validate/executor.js";
24
+ export { parseGradleTaskNames, isGradleTaskAvailable } from "./android/gradle/validate/taskListParser.js";
25
+ export { buildTargetMutationReport, isExpectedAndroidGeneratedPath, captureTargetSnapshot } from "./android/gradle/validate/targetMutation.js";
26
+ export { buildGradleOperationCheckResult } from "./android/gradle/validate/operationCheckResult.js";
27
+ export { runOptionalGradleValidation } from "./android/gradle/validate/runOptionalGradleValidation.js";
28
+ export { ANDROID_CHECK_STATUSES, ANDROID_NON_PASSING_STATUSES, ANDROID_CHECK_CATEGORIES, isPassingAndroidStatus, } from "./android/validation/checkResult.js";
29
+ export { ANDROID_VALIDATION_SCHEMA_VERSION, ANDROID_VALIDATION_ARTIFACT_TYPE, ANDROID_VERDICT_NOT_CALCULATED, } from "./android/validation/result.js";
30
+ export { toAndroidReportModel, serializeAndroidReportModel } from "./android/report/model.js";
31
+ export { renderAndroidTextReport } from "./android/report/renderAndroidReport.js";
32
+ export { writeAndroidReportFiles } from "./android/report/writeAndroidReportFiles.js";
33
+ export { makeAndroidFinding } from "./android/audit/androidFinding.js";
34
+ export { buildAndroidManifestCheckResult } from "./android/audit/checkResultBuilder.js";
35
+ export { auditAndroidPermissions } from "./android/audit/permissionAudit.js";
36
+ export { auditAndroidExportedComponents } from "./android/audit/exportedComponentAudit.js";
37
+ export { auditAndroidIntentFilters } from "./android/audit/intentFilterAudit.js";
38
+ export { auditAndroidDeepLinks } from "./android/audit/deepLinkAudit.js";
39
+ export { ANDROID_PLAY_READINESS_ITEM_STATUSES, buildAndroidPlayReadinessChecklist } from "./android/validate/playReadinessChecklist.js";
40
+ export { calculateAndroidVerdict, androidVerdictToHumanLabel } from "./android/validate/androidVerdict.js";
41
+ export { buildAndroidDetectionCheckResult, buildAndroidManifestParsingCheckResult, buildAndroidTargetImmutabilityCheckResult, } from "./android/validate/detectionAndImmutabilityCheckResults.js";
42
+ export { validateAndroidTarget } from "./android/validate/validateAndroidTarget.js";
@@ -0,0 +1,27 @@
1
+ // Shared mobile-platform contracts for my-dev-kit-lab's mobile/Android
2
+ // validation substrate (v0.4.0 Batch 1).
3
+ //
4
+ // Android is the only implemented platform. Additional platform ids may be
5
+ // added to MOBILE_PLATFORMS in a future version once real detection and
6
+ // validation exist for them — this file must never claim support for a
7
+ // platform that has no implementation behind it.
8
+ export const MOBILE_PLATFORMS = ["android"];
9
+ // Confidence in a detection/classification/evidence claim. "unknown" is used
10
+ // when confidence itself could not be determined (distinct from "low", which
11
+ // asserts a deliberately weak but present signal).
12
+ export const MOBILE_CONFIDENCE_LEVELS = ["unknown", "low", "medium", "high"];
13
+ // Validation capabilities a profile can declare support for. This batch only
14
+ // defines the vocabulary; no capability is exercised until its owning batch
15
+ // implements it. Capabilities are declarative metadata, not proof of
16
+ // execution — a profile listing a capability does not mean a check ran.
17
+ export const MOBILE_VALIDATION_CAPABILITIES = [
18
+ "static-detection",
19
+ "manifest-analysis",
20
+ "gradle-metadata",
21
+ "gradle-task-execution",
22
+ "permission-audit",
23
+ "component-audit",
24
+ "deep-link-audit",
25
+ "release-metadata-summary",
26
+ "play-readiness-checklist",
27
+ ];
@@ -2,10 +2,12 @@ import { SECURITY_PROFILE_IDS } from "../validate/cliOptions.js";
2
2
  import { NODE_CLI_PACKAGE_PROFILE } from "./profiles/nodeCliPackageProfile.js";
3
3
  import { LOCAL_TOOL_PROFILE } from "./profiles/localToolProfile.js";
4
4
  import { NPM_PACKAGE_PROFILE } from "./profiles/npmPackageProfile.js";
5
+ import { ANDROID_PROFILE } from "./profiles/androidProfile.js";
5
6
  const PROFILE_REGISTRY = {
6
7
  "node-cli-package": NODE_CLI_PACKAGE_PROFILE,
7
8
  "local-tool": LOCAL_TOOL_PROFILE,
8
9
  "npm-package": NPM_PACKAGE_PROFILE,
10
+ android: ANDROID_PROFILE,
9
11
  };
10
12
  // Resolves a profile id to its definition. Throws a clean, descriptive Error
11
13
  // for unknown ids (mirrors Batch 1's --profile validation error style).