@dailephd/my-dev-kit-lab 0.3.4 → 0.4.1

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 (252) hide show
  1. package/README.md +12 -287
  2. package/dist/scripts/security/validate.js +210 -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/mobile/android/advancedSecurity/backupConfiguration/analyzeBackupConfiguration.js +300 -0
  6. package/dist/src/mobile/android/advancedSecurity/backupConfiguration/checkResult.js +41 -0
  7. package/dist/src/mobile/android/advancedSecurity/backupConfiguration/index.js +8 -0
  8. package/dist/src/mobile/android/advancedSecurity/backupConfiguration/manifestEvidence.js +102 -0
  9. package/dist/src/mobile/android/advancedSecurity/backupConfiguration/parseBackupRules.js +110 -0
  10. package/dist/src/mobile/android/advancedSecurity/backupConfiguration/types.js +27 -0
  11. package/dist/src/mobile/android/advancedSecurity/candidateEvidence.js +50 -0
  12. package/dist/src/mobile/android/advancedSecurity/clipboard/analyzeClipboardSecurity.js +166 -0
  13. package/dist/src/mobile/android/advancedSecurity/clipboard/checkResult.js +74 -0
  14. package/dist/src/mobile/android/advancedSecurity/clipboard/collectClipboardEvidence.js +82 -0
  15. package/dist/src/mobile/android/advancedSecurity/clipboard/index.js +1 -0
  16. package/dist/src/mobile/android/advancedSecurity/clipboard/types.js +1 -0
  17. package/dist/src/mobile/android/advancedSecurity/externalTools/androidLint/checkResult.js +255 -0
  18. package/dist/src/mobile/android/advancedSecurity/externalTools/androidLint/index.js +1 -0
  19. package/dist/src/mobile/android/advancedSecurity/externalTools/androidLint/parseLintSarif.js +37 -0
  20. package/dist/src/mobile/android/advancedSecurity/externalTools/androidLint/parseLintXml.js +36 -0
  21. package/dist/src/mobile/android/advancedSecurity/externalTools/artifacts.js +95 -0
  22. package/dist/src/mobile/android/advancedSecurity/externalTools/boundedOutput.js +33 -0
  23. package/dist/src/mobile/android/advancedSecurity/externalTools/dependencyCheck/checkResult.js +173 -0
  24. package/dist/src/mobile/android/advancedSecurity/externalTools/dependencyCheck/command.js +33 -0
  25. package/dist/src/mobile/android/advancedSecurity/externalTools/dependencyCheck/index.js +1 -0
  26. package/dist/src/mobile/android/advancedSecurity/externalTools/dependencyCheck/parseDependencyCheckJson.js +133 -0
  27. package/dist/src/mobile/android/advancedSecurity/externalTools/discoverExecutable.js +13 -0
  28. package/dist/src/mobile/android/advancedSecurity/externalTools/index.js +8 -0
  29. package/dist/src/mobile/android/advancedSecurity/externalTools/minimalEnvironment.js +26 -0
  30. package/dist/src/mobile/android/advancedSecurity/externalTools/mutation.js +77 -0
  31. package/dist/src/mobile/android/advancedSecurity/externalTools/osv/checkResult.js +161 -0
  32. package/dist/src/mobile/android/advancedSecurity/externalTools/osv/command.js +25 -0
  33. package/dist/src/mobile/android/advancedSecurity/externalTools/osv/index.js +1 -0
  34. package/dist/src/mobile/android/advancedSecurity/externalTools/osv/parseOsvJson.js +136 -0
  35. package/dist/src/mobile/android/advancedSecurity/externalTools/runBoundedExternalTool.js +51 -0
  36. package/dist/src/mobile/android/advancedSecurity/externalTools/runRequestedAndroidExternalTools.js +54 -0
  37. package/dist/src/mobile/android/advancedSecurity/externalTools/semgrep/checkResult.js +170 -0
  38. package/dist/src/mobile/android/advancedSecurity/externalTools/semgrep/command.js +28 -0
  39. package/dist/src/mobile/android/advancedSecurity/externalTools/semgrep/index.js +2 -0
  40. package/dist/src/mobile/android/advancedSecurity/externalTools/semgrep/parseSemgrepJson.js +141 -0
  41. package/dist/src/mobile/android/advancedSecurity/externalTools/semgrep/rules.js +105 -0
  42. package/dist/src/mobile/android/advancedSecurity/externalTools/types.js +107 -0
  43. package/dist/src/mobile/android/advancedSecurity/fileProvider/checkResult.js +188 -0
  44. package/dist/src/mobile/android/advancedSecurity/fileProvider/index.js +1 -0
  45. package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/analyzeFirebaseGoogleServices.js +197 -0
  46. package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/checkResult.js +77 -0
  47. package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/collectFirebaseServiceEvidence.js +193 -0
  48. package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/discoverFirebaseArtifacts.js +128 -0
  49. package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/index.js +1 -0
  50. package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/parseDatabaseRules.js +55 -0
  51. package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/parseFirebaseConfig.js +75 -0
  52. package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/parseFirestoreStorageRules.js +88 -0
  53. package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/parseGoogleServicesConfig.js +55 -0
  54. package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/types.js +6 -0
  55. package/dist/src/mobile/android/advancedSecurity/networkSecurity/analyzeNetworkSecurity.js +394 -0
  56. package/dist/src/mobile/android/advancedSecurity/networkSecurity/checkResult.js +47 -0
  57. package/dist/src/mobile/android/advancedSecurity/networkSecurity/deriveEffectiveNetworkPolicy.js +52 -0
  58. package/dist/src/mobile/android/advancedSecurity/networkSecurity/index.js +14 -0
  59. package/dist/src/mobile/android/advancedSecurity/networkSecurity/manifestEvidence.js +109 -0
  60. package/dist/src/mobile/android/advancedSecurity/networkSecurity/parseNetworkSecurityConfig.js +186 -0
  61. package/dist/src/mobile/android/advancedSecurity/networkSecurity/types.js +1 -0
  62. package/dist/src/mobile/android/advancedSecurity/ordering.js +44 -0
  63. package/dist/src/mobile/android/advancedSecurity/redaction.js +54 -0
  64. package/dist/src/mobile/android/advancedSecurity/releaseConfiguration/analyzeReleaseConfiguration.js +221 -0
  65. package/dist/src/mobile/android/advancedSecurity/releaseConfiguration/checkResult.js +39 -0
  66. package/dist/src/mobile/android/advancedSecurity/releaseConfiguration/index.js +7 -0
  67. package/dist/src/mobile/android/advancedSecurity/releaseConfiguration/manifestEvidence.js +19 -0
  68. package/dist/src/mobile/android/advancedSecurity/resourceReference.js +69 -0
  69. package/dist/src/mobile/android/advancedSecurity/resourceResolution.js +118 -0
  70. package/dist/src/mobile/android/advancedSecurity/ruleIds.js +141 -0
  71. package/dist/src/mobile/android/advancedSecurity/secretCandidates/analyzeSecretCandidates.js +116 -0
  72. package/dist/src/mobile/android/advancedSecurity/secretCandidates/checkResult.js +80 -0
  73. package/dist/src/mobile/android/advancedSecurity/secretCandidates/discoverSecretSourceFiles.js +152 -0
  74. package/dist/src/mobile/android/advancedSecurity/secretCandidates/index.js +9 -0
  75. package/dist/src/mobile/android/advancedSecurity/secretCandidates/matchSecretCandidates.js +136 -0
  76. package/dist/src/mobile/android/advancedSecurity/secretCandidates/suppressValue.js +61 -0
  77. package/dist/src/mobile/android/advancedSecurity/secretCandidates/types.js +11 -0
  78. package/dist/src/mobile/android/advancedSecurity/sensitiveCategories.js +34 -0
  79. package/dist/src/mobile/android/advancedSecurity/sensitiveData/classifyDirectExpression.js +99 -0
  80. package/dist/src/mobile/android/advancedSecurity/sensitiveData/classifySensitiveIdentifier.js +111 -0
  81. package/dist/src/mobile/android/advancedSecurity/sensitiveData/index.js +3 -0
  82. package/dist/src/mobile/android/advancedSecurity/sensitiveData/localSourceContext.js +90 -0
  83. package/dist/src/mobile/android/advancedSecurity/sensitiveData/splitArguments.js +59 -0
  84. package/dist/src/mobile/android/advancedSecurity/sensitiveLogging/analyzeSensitiveLogging.js +117 -0
  85. package/dist/src/mobile/android/advancedSecurity/sensitiveLogging/checkResult.js +74 -0
  86. package/dist/src/mobile/android/advancedSecurity/sensitiveLogging/collectLoggingEvidence.js +87 -0
  87. package/dist/src/mobile/android/advancedSecurity/sensitiveLogging/index.js +1 -0
  88. package/dist/src/mobile/android/advancedSecurity/sensitiveLogging/types.js +1 -0
  89. package/dist/src/mobile/android/advancedSecurity/sensitiveStorage/analyzeSensitiveStorage.js +182 -0
  90. package/dist/src/mobile/android/advancedSecurity/sensitiveStorage/checkResult.js +74 -0
  91. package/dist/src/mobile/android/advancedSecurity/sensitiveStorage/collectStorageEvidence.js +109 -0
  92. package/dist/src/mobile/android/advancedSecurity/sensitiveStorage/index.js +1 -0
  93. package/dist/src/mobile/android/advancedSecurity/sensitiveStorage/types.js +4 -0
  94. package/dist/src/mobile/android/advancedSecurity/signingConfiguration/analyzeSigningConfiguration.js +270 -0
  95. package/dist/src/mobile/android/advancedSecurity/signingConfiguration/checkResult.js +119 -0
  96. package/dist/src/mobile/android/advancedSecurity/signingConfiguration/discoverKeystoreCandidates.js +55 -0
  97. package/dist/src/mobile/android/advancedSecurity/signingConfiguration/extractSigningConfigurations.js +81 -0
  98. package/dist/src/mobile/android/advancedSecurity/signingConfiguration/index.js +8 -0
  99. package/dist/src/mobile/android/advancedSecurity/signingConfiguration/types.js +20 -0
  100. package/dist/src/mobile/android/advancedSecurity/sourceLocation.js +15 -0
  101. package/dist/src/mobile/android/advancedSecurity/webview/checkResult.js +210 -0
  102. package/dist/src/mobile/android/advancedSecurity/webview/index.js +1 -0
  103. package/dist/src/mobile/android/audit/androidFinding.js +68 -0
  104. package/dist/src/mobile/android/audit/checkResultBuilder.js +97 -0
  105. package/dist/src/mobile/android/audit/deepLinkAudit.js +183 -0
  106. package/dist/src/mobile/android/audit/exportedComponentAudit.js +183 -0
  107. package/dist/src/mobile/android/audit/intentFilterAudit.js +128 -0
  108. package/dist/src/mobile/android/audit/permissionAudit.js +142 -0
  109. package/dist/src/mobile/android/detect/androidTargetDetection.js +18 -0
  110. package/dist/src/mobile/android/detect/buildFileEvidence.js +36 -0
  111. package/dist/src/mobile/android/detect/classify.js +150 -0
  112. package/dist/src/mobile/android/detect/detectAndroidProject.js +252 -0
  113. package/dist/src/mobile/android/detect/settingsModules.js +60 -0
  114. package/dist/src/mobile/android/detect/sourceEvidence.js +92 -0
  115. package/dist/src/mobile/android/detect/traversal.js +79 -0
  116. package/dist/src/mobile/android/detect/versionCatalogEvidence.js +37 -0
  117. package/dist/src/mobile/android/detection.js +28 -0
  118. package/dist/src/mobile/android/gradle/gradleMetadataCheckResult.js +120 -0
  119. package/dist/src/mobile/android/gradle/moduleMetadataExtractor.js +243 -0
  120. package/dist/src/mobile/android/gradle/pluginVersionExtractor.js +40 -0
  121. package/dist/src/mobile/android/gradle/readAndroidGradleMetadata.js +210 -0
  122. package/dist/src/mobile/android/gradle/releaseMetadataSummary.js +64 -0
  123. package/dist/src/mobile/android/gradle/settingsMetadata.js +20 -0
  124. package/dist/src/mobile/android/gradle/types.js +1 -0
  125. package/dist/src/mobile/android/gradle/validate/executor.js +9 -0
  126. package/dist/src/mobile/android/gradle/validate/operationCheckResult.js +99 -0
  127. package/dist/src/mobile/android/gradle/validate/operations.js +68 -0
  128. package/dist/src/mobile/android/gradle/validate/planner.js +49 -0
  129. package/dist/src/mobile/android/gradle/validate/runOptionalGradleValidation.js +113 -0
  130. package/dist/src/mobile/android/gradle/validate/targetMutation.js +45 -0
  131. package/dist/src/mobile/android/gradle/validate/taskListParser.js +24 -0
  132. package/dist/src/mobile/android/gradle/versionCatalogMetadata.js +94 -0
  133. package/dist/src/mobile/android/gradle/wrapperMetadata.js +32 -0
  134. package/dist/src/mobile/android/manifest/parseAndroidManifest.js +364 -0
  135. package/dist/src/mobile/android/manifest/types.js +29 -0
  136. package/dist/src/mobile/android/manifest/xml/parseXml.js +329 -0
  137. package/dist/src/mobile/android/profile.js +29 -0
  138. package/dist/src/mobile/android/report/model.js +135 -0
  139. package/dist/src/mobile/android/report/renderAndroidReport.js +286 -0
  140. package/dist/src/mobile/android/report/writeAndroidReportFiles.js +23 -0
  141. package/dist/src/mobile/android/targetMetadata.js +27 -0
  142. package/dist/src/mobile/android/validate/androidVerdict.js +248 -0
  143. package/dist/src/mobile/android/validate/detectionAndImmutabilityCheckResults.js +177 -0
  144. package/dist/src/mobile/android/validate/playReadinessChecklist.js +145 -0
  145. package/dist/src/mobile/android/validate/validateAndroidTarget.js +315 -0
  146. package/dist/src/mobile/android/validation/checkResult.js +58 -0
  147. package/dist/src/mobile/android/validation/result.js +25 -0
  148. package/dist/src/mobile/index.js +42 -0
  149. package/dist/src/mobile/types.js +27 -0
  150. package/dist/src/securityValidation/attackScenarios/attackProfile.js +2 -0
  151. package/dist/src/securityValidation/attackScenarios/profiles/androidProfile.js +20 -0
  152. package/dist/src/securityValidation/commandRunner.js +10 -1
  153. package/dist/src/securityValidation/validate/cliOptions.js +92 -2
  154. package/package.json +2 -1
  155. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/debug/res/xml/ambiguous_backup_rules.xml +4 -0
  156. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/main/res/xml/ambiguous_backup_rules.xml +4 -0
  157. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/main/res/xml/backup_rules.xml +5 -0
  158. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/main/res/xml/broad_backup_rules.xml +4 -0
  159. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/main/res/xml/data_extraction_rules.xml +10 -0
  160. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/main/res/xml/unsupported_domain_rules.xml +4 -0
  161. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-b/src/main/res/xml/malformed_backup_rules.xml +4 -0
  162. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-b/src/main/res/xml/unsupported_root.xml +4 -0
  163. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/debug/res/xml/ambiguous_config.xml +4 -0
  164. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/ambiguous_config.xml +4 -0
  165. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/base_cleartext_false.xml +8 -0
  166. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/broad_and_duplicate_domains.xml +8 -0
  167. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/empty_domain.xml +6 -0
  168. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/malformed_pin.xml +11 -0
  169. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/network_security_config.xml +26 -0
  170. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-b/src/main/res/xml/malformed_network_security_config.xml +6 -0
  171. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-b/src/main/res/xml/unsupported_root.xml +4 -0
  172. package/tests/fixtures/android/advanced-security-fixtures/release-configuration/groovy-release-debuggable-dynamic.gradle +7 -0
  173. package/tests/fixtures/android/advanced-security-fixtures/release-configuration/groovy-release-debuggable-false.gradle +9 -0
  174. package/tests/fixtures/android/advanced-security-fixtures/release-configuration/groovy-release-debuggable-true.gradle +13 -0
  175. package/tests/fixtures/android/advanced-security-fixtures/release-configuration/kotlin-release-debuggable-false.gradle.kts +7 -0
  176. package/tests/fixtures/android/advanced-security-fixtures/release-configuration/kotlin-release-debuggable-true.gradle.kts +10 -0
  177. package/tests/fixtures/android/advanced-security-fixtures/release-configuration/missing-release-build-type.gradle.kts +7 -0
  178. package/tests/fixtures/android/advanced-security-fixtures/resource-resolution/module-a/src/debug/res/xml/network_security_config.xml +8 -0
  179. package/tests/fixtures/android/advanced-security-fixtures/resource-resolution/module-a/src/main/res/xml/backup_rules.xml +4 -0
  180. package/tests/fixtures/android/advanced-security-fixtures/resource-resolution/module-a/src/main/res/xml/network_security_config.xml +8 -0
  181. package/tests/fixtures/android/advanced-security-fixtures/resource-resolution/module-b/src/main/res/xml/malformed_network_security_config.xml +7 -0
  182. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/SampleActivity.java +8 -0
  183. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/SampleActivity.kt +9 -0
  184. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/build.gradle +11 -0
  185. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/build.gradle.kts +11 -0
  186. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/google-services.json +16 -0
  187. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/local.properties +3 -0
  188. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates-bounds/module/binary.properties +0 -0
  189. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates-bounds/module/oversized.json +1 -0
  190. package/tests/fixtures/android/advanced-security-fixtures/signing-configuration/app/fake-release-keystore.jks +1 -0
  191. package/tests/fixtures/android/advanced-security-fixtures/signing-configuration/build/generated-fake.keystore +1 -0
  192. package/tests/fixtures/android/compose-app/app/build.gradle.kts +21 -0
  193. package/tests/fixtures/android/compose-app/app/src/main/AndroidManifest.xml +22 -0
  194. package/tests/fixtures/android/compose-app/app/src/main/java/com/example/composeapp/MainActivity.kt +11 -0
  195. package/tests/fixtures/android/compose-app/build.gradle.kts +4 -0
  196. package/tests/fixtures/android/compose-app/gradle/wrapper/gradle-wrapper.properties +4 -0
  197. package/tests/fixtures/android/compose-app/settings.gradle.kts +2 -0
  198. package/tests/fixtures/android/gradle-metadata-fixtures/catalog-only-plugin/build.gradle.kts +3 -0
  199. package/tests/fixtures/android/gradle-metadata-fixtures/catalog-only-plugin/gradle/libs.versions.toml +5 -0
  200. package/tests/fixtures/android/gradle-metadata-fixtures/comment-safety.gradle.kts +21 -0
  201. package/tests/fixtures/android/gradle-metadata-fixtures/dynamic-values.gradle.kts +17 -0
  202. package/tests/fixtures/android/gradle-metadata-fixtures/groovy-literal.gradle +23 -0
  203. package/tests/fixtures/android/gradle-metadata-fixtures/wrapper-malformed.properties +4 -0
  204. package/tests/fixtures/android/gradle-metadata-fixtures/wrapper-missing-url.properties +3 -0
  205. package/tests/fixtures/android/gradle-metadata-fixtures/wrapper-with-checksum.properties +5 -0
  206. package/tests/fixtures/android/java-app/app/build.gradle +16 -0
  207. package/tests/fixtures/android/java-app/app/src/main/AndroidManifest.xml +19 -0
  208. package/tests/fixtures/android/java-app/app/src/main/java/com/example/javaapp/MainActivity.java +4 -0
  209. package/tests/fixtures/android/java-app/build.gradle +3 -0
  210. package/tests/fixtures/android/java-app/gradle/wrapper/gradle-wrapper.properties +4 -0
  211. package/tests/fixtures/android/java-app/settings.gradle +2 -0
  212. package/tests/fixtures/android/library/build.gradle.kts +3 -0
  213. package/tests/fixtures/android/library/gradle/wrapper/gradle-wrapper.properties +4 -0
  214. package/tests/fixtures/android/library/mylibrary/build.gradle.kts +13 -0
  215. package/tests/fixtures/android/library/mylibrary/src/main/AndroidManifest.xml +2 -0
  216. package/tests/fixtures/android/library/settings.gradle.kts +2 -0
  217. package/tests/fixtures/android/manifest-audit-fixtures/incomplete-intent-filter.xml +14 -0
  218. package/tests/fixtures/android/manifest-audit-fixtures/malformed-exported.xml +10 -0
  219. package/tests/fixtures/android/manifest-audit-fixtures/malformed.xml +5 -0
  220. package/tests/fixtures/android/manifest-audit-fixtures/missing-application.xml +7 -0
  221. package/tests/fixtures/android/manifest-audit-fixtures/missing-component-name.xml +11 -0
  222. package/tests/fixtures/android/manifest-audit-fixtures/permission-coverage.xml +29 -0
  223. package/tests/fixtures/android/manifest-audit-fixtures/risky-components.xml +62 -0
  224. package/tests/fixtures/android/manifest-audit-fixtures/risky-deep-links.xml +45 -0
  225. package/tests/fixtures/android/manifest-audit-fixtures/unresolved-placeholder.xml +14 -0
  226. package/tests/fixtures/android/manifest-audit-fixtures/unusual-namespace-prefix.xml +11 -0
  227. package/tests/fixtures/android/multi-module/app/build.gradle.kts +22 -0
  228. package/tests/fixtures/android/multi-module/app/src/androidTest/java/com/example/multimodule/app/ExampleInstrumentedTest.kt +3 -0
  229. package/tests/fixtures/android/multi-module/app/src/main/AndroidManifest.xml +11 -0
  230. package/tests/fixtures/android/multi-module/app/src/test/java/com/example/multimodule/app/ExampleUnitTest.kt +3 -0
  231. package/tests/fixtures/android/multi-module/build.gradle.kts +5 -0
  232. package/tests/fixtures/android/multi-module/core/build.gradle.kts +14 -0
  233. package/tests/fixtures/android/multi-module/core/src/main/AndroidManifest.xml +2 -0
  234. package/tests/fixtures/android/multi-module/feature-login/build.gradle.kts +18 -0
  235. package/tests/fixtures/android/multi-module/feature-login/src/main/AndroidManifest.xml +2 -0
  236. package/tests/fixtures/android/multi-module/gradle/libs.versions.toml +8 -0
  237. package/tests/fixtures/android/multi-module/gradle/wrapper/gradle-wrapper.properties +4 -0
  238. package/tests/fixtures/android/multi-module/settings.gradle.kts +4 -0
  239. package/tests/fixtures/android/non-android-gradle/build.gradle.kts +3 -0
  240. package/tests/fixtures/android/non-android-gradle/core/build.gradle.kts +3 -0
  241. package/tests/fixtures/android/non-android-gradle/core/src/main/java/com/example/core/Core.java +4 -0
  242. package/tests/fixtures/android/non-android-gradle/gradle/wrapper/gradle-wrapper.properties +4 -0
  243. package/tests/fixtures/android/non-android-gradle/settings.gradle.kts +2 -0
  244. package/tests/fixtures/android/partial/app/build.gradle.kts +7 -0
  245. package/tests/fixtures/android/partial/settings.gradle.kts +2 -0
  246. package/tests/fixtures/android/xml-view-app/app/build.gradle.kts +17 -0
  247. package/tests/fixtures/android/xml-view-app/app/src/main/AndroidManifest.xml +31 -0
  248. package/tests/fixtures/android/xml-view-app/app/src/main/kotlin/com/example/xmlviewapp/MainActivity.kt +7 -0
  249. package/tests/fixtures/android/xml-view-app/app/src/main/res/layout/activity_main.xml +4 -0
  250. package/tests/fixtures/android/xml-view-app/build.gradle.kts +4 -0
  251. package/tests/fixtures/android/xml-view-app/gradle/wrapper/gradle-wrapper.properties +4 -0
  252. package/tests/fixtures/android/xml-view-app/settings.gradle.kts +2 -0
@@ -0,0 +1,170 @@
1
+ import path from "node:path";
2
+ import { sortCandidateEvidence } from "../../ordering.js";
3
+ import { discoverAllowlistedExecutable } from "../discoverExecutable.js";
4
+ import { buildMinimalEnvironment } from "../minimalEnvironment.js";
5
+ import { writeExternalToolArtifact } from "../artifacts.js";
6
+ import { captureTargetSnapshot, buildExternalToolMutationReport } from "../mutation.js";
7
+ import { runBoundedExternalTool, buildCommandSummary, VERSION_PROBE_TIMEOUT_MS } from "../runBoundedExternalTool.js";
8
+ import { serializeSemgrepRulePack } from "./rules.js";
9
+ import { buildSemgrepArgs, parseSemgrepMajorVersion, semgrepCommandFamilyForVersion } from "./command.js";
10
+ import { parseSemgrepJson } from "./parseSemgrepJson.js";
11
+ // ---------------------------------------------------------------------------
12
+ // v0.4.1 Batch 7 — standalone Android Semgrep check. Not called from
13
+ // validateAndroidTarget or any active orchestration; not rendered in
14
+ // reports; no CLI effect. Batch 8 integrates.
15
+ // ---------------------------------------------------------------------------
16
+ export const ANDROID_SEMGREP_AUDIT_CHECK_ID = "android-semgrep-audit";
17
+ const SEMGREP_CANDIDATES = ["semgrep", "semgrep.exe"];
18
+ const SEMGREP_TIMEOUT_MS = 5 * 60_000;
19
+ function skippedCheck(reason, environmentRequirements = []) {
20
+ return {
21
+ id: ANDROID_SEMGREP_AUDIT_CHECK_ID,
22
+ category: "android-semgrep",
23
+ title: "Android Semgrep audit",
24
+ status: "skipped",
25
+ requirementLevel: "optional",
26
+ ran: false,
27
+ skipped: true,
28
+ skipInfo: {
29
+ checkId: ANDROID_SEMGREP_AUDIT_CHECK_ID,
30
+ reason,
31
+ requirementLevel: "optional",
32
+ missingCapability: "semgrep-executable",
33
+ verdictImpact: "does not apply until Batch 8 integration",
34
+ recommendedNextAction: "Install Semgrep locally, or run again after Batch 8 wires optional-tool selection into the CLI.",
35
+ },
36
+ evidence: [],
37
+ findings: [],
38
+ warnings: [],
39
+ errors: [],
40
+ sourcePaths: [],
41
+ confidence: "unknown",
42
+ environmentRequirements,
43
+ candidateEvidence: [],
44
+ };
45
+ }
46
+ export async function auditAndroidSemgrep(options) {
47
+ const targetRoot = path.resolve(options.targetRoot);
48
+ const discovered = (options.discover ?? (() => discoverAllowlistedExecutable(SEMGREP_CANDIDATES, targetRoot)))();
49
+ if (!discovered.available) {
50
+ return skippedCheck("Semgrep executable was not found on PATH.", ["Requires a Semgrep executable (semgrep) on PATH."]);
51
+ }
52
+ const env = buildMinimalEnvironment();
53
+ const versionInput = { command: discovered.command, args: ["--version"], cwd: targetRoot, timeoutMs: VERSION_PROBE_TIMEOUT_MS, env };
54
+ const versionOutcome = await runBoundedExternalTool(options.executor, versionInput);
55
+ if (versionOutcome.result.timedOut || versionOutcome.result.exitCode === null) {
56
+ return skippedCheck("Semgrep version probe failed or timed out.");
57
+ }
58
+ const rawVersion = versionOutcome.stdout.trim();
59
+ const majorVersion = parseSemgrepMajorVersion(rawVersion);
60
+ const family = semgrepCommandFamilyForVersion(majorVersion);
61
+ if (!family) {
62
+ return skippedCheck(`Semgrep version "${rawVersion || "(unknown)"}" is not a supported version family.`);
63
+ }
64
+ const configArtifact = writeExternalToolArtifact(options.artifactRoot, "semgrep", "android-rules.yaml", serializeSemgrepRulePack(), "text");
65
+ const configAbsolutePath = path.resolve(options.artifactRoot, configArtifact.relativePath);
66
+ const args = buildSemgrepArgs(family, configAbsolutePath, targetRoot);
67
+ const commandSummary = buildCommandSummary(discovered.basename, args, targetRoot, path.resolve(options.artifactRoot));
68
+ const before = captureTargetSnapshot(targetRoot, true);
69
+ const analysisOutcome = await runBoundedExternalTool(options.executor, { command: discovered.command, args, cwd: targetRoot, timeoutMs: SEMGREP_TIMEOUT_MS, env });
70
+ const after = captureTargetSnapshot(targetRoot, true);
71
+ const mutation = buildExternalToolMutationReport(before, after);
72
+ const warnings = [];
73
+ if (analysisOutcome.stdoutTruncated)
74
+ warnings.push("stdout was truncated; JSON output was not parsed as complete.");
75
+ if (analysisOutcome.stderrTruncated)
76
+ warnings.push("stderr was truncated.");
77
+ if (!mutation.comparable)
78
+ warnings.push(`Target mutation evidence unavailable: ${mutation.reason ?? "unknown reason"}`);
79
+ if (mutation.unexpectedChanges.length > 0)
80
+ warnings.push(`Unexpected target change(s) observed: ${mutation.unexpectedChanges.join(", ")}`);
81
+ if (analysisOutcome.result.timedOut) {
82
+ return {
83
+ id: ANDROID_SEMGREP_AUDIT_CHECK_ID,
84
+ category: "android-semgrep",
85
+ title: "Android Semgrep audit",
86
+ status: "inconclusive",
87
+ requirementLevel: "optional",
88
+ ran: true,
89
+ skipped: false,
90
+ evidence: [`version=${rawVersion}`, `commandFamily=${family}`],
91
+ findings: [],
92
+ warnings: [...warnings, `Semgrep timed out after ${SEMGREP_TIMEOUT_MS}ms.`],
93
+ errors: [],
94
+ sourcePaths: [],
95
+ confidence: "medium",
96
+ environmentRequirements: [],
97
+ candidateEvidence: [],
98
+ };
99
+ }
100
+ if (analysisOutcome.stdoutTruncated) {
101
+ return {
102
+ id: ANDROID_SEMGREP_AUDIT_CHECK_ID,
103
+ category: "android-semgrep",
104
+ title: "Android Semgrep audit",
105
+ status: "inconclusive",
106
+ requirementLevel: "optional",
107
+ ran: true,
108
+ skipped: false,
109
+ evidence: [`version=${rawVersion}`, `commandFamily=${family}`],
110
+ findings: [],
111
+ warnings,
112
+ errors: [],
113
+ sourcePaths: [],
114
+ confidence: "low",
115
+ environmentRequirements: [],
116
+ candidateEvidence: [],
117
+ };
118
+ }
119
+ const parsed = parseSemgrepJson(analysisOutcome.stdout, targetRoot, configArtifact.sha256);
120
+ const isFindingsExitCode = analysisOutcome.result.exitCode === 0 || analysisOutcome.result.exitCode === 1;
121
+ if (parsed.malformed) {
122
+ return {
123
+ id: ANDROID_SEMGREP_AUDIT_CHECK_ID,
124
+ category: "android-semgrep",
125
+ title: "Android Semgrep audit",
126
+ status: isFindingsExitCode ? "inconclusive" : "failed",
127
+ requirementLevel: "optional",
128
+ ran: true,
129
+ skipped: false,
130
+ evidence: [`version=${rawVersion}`, `commandFamily=${family}`, `exitCode=${analysisOutcome.result.exitCode}`],
131
+ findings: [],
132
+ warnings,
133
+ errors: isFindingsExitCode ? [] : ["Semgrep exited with a non-findings error and produced no parseable JSON report."],
134
+ sourcePaths: [],
135
+ confidence: "low",
136
+ environmentRequirements: [],
137
+ candidateEvidence: [],
138
+ };
139
+ }
140
+ for (const parserError of parsed.parserErrors) {
141
+ warnings.push(`Semgrep reported a parser error: ${parserError}`);
142
+ }
143
+ if (parsed.truncated)
144
+ warnings.push(`Finding count exceeded the bounded limit; results were truncated.`);
145
+ const findingsById = new Map(parsed.findings.map((f) => [f.id, f]));
146
+ const candidatesById = new Map(parsed.candidates.map((c) => [c.id, c]));
147
+ const findings = [...findingsById.values()].sort((a, b) => a.id.localeCompare(b.id));
148
+ const candidateEvidence = sortCandidateEvidence([...candidatesById.values()]);
149
+ // A findings exit code (0 or 1) with valid JSON is never treated as a
150
+ // process crash — Semgrep uses 1 to mean "findings were reported", not
151
+ // "the tool failed" (agents.txt Batch 7 section 10.11).
152
+ const status = findings.length > 0 ? "failed" : isFindingsExitCode ? "passed" : "error";
153
+ return {
154
+ id: ANDROID_SEMGREP_AUDIT_CHECK_ID,
155
+ category: "android-semgrep",
156
+ title: "Android Semgrep audit",
157
+ status,
158
+ requirementLevel: "optional",
159
+ ran: true,
160
+ skipped: false,
161
+ evidence: [`version=${rawVersion}`, `commandFamily=${family}`, `scanned=${parsed.scannedCount}`, `command=${commandSummary}`],
162
+ findings,
163
+ warnings,
164
+ errors: status === "error" ? ["Semgrep exited with a non-findings error."] : [],
165
+ sourcePaths: [],
166
+ confidence: parsed.parserErrors.length > 0 ? "medium" : "high",
167
+ environmentRequirements: [],
168
+ candidateEvidence,
169
+ };
170
+ }
@@ -0,0 +1,28 @@
1
+ // ---------------------------------------------------------------------------
2
+ // v0.4.1 Batch 7 — fixed Semgrep command planning.
3
+ //
4
+ // Exactly two supported command families, selected only by parsed major
5
+ // version — never by trial-and-error after a failed attempt. No caller-
6
+ // provided flags, config identifiers, or excludes are ever accepted.
7
+ // ---------------------------------------------------------------------------
8
+ export const SEMGREP_FIXED_EXCLUDES = [".git", "node_modules", "build", "dist", "out", "coverage", ".gradle", "reports", "lab-output", ".my-dev-kit", "*.apk", "*.aab"];
9
+ export function semgrepCommandFamilyForVersion(majorVersion) {
10
+ if (majorVersion === undefined)
11
+ return undefined;
12
+ if (majorVersion >= 1)
13
+ return "modern";
14
+ if (majorVersion === 0)
15
+ return "legacy";
16
+ return undefined;
17
+ }
18
+ export function parseSemgrepMajorVersion(rawVersion) {
19
+ const match = /(\d+)\.\d+\.\d+/.exec(rawVersion);
20
+ if (!match)
21
+ return undefined;
22
+ return Number.parseInt(match[1], 10);
23
+ }
24
+ export function buildSemgrepArgs(family, configPath, targetRoot) {
25
+ const excludeArgs = SEMGREP_FIXED_EXCLUDES.flatMap((pattern) => ["--exclude", pattern]);
26
+ const base = ["--config", configPath, "--json", "--metrics=off", "--disable-version-check", ...excludeArgs, targetRoot];
27
+ return family === "modern" ? ["scan", ...base] : base;
28
+ }
@@ -0,0 +1,2 @@
1
+ export { auditAndroidSemgrep, ANDROID_SEMGREP_AUDIT_CHECK_ID } from "./checkResult.js";
2
+ export { ANDROID_SEMGREP_RULE_PACK, assertUniqueSemgrepRuleIds, serializeSemgrepRulePack } from "./rules.js";
@@ -0,0 +1,141 @@
1
+ import path from "node:path";
2
+ import { makeAndroidFinding } from "../../../audit/androidFinding.js";
3
+ import { makeCandidateEvidence } from "../../candidateEvidence.js";
4
+ import { buildAndroidSourceLocation } from "../../sourceLocation.js";
5
+ import { safeJsonParse, DEFAULT_MAX_MESSAGE_LENGTH } from "../boundedOutput.js";
6
+ // ---------------------------------------------------------------------------
7
+ // v0.4.1 Batch 7 — bounded Semgrep JSON normalization.
8
+ //
9
+ // Only the one Batch 1 Semgrep rule id (android-optional-tool-semgrep-
10
+ // evidence) is used internally; the external Semgrep rule id is preserved
11
+ // separately as `externalRuleId`. Full source snippets (extra.lines) and
12
+ // complete metavariable values are never retained.
13
+ // ---------------------------------------------------------------------------
14
+ const MAX_RESULTS = 5_000;
15
+ const MAX_SKIPPED_PATHS = 200;
16
+ function severityToProjectSeverity(severity) {
17
+ switch (severity) {
18
+ case "ERROR":
19
+ return "major";
20
+ case "WARNING":
21
+ return "minor";
22
+ default:
23
+ return "informational";
24
+ }
25
+ }
26
+ function boundedMessage(message) {
27
+ const text = typeof message === "string" ? message : "";
28
+ return text.length > DEFAULT_MAX_MESSAGE_LENGTH ? `${text.slice(0, DEFAULT_MAX_MESSAGE_LENGTH)}...` : text;
29
+ }
30
+ export function parseSemgrepJson(rawStdout, targetRoot, configFingerprint) {
31
+ const parsed = safeJsonParse(rawStdout);
32
+ if (!parsed.ok) {
33
+ return { malformed: true, findings: [], candidates: [], scannedCount: 0, skippedPaths: [], parserErrors: [], truncated: parsed.truncated };
34
+ }
35
+ const doc = parsed.value;
36
+ const toolVersion = typeof doc.version === "string" ? doc.version : undefined;
37
+ const results = Array.isArray(doc.results) ? doc.results : [];
38
+ const errors = Array.isArray(doc.errors) ? doc.errors : [];
39
+ const pathsSection = doc.paths;
40
+ const scanned = Array.isArray(pathsSection?.scanned) ? pathsSection.scanned : [];
41
+ const skipped = Array.isArray(pathsSection?.skipped) ? pathsSection.skipped : [];
42
+ const findings = [];
43
+ const candidates = [];
44
+ let truncated = false;
45
+ for (const rawResult of results.slice(0, MAX_RESULTS)) {
46
+ if (results.length > MAX_RESULTS)
47
+ truncated = true;
48
+ const result = rawResult;
49
+ const checkId = typeof result.check_id === "string" ? result.check_id : "(unknown-rule)";
50
+ const relativePath = typeof result.path === "string" ? result.path : undefined;
51
+ const start = result.start;
52
+ const line = typeof start?.line === "number" ? start.line : undefined;
53
+ const extra = result.extra ?? {};
54
+ const severity = typeof extra.severity === "string" ? extra.severity : undefined;
55
+ const message = boundedMessage(extra.message);
56
+ const projectSeverity = severityToProjectSeverity(severity);
57
+ if (!relativePath) {
58
+ candidates.push(makeCandidateEvidence({
59
+ ruleId: "android-optional-tool-semgrep-evidence",
60
+ category: "android-semgrep",
61
+ confidence: "low",
62
+ location: { path: "(unknown)" },
63
+ summary: `Semgrep result "${checkId}" had no reportable path`,
64
+ rawValue: undefined,
65
+ resolutionState: "unresolved",
66
+ staticAnalysisLimitations: ["Semgrep static analysis only; does not prove runtime exploitability."],
67
+ }));
68
+ continue;
69
+ }
70
+ const absolutePath = path.resolve(targetRoot, relativePath);
71
+ let location;
72
+ try {
73
+ location = buildAndroidSourceLocation(targetRoot, absolutePath, { line });
74
+ }
75
+ catch {
76
+ candidates.push(makeCandidateEvidence({
77
+ ruleId: "android-optional-tool-semgrep-evidence",
78
+ category: "android-semgrep",
79
+ confidence: "low",
80
+ location: { path: relativePath },
81
+ summary: `Semgrep result "${checkId}" reported a path outside the target`,
82
+ rawValue: relativePath,
83
+ resolutionState: "unresolved",
84
+ staticAnalysisLimitations: ["Semgrep static analysis only; does not prove runtime exploitability."],
85
+ }));
86
+ continue;
87
+ }
88
+ if (projectSeverity === "informational" || severity === undefined) {
89
+ candidates.push(makeCandidateEvidence({
90
+ ruleId: "android-optional-tool-semgrep-evidence",
91
+ category: "android-semgrep",
92
+ confidence: "medium",
93
+ location,
94
+ summary: `Semgrep ${severity ?? "unknown-severity"} result: ${checkId} — ${message}`,
95
+ rawValue: message,
96
+ resolutionState: "resolved",
97
+ staticAnalysisLimitations: [`External rule: ${checkId}`, "Semgrep static analysis only; does not prove runtime exploitability."],
98
+ }));
99
+ continue;
100
+ }
101
+ findings.push(makeAndroidFinding({
102
+ ruleId: "android-optional-tool-semgrep-evidence",
103
+ title: `Semgrep finding: ${checkId}`,
104
+ severity: projectSeverity,
105
+ confidence: "medium",
106
+ description: message || "Semgrep reported a finding for this rule.",
107
+ manifestPath: relativePath,
108
+ identity: `semgrep:${checkId}:${configFingerprint.slice(0, 16)}`,
109
+ location: { line },
110
+ evidenceDetails: [`externalRuleId=${checkId}`, `toolSeverity=${severity}`],
111
+ recommendation: "Review the Semgrep rule pack entry and remediate the matched pattern.",
112
+ }));
113
+ }
114
+ for (const rawSkip of skipped.slice(0, MAX_SKIPPED_PATHS)) {
115
+ const skip = rawSkip;
116
+ const skippedPath = typeof skip === "string" ? skip : typeof skip.path === "string" ? skip.path : undefined;
117
+ if (skippedPath) {
118
+ candidates.push(makeCandidateEvidence({
119
+ ruleId: "android-optional-tool-semgrep-evidence",
120
+ category: "android-semgrep",
121
+ confidence: "low",
122
+ location: { path: skippedPath },
123
+ summary: "Semgrep skipped this path during the scan",
124
+ rawValue: undefined,
125
+ resolutionState: "not-applicable",
126
+ staticAnalysisLimitations: ["Semgrep static analysis only."],
127
+ }));
128
+ }
129
+ }
130
+ const parserErrors = errors.slice(0, 50).map((e) => (typeof e === "object" && e !== null && "message" in e ? boundedMessage(e.message) : "Semgrep reported an error."));
131
+ return {
132
+ malformed: false,
133
+ toolVersion,
134
+ findings,
135
+ candidates,
136
+ scannedCount: scanned.length,
137
+ skippedPaths: skipped.map((s) => (typeof s === "string" ? s : typeof s?.path === "string" ? s.path : "")).filter(Boolean),
138
+ parserErrors,
139
+ truncated,
140
+ };
141
+ }
@@ -0,0 +1,105 @@
1
+ // ---------------------------------------------------------------------------
2
+ // v0.4.1 Batch 7 — package-owned, bounded Android-focused Semgrep rule pack.
3
+ //
4
+ // Small, stable supplement to the internal checks — NOT a replacement.
5
+ // Deliberately excludes hardcoded-secret rules (owned by Batch 4's
6
+ // secretCandidates analyzer) and manifest-attribute rules (owned by the
7
+ // internal manifest parser); covers source-level patterns those analyzers
8
+ // do not reach. Caller-provided configuration is never accepted — this is
9
+ // the only rule pack Semgrep is ever invoked with.
10
+ // ---------------------------------------------------------------------------
11
+ export const ANDROID_SEMGREP_RULE_PACK = [
12
+ {
13
+ id: "android-semgrep-trust-all-hostname-verifier",
14
+ languages: ["java", "kotlin"],
15
+ message: "A HostnameVerifier implementation unconditionally returns true, disabling TLS hostname verification.",
16
+ severity: "ERROR",
17
+ cwe: ["CWE-297"],
18
+ owasp: ["A02:2021"],
19
+ patterns: ["new HostnameVerifier() { ... boolean verify(...) { return true; } ... }"],
20
+ },
21
+ {
22
+ id: "android-semgrep-permissive-x509-trust-manager",
23
+ languages: ["java", "kotlin"],
24
+ message: "An X509TrustManager implementation has an empty or no-op certificate validation method, accepting any certificate chain.",
25
+ severity: "ERROR",
26
+ cwe: ["CWE-295"],
27
+ owasp: ["A02:2021"],
28
+ patterns: [
29
+ "class $T : X509TrustManager { ... override fun checkServerTrusted(...) { } ... }",
30
+ "new X509TrustManager() { ... public void checkServerTrusted(...) { } ... }",
31
+ ],
32
+ },
33
+ {
34
+ id: "android-semgrep-ssl-error-handler-proceed",
35
+ languages: ["java", "kotlin"],
36
+ message: "onReceivedSslError calls handler.proceed(), ignoring the certificate error and continuing the connection.",
37
+ severity: "ERROR",
38
+ cwe: ["CWE-295"],
39
+ patterns: ["... onReceivedSslError(...) { ... $HANDLER.proceed() ... }"],
40
+ },
41
+ {
42
+ id: "android-semgrep-webview-universal-file-url-access",
43
+ languages: ["java", "kotlin"],
44
+ message: "setAllowUniversalAccessFromFileURLs(true) allows file:// content to access any origin.",
45
+ severity: "WARNING",
46
+ cwe: ["CWE-668"],
47
+ patterns: ["$WEBVIEW.settings.allowUniversalAccessFromFileURLs = true", "$SETTINGS.setAllowUniversalAccessFromFileURLs(true)"],
48
+ },
49
+ {
50
+ id: "android-semgrep-webview-debugging-enabled",
51
+ languages: ["java", "kotlin"],
52
+ message: "WebView.setWebContentsDebuggingEnabled(true) is set unconditionally, which may ship to release builds.",
53
+ severity: "INFO",
54
+ patterns: ["WebView.setWebContentsDebuggingEnabled(true)"],
55
+ },
56
+ {
57
+ id: "android-semgrep-world-readable-writable-mode",
58
+ languages: ["java", "kotlin"],
59
+ message: "A file or preferences API is opened with MODE_WORLD_READABLE or MODE_WORLD_WRITEABLE.",
60
+ severity: "ERROR",
61
+ cwe: ["CWE-732"],
62
+ patterns: ["$CTX.getSharedPreferences($NAME, Context.MODE_WORLD_READABLE)", "$CTX.getSharedPreferences($NAME, Context.MODE_WORLD_WRITEABLE)", "$CTX.openFileOutput($NAME, Context.MODE_WORLD_READABLE)", "$CTX.openFileOutput($NAME, Context.MODE_WORLD_WRITEABLE)"],
63
+ },
64
+ {
65
+ id: "android-semgrep-weak-digest-md5-sha1",
66
+ languages: ["java", "kotlin"],
67
+ message: "MessageDigest.getInstance uses a weak digest algorithm (MD5 or SHA-1) for security-sensitive hashing.",
68
+ severity: "WARNING",
69
+ cwe: ["CWE-327"],
70
+ patterns: ['MessageDigest.getInstance("MD5")', 'MessageDigest.getInstance("SHA-1")', 'MessageDigest.getInstance("SHA1")'],
71
+ },
72
+ {
73
+ id: "android-semgrep-insecure-cipher-transformation",
74
+ languages: ["java", "kotlin"],
75
+ message: "Cipher.getInstance uses an insecure transformation (ECB mode, DES, or RC4).",
76
+ severity: "ERROR",
77
+ cwe: ["CWE-327"],
78
+ patterns: ['Cipher.getInstance("...ECB...")', 'Cipher.getInstance("DES")', 'Cipher.getInstance("RC4")'],
79
+ },
80
+ ];
81
+ export function assertUniqueSemgrepRuleIds() {
82
+ const ids = ANDROID_SEMGREP_RULE_PACK.map((rule) => rule.id);
83
+ const unique = new Set(ids);
84
+ if (unique.size !== ids.length) {
85
+ throw new Error("Duplicate Semgrep rule id detected in ANDROID_SEMGREP_RULE_PACK.");
86
+ }
87
+ }
88
+ // Deterministic YAML-compatible serialization (JSON is valid YAML; Semgrep
89
+ // accepts a .yaml/.yml config file). Key order is fixed so repeated calls
90
+ // produce byte-identical output (config fingerprinting depends on this).
91
+ export function serializeSemgrepRulePack() {
92
+ const rules = ANDROID_SEMGREP_RULE_PACK.map((rule) => ({
93
+ id: rule.id,
94
+ languages: [...rule.languages],
95
+ message: rule.message,
96
+ severity: rule.severity,
97
+ metadata: {
98
+ ...(rule.cwe ? { cwe: [...rule.cwe] } : {}),
99
+ ...(rule.owasp ? { owasp: [...rule.owasp] } : {}),
100
+ "android-package-owned": true,
101
+ },
102
+ "pattern-either": rule.patterns.map((pattern) => ({ pattern })),
103
+ }));
104
+ return JSON.stringify({ rules }, null, 2);
105
+ }
@@ -0,0 +1,107 @@
1
+ // ---------------------------------------------------------------------------
2
+ // v0.4.1 Batch 7 — common contract for optional external Android
3
+ // security-tool evidence (Semgrep, OSV-Scanner, Android Lint,
4
+ // OWASP Dependency-Check).
5
+ //
6
+ // This module defines identities and request/result shapes only. No process
7
+ // ever executes because a type exists here. Every tool remains disconnected
8
+ // from validateAndroidTarget, the CLI, verdict calculation, and active
9
+ // reports until Batch 8 — see each adapter's checkResult.ts for the
10
+ // standalone AndroidCheckResult-compatible builder.
11
+ // ---------------------------------------------------------------------------
12
+ export const ANDROID_EXTERNAL_TOOL_IDS = ["semgrep", "osv", "android-lint", "dependency-check"];
13
+ export function isAndroidExternalSecurityToolId(value) {
14
+ return ANDROID_EXTERNAL_TOOL_IDS.includes(value);
15
+ }
16
+ // Deterministic dispatch order — never concurrent, never caller-controlled.
17
+ export const ANDROID_EXTERNAL_TOOL_ORDER = ["semgrep", "osv", "android-lint", "dependency-check"];
18
+ export const ANDROID_EXTERNAL_TOOL_NETWORK_POLICIES = ["deny", "allow-for-requested-tool"];
19
+ const MIN_TIMEOUT_OVERRIDE_MS = 5_000;
20
+ const MAX_TIMEOUT_OVERRIDE_MS = 30 * 60_000;
21
+ // Deduplicates and orders requested tool ids deterministically, and rejects
22
+ // any id outside the closed ANDROID_EXTERNAL_TOOL_IDS union at this single
23
+ // contract boundary — no caller can smuggle an arbitrary tool identity past
24
+ // this point.
25
+ export function normalizeAndroidExternalToolRequest(request) {
26
+ if (!request || !Array.isArray(request.requestedTools) || request.requestedTools.length === 0) {
27
+ return {
28
+ ok: true,
29
+ value: {
30
+ tools: [],
31
+ targetRoot: request?.targetRoot ?? "",
32
+ artifactRoot: request?.artifactRoot ?? "",
33
+ networkPolicy: request?.networkPolicy ?? "deny",
34
+ timeoutOverrideMs: request?.timeoutOverrideMs,
35
+ },
36
+ };
37
+ }
38
+ const unknown = request.requestedTools.filter((id) => !isAndroidExternalSecurityToolId(id));
39
+ if (unknown.length > 0) {
40
+ return { ok: false, error: `Unknown external security tool id(s): ${unknown.join(", ")}` };
41
+ }
42
+ if (!request.targetRoot)
43
+ return { ok: false, error: "targetRoot is required." };
44
+ if (!request.artifactRoot)
45
+ return { ok: false, error: "artifactRoot is required." };
46
+ if (request.timeoutOverrideMs !== undefined) {
47
+ if (request.timeoutOverrideMs < MIN_TIMEOUT_OVERRIDE_MS || request.timeoutOverrideMs > MAX_TIMEOUT_OVERRIDE_MS) {
48
+ return { ok: false, error: `timeoutOverrideMs must be between ${MIN_TIMEOUT_OVERRIDE_MS} and ${MAX_TIMEOUT_OVERRIDE_MS}.` };
49
+ }
50
+ }
51
+ const requestedSet = new Set(request.requestedTools);
52
+ const tools = ANDROID_EXTERNAL_TOOL_ORDER.filter((id) => requestedSet.has(id));
53
+ return {
54
+ ok: true,
55
+ value: {
56
+ tools,
57
+ targetRoot: request.targetRoot,
58
+ artifactRoot: request.artifactRoot,
59
+ networkPolicy: request.networkPolicy ?? "deny",
60
+ timeoutOverrideMs: request.timeoutOverrideMs,
61
+ },
62
+ };
63
+ }
64
+ // ---------------------------------------------------------------------------
65
+ // Artifact references — Batch 7 introduces this concept new (no existing
66
+ // owner copies/hashes generated tool output); Batch 8 decides where active
67
+ // reports place these.
68
+ // ---------------------------------------------------------------------------
69
+ export const ANDROID_EXTERNAL_TOOL_ARTIFACT_KINDS = ["json", "xml", "sarif", "text"];
70
+ export const ANDROID_EXTERNAL_TOOL_PARSE_STATUSES = ["parsed", "malformed", "truncated", "not-attempted"];
71
+ // ---------------------------------------------------------------------------
72
+ // Common per-tool execution result — every adapter's checkResult.ts folds
73
+ // this (plus tool-specific findings/candidates) into a standalone
74
+ // AndroidCheckResult, exactly as the existing Gradle operations fold a
75
+ // CommandExecutionResult via buildGradleOperationCheckResult.
76
+ // ---------------------------------------------------------------------------
77
+ export const ANDROID_EXTERNAL_TOOL_STATUSES = [
78
+ "not-requested",
79
+ "skipped",
80
+ "completed-without-findings",
81
+ "completed-with-findings",
82
+ "inconclusive",
83
+ "failed",
84
+ ];
85
+ export function notRequestedResult(toolId, targetRoot, networkPolicy) {
86
+ return {
87
+ toolId,
88
+ requested: false,
89
+ available: false,
90
+ executed: false,
91
+ status: "not-requested",
92
+ commandSummary: "(not requested)",
93
+ workingDirectory: targetRoot,
94
+ networkPolicy,
95
+ durationMs: 0,
96
+ exitCode: null,
97
+ timedOut: false,
98
+ stdoutTruncated: false,
99
+ stderrTruncated: false,
100
+ artifacts: [],
101
+ warnings: [],
102
+ errors: [],
103
+ skips: [],
104
+ mutationEvidence: { comparable: true, expectedGeneratedChanges: [], unexpectedChanges: [] },
105
+ limitations: [],
106
+ };
107
+ }