@dailephd/my-dev-kit-lab 0.4.0 → 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.
- package/README.md +10 -61
- package/dist/scripts/security/validate.js +38 -5
- package/dist/src/mobile/android/advancedSecurity/backupConfiguration/analyzeBackupConfiguration.js +300 -0
- package/dist/src/mobile/android/advancedSecurity/backupConfiguration/checkResult.js +41 -0
- package/dist/src/mobile/android/advancedSecurity/backupConfiguration/index.js +8 -0
- package/dist/src/mobile/android/advancedSecurity/backupConfiguration/manifestEvidence.js +102 -0
- package/dist/src/mobile/android/advancedSecurity/backupConfiguration/parseBackupRules.js +110 -0
- package/dist/src/mobile/android/advancedSecurity/backupConfiguration/types.js +27 -0
- package/dist/src/mobile/android/advancedSecurity/candidateEvidence.js +50 -0
- package/dist/src/mobile/android/advancedSecurity/clipboard/analyzeClipboardSecurity.js +166 -0
- package/dist/src/mobile/android/advancedSecurity/clipboard/checkResult.js +74 -0
- package/dist/src/mobile/android/advancedSecurity/clipboard/collectClipboardEvidence.js +82 -0
- package/dist/src/mobile/android/advancedSecurity/clipboard/index.js +1 -0
- package/dist/src/mobile/android/advancedSecurity/clipboard/types.js +1 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/androidLint/checkResult.js +255 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/androidLint/index.js +1 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/androidLint/parseLintSarif.js +37 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/androidLint/parseLintXml.js +36 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/artifacts.js +95 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/boundedOutput.js +33 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/dependencyCheck/checkResult.js +173 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/dependencyCheck/command.js +33 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/dependencyCheck/index.js +1 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/dependencyCheck/parseDependencyCheckJson.js +133 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/discoverExecutable.js +13 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/index.js +8 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/minimalEnvironment.js +26 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/mutation.js +77 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/osv/checkResult.js +161 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/osv/command.js +25 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/osv/index.js +1 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/osv/parseOsvJson.js +136 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/runBoundedExternalTool.js +51 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/runRequestedAndroidExternalTools.js +54 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/semgrep/checkResult.js +170 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/semgrep/command.js +28 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/semgrep/index.js +2 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/semgrep/parseSemgrepJson.js +141 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/semgrep/rules.js +105 -0
- package/dist/src/mobile/android/advancedSecurity/externalTools/types.js +107 -0
- package/dist/src/mobile/android/advancedSecurity/fileProvider/checkResult.js +188 -0
- package/dist/src/mobile/android/advancedSecurity/fileProvider/index.js +1 -0
- package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/analyzeFirebaseGoogleServices.js +197 -0
- package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/checkResult.js +77 -0
- package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/collectFirebaseServiceEvidence.js +193 -0
- package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/discoverFirebaseArtifacts.js +128 -0
- package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/index.js +1 -0
- package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/parseDatabaseRules.js +55 -0
- package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/parseFirebaseConfig.js +75 -0
- package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/parseFirestoreStorageRules.js +88 -0
- package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/parseGoogleServicesConfig.js +55 -0
- package/dist/src/mobile/android/advancedSecurity/firebaseGoogle/types.js +6 -0
- package/dist/src/mobile/android/advancedSecurity/networkSecurity/analyzeNetworkSecurity.js +394 -0
- package/dist/src/mobile/android/advancedSecurity/networkSecurity/checkResult.js +47 -0
- package/dist/src/mobile/android/advancedSecurity/networkSecurity/deriveEffectiveNetworkPolicy.js +52 -0
- package/dist/src/mobile/android/advancedSecurity/networkSecurity/index.js +14 -0
- package/dist/src/mobile/android/advancedSecurity/networkSecurity/manifestEvidence.js +109 -0
- package/dist/src/mobile/android/advancedSecurity/networkSecurity/parseNetworkSecurityConfig.js +186 -0
- package/dist/src/mobile/android/advancedSecurity/networkSecurity/types.js +1 -0
- package/dist/src/mobile/android/advancedSecurity/ordering.js +44 -0
- package/dist/src/mobile/android/advancedSecurity/redaction.js +54 -0
- package/dist/src/mobile/android/advancedSecurity/releaseConfiguration/analyzeReleaseConfiguration.js +221 -0
- package/dist/src/mobile/android/advancedSecurity/releaseConfiguration/checkResult.js +39 -0
- package/dist/src/mobile/android/advancedSecurity/releaseConfiguration/index.js +7 -0
- package/dist/src/mobile/android/advancedSecurity/releaseConfiguration/manifestEvidence.js +19 -0
- package/dist/src/mobile/android/advancedSecurity/resourceReference.js +69 -0
- package/dist/src/mobile/android/advancedSecurity/resourceResolution.js +118 -0
- package/dist/src/mobile/android/advancedSecurity/ruleIds.js +141 -0
- package/dist/src/mobile/android/advancedSecurity/secretCandidates/analyzeSecretCandidates.js +116 -0
- package/dist/src/mobile/android/advancedSecurity/secretCandidates/checkResult.js +80 -0
- package/dist/src/mobile/android/advancedSecurity/secretCandidates/discoverSecretSourceFiles.js +152 -0
- package/dist/src/mobile/android/advancedSecurity/secretCandidates/index.js +9 -0
- package/dist/src/mobile/android/advancedSecurity/secretCandidates/matchSecretCandidates.js +136 -0
- package/dist/src/mobile/android/advancedSecurity/secretCandidates/suppressValue.js +61 -0
- package/dist/src/mobile/android/advancedSecurity/secretCandidates/types.js +11 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveCategories.js +34 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveData/classifyDirectExpression.js +99 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveData/classifySensitiveIdentifier.js +111 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveData/index.js +3 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveData/localSourceContext.js +90 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveData/splitArguments.js +59 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveLogging/analyzeSensitiveLogging.js +117 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveLogging/checkResult.js +74 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveLogging/collectLoggingEvidence.js +87 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveLogging/index.js +1 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveLogging/types.js +1 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveStorage/analyzeSensitiveStorage.js +182 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveStorage/checkResult.js +74 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveStorage/collectStorageEvidence.js +109 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveStorage/index.js +1 -0
- package/dist/src/mobile/android/advancedSecurity/sensitiveStorage/types.js +4 -0
- package/dist/src/mobile/android/advancedSecurity/signingConfiguration/analyzeSigningConfiguration.js +270 -0
- package/dist/src/mobile/android/advancedSecurity/signingConfiguration/checkResult.js +119 -0
- package/dist/src/mobile/android/advancedSecurity/signingConfiguration/discoverKeystoreCandidates.js +55 -0
- package/dist/src/mobile/android/advancedSecurity/signingConfiguration/extractSigningConfigurations.js +81 -0
- package/dist/src/mobile/android/advancedSecurity/signingConfiguration/index.js +8 -0
- package/dist/src/mobile/android/advancedSecurity/signingConfiguration/types.js +20 -0
- package/dist/src/mobile/android/advancedSecurity/sourceLocation.js +15 -0
- package/dist/src/mobile/android/advancedSecurity/webview/checkResult.js +210 -0
- package/dist/src/mobile/android/advancedSecurity/webview/index.js +1 -0
- package/dist/src/mobile/android/gradle/moduleMetadataExtractor.js +123 -17
- package/dist/src/mobile/android/manifest/parseAndroidManifest.js +27 -4
- package/dist/src/mobile/android/manifest/xml/parseXml.js +25 -9
- package/dist/src/mobile/android/report/model.js +32 -1
- package/dist/src/mobile/android/report/renderAndroidReport.js +63 -0
- package/dist/src/mobile/android/validate/validateAndroidTarget.js +146 -3
- package/dist/src/mobile/android/validation/checkResult.js +7 -0
- package/dist/src/securityValidation/commandRunner.js +10 -1
- package/dist/src/securityValidation/validate/cliOptions.js +46 -0
- package/package.json +1 -1
- package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/debug/res/xml/ambiguous_backup_rules.xml +4 -0
- package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/main/res/xml/ambiguous_backup_rules.xml +4 -0
- package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/main/res/xml/backup_rules.xml +5 -0
- package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/main/res/xml/broad_backup_rules.xml +4 -0
- package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/main/res/xml/data_extraction_rules.xml +10 -0
- package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/main/res/xml/unsupported_domain_rules.xml +4 -0
- package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-b/src/main/res/xml/malformed_backup_rules.xml +4 -0
- package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-b/src/main/res/xml/unsupported_root.xml +4 -0
- package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/debug/res/xml/ambiguous_config.xml +4 -0
- package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/ambiguous_config.xml +4 -0
- package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/base_cleartext_false.xml +8 -0
- package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/broad_and_duplicate_domains.xml +8 -0
- package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/empty_domain.xml +6 -0
- package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/malformed_pin.xml +11 -0
- package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/network_security_config.xml +26 -0
- package/tests/fixtures/android/advanced-security-fixtures/network-security/module-b/src/main/res/xml/malformed_network_security_config.xml +6 -0
- package/tests/fixtures/android/advanced-security-fixtures/network-security/module-b/src/main/res/xml/unsupported_root.xml +4 -0
- package/tests/fixtures/android/advanced-security-fixtures/release-configuration/groovy-release-debuggable-dynamic.gradle +7 -0
- package/tests/fixtures/android/advanced-security-fixtures/release-configuration/groovy-release-debuggable-false.gradle +9 -0
- package/tests/fixtures/android/advanced-security-fixtures/release-configuration/groovy-release-debuggable-true.gradle +13 -0
- package/tests/fixtures/android/advanced-security-fixtures/release-configuration/kotlin-release-debuggable-false.gradle.kts +7 -0
- package/tests/fixtures/android/advanced-security-fixtures/release-configuration/kotlin-release-debuggable-true.gradle.kts +10 -0
- package/tests/fixtures/android/advanced-security-fixtures/release-configuration/missing-release-build-type.gradle.kts +7 -0
- package/tests/fixtures/android/advanced-security-fixtures/resource-resolution/module-a/src/debug/res/xml/network_security_config.xml +8 -0
- package/tests/fixtures/android/advanced-security-fixtures/resource-resolution/module-a/src/main/res/xml/backup_rules.xml +4 -0
- package/tests/fixtures/android/advanced-security-fixtures/resource-resolution/module-a/src/main/res/xml/network_security_config.xml +8 -0
- package/tests/fixtures/android/advanced-security-fixtures/resource-resolution/module-b/src/main/res/xml/malformed_network_security_config.xml +7 -0
- package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/SampleActivity.java +8 -0
- package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/SampleActivity.kt +9 -0
- package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/build.gradle +11 -0
- package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/build.gradle.kts +11 -0
- package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/google-services.json +16 -0
- package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/local.properties +3 -0
- package/tests/fixtures/android/advanced-security-fixtures/secret-candidates-bounds/module/binary.properties +0 -0
- package/tests/fixtures/android/advanced-security-fixtures/secret-candidates-bounds/module/oversized.json +1 -0
- package/tests/fixtures/android/advanced-security-fixtures/signing-configuration/app/fake-release-keystore.jks +1 -0
- package/tests/fixtures/android/advanced-security-fixtures/signing-configuration/build/generated-fake.keystore +1 -0
|
@@ -17,6 +17,27 @@ function formatDuration(ms) {
|
|
|
17
17
|
return `${ms}ms`;
|
|
18
18
|
return `${(ms / 1000).toFixed(1)}s`;
|
|
19
19
|
}
|
|
20
|
+
// v0.4.1 Batch 8 — shared per-check rendering for the new ADVANCED SECURITY
|
|
21
|
+
// CHECKS and EXTERNAL TOOL RESULTS sections. Bounded: prints counts and
|
|
22
|
+
// identifiers, never raw stdout/stderr/environment/complete source (that
|
|
23
|
+
// bound is already enforced by each check builder before evidence reaches
|
|
24
|
+
// this report — this function does not itself receive raw tool output).
|
|
25
|
+
function renderCheckSummaryLines(lines, c) {
|
|
26
|
+
lines.push(` ${c.id} [${c.status}] — ${sanitize(c.title)}`);
|
|
27
|
+
lines.push(` category: ${c.category} findings: ${c.findings.length} candidates: ${c.candidateEvidence?.length ?? 0}`);
|
|
28
|
+
if (c.evidence.length > 0)
|
|
29
|
+
lines.push(` evidence: ${c.evidence.map(sanitize).join(" | ")}`);
|
|
30
|
+
if (c.durationMs !== undefined)
|
|
31
|
+
lines.push(` duration: ${formatDuration(c.durationMs)}`);
|
|
32
|
+
if (c.skipInfo)
|
|
33
|
+
lines.push(` skip reason: ${sanitize(c.skipInfo.reason)}`);
|
|
34
|
+
for (const w of c.warnings)
|
|
35
|
+
lines.push(` warning: ${sanitize(w)}`);
|
|
36
|
+
for (const e of c.errors)
|
|
37
|
+
lines.push(` error: ${sanitize(e)}`);
|
|
38
|
+
if (c.environmentRequirements.length > 0)
|
|
39
|
+
lines.push(` limitations: ${c.environmentRequirements.map(sanitize).join(" | ")}`);
|
|
40
|
+
}
|
|
20
41
|
export function renderAndroidTextReport(model) {
|
|
21
42
|
const lines = [];
|
|
22
43
|
// 1. Summary
|
|
@@ -143,6 +164,48 @@ export function renderAndroidTextReport(model) {
|
|
|
143
164
|
}
|
|
144
165
|
}
|
|
145
166
|
lines.push("");
|
|
167
|
+
// 15b. Advanced security checks (v0.4.1 Batch 8)
|
|
168
|
+
lines.push(divider());
|
|
169
|
+
lines.push("ADVANCED SECURITY CHECKS");
|
|
170
|
+
lines.push(divider());
|
|
171
|
+
if (model.advancedSecurityChecks.length === 0) {
|
|
172
|
+
lines.push("(none ran — not applicable to this target)");
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
for (const c of model.advancedSecurityChecks) {
|
|
176
|
+
renderCheckSummaryLines(lines, c);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
lines.push("");
|
|
180
|
+
// 15c. External tool results (v0.4.1 Batch 8)
|
|
181
|
+
lines.push(divider());
|
|
182
|
+
lines.push("EXTERNAL TOOL RESULTS");
|
|
183
|
+
lines.push(divider());
|
|
184
|
+
lines.push(`Requested external tools: ${model.metadata.requestedExternalTools.length > 0 ? model.metadata.requestedExternalTools.join(", ") : "(none — zero external-tool process execution)"}`);
|
|
185
|
+
if (model.metadata.requestedExternalTools.length > 0) {
|
|
186
|
+
lines.push(`Effective network policy: ${model.metadata.externalNetworkPolicy}`);
|
|
187
|
+
}
|
|
188
|
+
if (model.externalToolChecks.length === 0) {
|
|
189
|
+
lines.push("No external tools were requested.");
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
for (const c of model.externalToolChecks) {
|
|
193
|
+
renderCheckSummaryLines(lines, c);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
lines.push("");
|
|
197
|
+
// 15d. Candidate evidence (review required, distinct from confirmed findings)
|
|
198
|
+
lines.push(divider());
|
|
199
|
+
lines.push("CANDIDATE EVIDENCE (REVIEW REQUIRED — NOT CONFIRMED FINDINGS)");
|
|
200
|
+
lines.push(divider());
|
|
201
|
+
lines.push(sanitize(model.candidateEvidenceSummary.summary));
|
|
202
|
+
for (const c of [...model.advancedSecurityChecks, ...model.externalToolChecks]) {
|
|
203
|
+
for (const candidate of c.candidateEvidence ?? []) {
|
|
204
|
+
lines.push(` [${candidate.confidence}/${candidate.resolutionState}] ${candidate.ruleId} — ${sanitize(candidate.summary)}`);
|
|
205
|
+
lines.push(` location: ${candidate.location.path}${candidate.location.line !== undefined ? `:${candidate.location.line}` : ""}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
lines.push("");
|
|
146
209
|
// 16. Release metadata
|
|
147
210
|
lines.push(divider());
|
|
148
211
|
lines.push("RELEASE METADATA");
|
|
@@ -18,6 +18,80 @@ import { buildAndroidPlayReadinessChecklist } from "./playReadinessChecklist.js"
|
|
|
18
18
|
import { calculateAndroidVerdict } from "./androidVerdict.js";
|
|
19
19
|
import { buildAndroidDetectionCheckResult, buildAndroidManifestParsingCheckResult, buildAndroidTargetImmutabilityCheckResult, } from "./detectionAndImmutabilityCheckResults.js";
|
|
20
20
|
import { ANDROID_VALIDATION_ARTIFACT_TYPE, ANDROID_VALIDATION_SCHEMA_VERSION, } from "../validation/result.js";
|
|
21
|
+
import { auditAndroidNetworkSecurity } from "../advancedSecurity/networkSecurity/checkResult.js";
|
|
22
|
+
import { auditAndroidBackupConfiguration } from "../advancedSecurity/backupConfiguration/checkResult.js";
|
|
23
|
+
import { auditAndroidReleaseConfiguration } from "../advancedSecurity/releaseConfiguration/checkResult.js";
|
|
24
|
+
import { auditAndroidSecretCandidates } from "../advancedSecurity/secretCandidates/checkResult.js";
|
|
25
|
+
import { auditAndroidSigningConfiguration } from "../advancedSecurity/signingConfiguration/checkResult.js";
|
|
26
|
+
import { auditAndroidWebViewSecurity } from "../advancedSecurity/webview/checkResult.js";
|
|
27
|
+
import { auditAndroidFileProviders } from "../advancedSecurity/fileProvider/checkResult.js";
|
|
28
|
+
import { auditAndroidSensitiveStorage } from "../advancedSecurity/sensitiveStorage/checkResult.js";
|
|
29
|
+
import { auditAndroidSensitiveLogging } from "../advancedSecurity/sensitiveLogging/checkResult.js";
|
|
30
|
+
import { auditAndroidClipboardSecurity } from "../advancedSecurity/clipboard/checkResult.js";
|
|
31
|
+
import { auditAndroidFirebaseGoogleServices } from "../advancedSecurity/firebaseGoogle/checkResult.js";
|
|
32
|
+
import { runRequestedAndroidExternalTools } from "../advancedSecurity/externalTools/runRequestedAndroidExternalTools.js";
|
|
33
|
+
import { createRealExternalToolExecutor } from "../advancedSecurity/externalTools/runBoundedExternalTool.js";
|
|
34
|
+
import { createRealGradleCommandExecutor } from "../gradle/validate/executor.js";
|
|
35
|
+
function runIsolatedCheck(descriptor) {
|
|
36
|
+
try {
|
|
37
|
+
return descriptor.run();
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
41
|
+
return {
|
|
42
|
+
id: descriptor.id,
|
|
43
|
+
category: descriptor.category,
|
|
44
|
+
title: descriptor.title,
|
|
45
|
+
status: "error",
|
|
46
|
+
requirementLevel: "optional",
|
|
47
|
+
ran: false,
|
|
48
|
+
skipped: false,
|
|
49
|
+
evidence: [],
|
|
50
|
+
findings: [],
|
|
51
|
+
warnings: [],
|
|
52
|
+
errors: [`Advanced check raised an unexpected error: ${message}`],
|
|
53
|
+
sourcePaths: [],
|
|
54
|
+
confidence: "unknown",
|
|
55
|
+
environmentRequirements: [],
|
|
56
|
+
candidateEvidence: [],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function buildAdvancedSecurityChecks(targetRoot, detection, manifestEntries, gradleModules) {
|
|
61
|
+
// Fixed order (agents.txt Batch 8 section 9.1) — never alphabetical, never
|
|
62
|
+
// derived from filesystem/object iteration.
|
|
63
|
+
const descriptors = [
|
|
64
|
+
{ id: "android-network-security-audit", category: "android-network-security", title: "Android Network Security Config audit", run: () => auditAndroidNetworkSecurity(targetRoot, detection, manifestEntries) },
|
|
65
|
+
{ id: "android-backup-configuration-audit", category: "android-backup-configuration", title: "Android backup configuration audit", run: () => auditAndroidBackupConfiguration(targetRoot, detection, manifestEntries) },
|
|
66
|
+
{ id: "android-release-configuration-audit", category: "android-release-configuration", title: "Android release configuration audit", run: () => auditAndroidReleaseConfiguration(targetRoot, detection, manifestEntries, gradleModules) },
|
|
67
|
+
{ id: "android-secret-candidates-audit", category: "android-secret-candidates", title: "Android hardcoded secret-candidate audit", run: () => auditAndroidSecretCandidates(targetRoot, detection) },
|
|
68
|
+
{ id: "android-signing-configuration-audit", category: "android-signing-configuration", title: "Android signing configuration audit", run: () => auditAndroidSigningConfiguration(targetRoot, detection, gradleModules) },
|
|
69
|
+
{ id: "android-webview-security-audit", category: "android-webview", title: "Android WebView security audit", run: () => auditAndroidWebViewSecurity(targetRoot, detection) },
|
|
70
|
+
{ id: "android-file-provider-audit", category: "android-file-provider", title: "Android FileProvider security audit", run: () => auditAndroidFileProviders(targetRoot, detection, manifestEntries) },
|
|
71
|
+
{ id: "android-sensitive-storage-audit", category: "android-sensitive-storage", title: "Android sensitive-storage audit", run: () => auditAndroidSensitiveStorage(targetRoot, detection) },
|
|
72
|
+
{ id: "android-sensitive-logging-audit", category: "android-sensitive-logging", title: "Android sensitive-logging audit", run: () => auditAndroidSensitiveLogging(targetRoot, detection) },
|
|
73
|
+
{ id: "android-clipboard-security-audit", category: "android-clipboard", title: "Android clipboard security audit", run: () => auditAndroidClipboardSecurity(targetRoot, detection) },
|
|
74
|
+
{ id: "android-firebase-google-services-audit", category: "android-firebase-google-services", title: "Android Firebase/Google services audit", run: () => auditAndroidFirebaseGoogleServices(targetRoot, detection) },
|
|
75
|
+
];
|
|
76
|
+
return descriptors.map(runIsolatedCheck);
|
|
77
|
+
}
|
|
78
|
+
// Memoizes by Gradle operation id: the fixed allowlist (operations.ts) maps
|
|
79
|
+
// each operation id to fixed args, so within one validation run the same
|
|
80
|
+
// operation id always means the same command — caching by id is safe and
|
|
81
|
+
// prevents running `lintDebug` twice when both --android-gradle-operations
|
|
82
|
+
// lint-debug and --android-external-tools android-lint are requested
|
|
83
|
+
// together (agents.txt Batch 8 section 10.6).
|
|
84
|
+
function createDedupingGradleExecutor(base) {
|
|
85
|
+
const cache = new Map();
|
|
86
|
+
return (plan) => {
|
|
87
|
+
const cached = cache.get(plan.operationId);
|
|
88
|
+
if (cached)
|
|
89
|
+
return cached;
|
|
90
|
+
const promise = base(plan);
|
|
91
|
+
cache.set(plan.operationId, promise);
|
|
92
|
+
return promise;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
21
95
|
function readToolPackageMetadata(toolRoot) {
|
|
22
96
|
try {
|
|
23
97
|
const pkgRaw = fs.readFileSync(path.join(toolRoot, "package.json"), "utf8");
|
|
@@ -75,6 +149,11 @@ export async function validateAndroidTarget(options) {
|
|
|
75
149
|
// batch owner).
|
|
76
150
|
const detectionCheck = buildAndroidDetectionCheckResult(detection);
|
|
77
151
|
const manifestParsingCheck = buildAndroidManifestParsingCheckResult(detection, manifestEntries);
|
|
152
|
+
// Shared, deduping Gradle executor: reused for both --android-gradle-
|
|
153
|
+
// operations and (if android-lint is also externally requested) the
|
|
154
|
+
// Batch 7 Android Lint adapter, so `lintDebug` never runs twice for one
|
|
155
|
+
// validation run (agents.txt Batch 8 section 10.6).
|
|
156
|
+
const sharedGradleExecutor = createDedupingGradleExecutor(options.gradleExecutor ?? createRealGradleCommandExecutor());
|
|
78
157
|
// 15-16. Optional Gradle operations — never run unless explicitly requested.
|
|
79
158
|
const requestedOperationIds = options.requestedGradleOperationIds ?? [];
|
|
80
159
|
const gradleOperationResults = requestedOperationIds.length > 0
|
|
@@ -83,17 +162,79 @@ export async function validateAndroidTarget(options) {
|
|
|
83
162
|
targetRoot: resolvedTargetRoot,
|
|
84
163
|
detection,
|
|
85
164
|
operationIds: requestedOperationIds,
|
|
86
|
-
executor:
|
|
165
|
+
executor: sharedGradleExecutor,
|
|
87
166
|
allowWithoutTaskDiscovery: options.allowGradleWithoutTaskDiscovery,
|
|
88
167
|
platform: options.platform,
|
|
89
168
|
})
|
|
90
169
|
: [];
|
|
91
|
-
//
|
|
170
|
+
// v0.4.1 Batch 8 — eleven internal advanced Android checks, active by
|
|
171
|
+
// default (agents.txt Batch 8 section 9.1). Sequential, fixed order,
|
|
172
|
+
// isolated per-check via runIsolatedCheck. Never executes a process or
|
|
173
|
+
// touches the network.
|
|
174
|
+
const advancedSecurityChecks = buildAdvancedSecurityChecks(resolvedTargetRoot, detection, manifestEntries, gradle.modules);
|
|
175
|
+
// v0.4.1 Batch 8 — optional external-tool integration. Never executes
|
|
176
|
+
// unless requestedExternalToolIds is non-empty (same "no request, no
|
|
177
|
+
// execution" convention as Gradle operations). Wrapped in try/catch so a
|
|
178
|
+
// dispatcher-level failure (e.g. an unknown tool id slipping past CLI
|
|
179
|
+
// validation in a programmatic caller) cannot discard the checks already
|
|
180
|
+
// gathered above.
|
|
181
|
+
const requestedExternalToolIds = options.requestedExternalToolIds ?? [];
|
|
182
|
+
let externalToolChecks = [];
|
|
183
|
+
if (requestedExternalToolIds.length > 0) {
|
|
184
|
+
try {
|
|
185
|
+
externalToolChecks = await runRequestedAndroidExternalTools({
|
|
186
|
+
request: {
|
|
187
|
+
requestedTools: requestedExternalToolIds,
|
|
188
|
+
targetRoot: resolvedTargetRoot,
|
|
189
|
+
artifactRoot: options.externalToolArtifactRoot ?? path.join(options.toolRoot, "reports", "security", "external-tools"),
|
|
190
|
+
networkPolicy: options.externalNetworkPolicy ?? "deny",
|
|
191
|
+
},
|
|
192
|
+
detection,
|
|
193
|
+
executors: {
|
|
194
|
+
semgrep: options.externalToolExecutors?.semgrep ?? createRealExternalToolExecutor(),
|
|
195
|
+
osv: options.externalToolExecutors?.osv ?? createRealExternalToolExecutor(),
|
|
196
|
+
androidLint: options.externalToolExecutors?.androidLint ?? sharedGradleExecutor,
|
|
197
|
+
dependencyCheck: options.externalToolExecutors?.dependencyCheck ?? createRealExternalToolExecutor(),
|
|
198
|
+
},
|
|
199
|
+
discover: options.externalToolDiscover,
|
|
200
|
+
javaAvailable: options.javaAvailable,
|
|
201
|
+
lintTaskAvailable: options.lintTaskAvailable,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
206
|
+
externalToolChecks = [
|
|
207
|
+
{
|
|
208
|
+
id: "android-external-tools-dispatch",
|
|
209
|
+
category: "android-semgrep",
|
|
210
|
+
title: "External security-tool dispatch",
|
|
211
|
+
status: "error",
|
|
212
|
+
requirementLevel: "optional",
|
|
213
|
+
ran: false,
|
|
214
|
+
skipped: false,
|
|
215
|
+
evidence: [],
|
|
216
|
+
findings: [],
|
|
217
|
+
warnings: [],
|
|
218
|
+
errors: [`External-tool dispatch raised an unexpected error: ${message}`],
|
|
219
|
+
sourcePaths: [],
|
|
220
|
+
confidence: "unknown",
|
|
221
|
+
environmentRequirements: [],
|
|
222
|
+
candidateEvidence: [],
|
|
223
|
+
},
|
|
224
|
+
];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// 17. Target mutation evidence — captured after every process this run may
|
|
228
|
+
// have executed (Gradle operations, advanced checks, and external tools),
|
|
92
229
|
// so the static-only path also proves it made no changes.
|
|
93
230
|
const snapshotAfter = captureTargetSnapshot(resolvedTargetRoot, hasGit);
|
|
94
231
|
const targetMutation = buildTargetMutationReport(snapshotBefore, snapshotAfter);
|
|
95
232
|
const immutabilityCheck = buildAndroidTargetImmutabilityCheckResult(targetMutation);
|
|
96
|
-
// 18. Assemble checks/findings in deterministic order (agents.txt
|
|
233
|
+
// 18. Assemble checks/findings in deterministic order (agents.txt Batch 8
|
|
234
|
+
// section 11.1): the existing v0.4.0 check order is preserved exactly
|
|
235
|
+
// (ending with immutabilityCheck, as before), and the eleven advanced
|
|
236
|
+
// checks and any requested external tools are APPENDED after it — never
|
|
237
|
+
// interleaved into the existing order.
|
|
97
238
|
const checks = [
|
|
98
239
|
detectionCheck,
|
|
99
240
|
manifestParsingCheck,
|
|
@@ -104,6 +245,8 @@ export async function validateAndroidTarget(options) {
|
|
|
104
245
|
gradleMetadataCheck,
|
|
105
246
|
...gradleOperationResults,
|
|
106
247
|
immutabilityCheck,
|
|
248
|
+
...advancedSecurityChecks,
|
|
249
|
+
...externalToolChecks,
|
|
107
250
|
];
|
|
108
251
|
const findingsById = new Map();
|
|
109
252
|
for (const check of checks) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ANDROID_ADVANCED_CHECK_CATEGORIES } from "../advancedSecurity/ruleIds.js";
|
|
1
2
|
// ---------------------------------------------------------------------------
|
|
2
3
|
// v0.4.0 Batch 1 — Android check-result and skip-representation contracts.
|
|
3
4
|
//
|
|
@@ -34,6 +35,11 @@ export const ANDROID_NON_PASSING_STATUSES = [
|
|
|
34
35
|
// v0.4.0 Batch 5 — added "android-target-immutability" (additive) for the
|
|
35
36
|
// required target mutation/immutability check (agents.txt Batch 5 section
|
|
36
37
|
// 10.1).
|
|
38
|
+
// v0.4.1 Batch 1 — added the advanced-security check-category families
|
|
39
|
+
// (network/backup/release/secrets/signing/webview/file-provider/sensitive-
|
|
40
|
+
// storage/sensitive-logging/clipboard/firebase/optional-tool) required by
|
|
41
|
+
// later v0.4.1 batches. These categories are additive only: no check in this
|
|
42
|
+
// batch is registered against them yet (see advancedSecurity/ruleIds.ts).
|
|
37
43
|
export const ANDROID_CHECK_CATEGORIES = [
|
|
38
44
|
"android-detection",
|
|
39
45
|
"android-manifest",
|
|
@@ -45,6 +51,7 @@ export const ANDROID_CHECK_CATEGORIES = [
|
|
|
45
51
|
"android-release-metadata",
|
|
46
52
|
"android-play-readiness",
|
|
47
53
|
"android-target-immutability",
|
|
54
|
+
...ANDROID_ADVANCED_CHECK_CATEGORIES,
|
|
48
55
|
];
|
|
49
56
|
export function isPassingAndroidStatus(status) {
|
|
50
57
|
return status === "passed";
|
|
@@ -8,11 +8,19 @@ export function resolveNpmCommand() {
|
|
|
8
8
|
// Runs a security check command with an argument array (no shell interpolation).
|
|
9
9
|
// npm audit returns exit code 1 on vulnerabilities; that is not treated as a
|
|
10
10
|
// spawn failure here — the caller inspects the output and exit code separately.
|
|
11
|
+
//
|
|
12
|
+
// `env` is optional and additive (v0.4.1 Batch 7): omitted, it defaults to
|
|
13
|
+
// `process.env` exactly as before this option existed, so every pre-existing
|
|
14
|
+
// caller is unaffected. A caller that supplies a minimized environment (e.g.
|
|
15
|
+
// the Batch 7 external-tool adapters, which must not propagate arbitrary
|
|
16
|
+
// caller credentials to a spawned child process) gets that environment used
|
|
17
|
+
// for both PATH resolution and the actual spawn.
|
|
11
18
|
export async function runSecurityCommand(options) {
|
|
12
19
|
const started = Date.now();
|
|
20
|
+
const env = options.env ?? process.env;
|
|
13
21
|
const resolved = resolveCommand(options.command, {
|
|
14
22
|
cwd: options.cwd,
|
|
15
|
-
env
|
|
23
|
+
env,
|
|
16
24
|
allowPowerShellShim: false,
|
|
17
25
|
});
|
|
18
26
|
const command = resolved.command;
|
|
@@ -48,6 +56,7 @@ export async function runSecurityCommand(options) {
|
|
|
48
56
|
cwd: options.cwd,
|
|
49
57
|
shell: false,
|
|
50
58
|
stdio: ["ignore", "pipe", "pipe"],
|
|
59
|
+
env,
|
|
51
60
|
});
|
|
52
61
|
}
|
|
53
62
|
catch (spawnErr) {
|
|
@@ -61,6 +61,8 @@ const FLAGS_WITH_VALUE = {
|
|
|
61
61
|
"--format": "format",
|
|
62
62
|
"--fail-on": "failOn",
|
|
63
63
|
"--android-gradle-operations": "androidGradleOperations",
|
|
64
|
+
"--android-external-tools": "androidExternalTools",
|
|
65
|
+
"--android-external-network": "androidExternalNetwork",
|
|
64
66
|
};
|
|
65
67
|
// v0.4.0 Batch 5 — the exact allowlisted optional Gradle operation ids
|
|
66
68
|
// (agents.txt Batch 5 section 7.3). Deliberately re-declared here rather than
|
|
@@ -69,6 +71,14 @@ const FLAGS_WITH_VALUE = {
|
|
|
69
71
|
// mobile dependency direction for five short string literals. Both lists
|
|
70
72
|
// must stay in sync; a regression test in tests/security/ asserts they do.
|
|
71
73
|
export const ANDROID_GRADLE_OPERATION_IDS = ["wrapper-version", "tasks", "assemble-debug", "unit-test-debug", "lint-debug"];
|
|
74
|
+
// v0.4.1 Batch 8 — the exact allowlisted external-tool ids (agents.txt Batch
|
|
75
|
+
// 8 section 10.1). Deliberately re-declared here for the same reason
|
|
76
|
+
// ANDROID_GRADLE_OPERATION_IDS is re-declared above: this module stays free
|
|
77
|
+
// of a securityValidation -> mobile import for four short string literals.
|
|
78
|
+
// Both lists must stay in sync; a regression test asserts they do.
|
|
79
|
+
export const ANDROID_EXTERNAL_TOOL_CLI_IDS = ["semgrep", "osv", "android-lint", "dependency-check"];
|
|
80
|
+
export const ANDROID_EXTERNAL_NETWORK_POLICIES = ["deny", "allow-requested"];
|
|
81
|
+
export const DEFAULT_ANDROID_EXTERNAL_NETWORK_POLICY = "deny";
|
|
72
82
|
export function parseSecurityValidateArgs(argv) {
|
|
73
83
|
const result = {};
|
|
74
84
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -99,6 +109,8 @@ export function normalizeSecurityValidateConfig(raw, toolRoot) {
|
|
|
99
109
|
const failOn = parseFailOnOption(raw.failOn);
|
|
100
110
|
const out = raw.out !== undefined ? resolveOutDir(raw.out) : path.join(toolRoot, "reports", "security");
|
|
101
111
|
const androidGradleOperationIds = parseAndroidGradleOperationsOption(raw.androidGradleOperations, profile.value);
|
|
112
|
+
const androidExternalToolIds = parseAndroidExternalToolsOption(raw.androidExternalTools, profile.value);
|
|
113
|
+
const androidExternalNetworkPolicy = parseAndroidExternalNetworkOption(raw.androidExternalNetwork, profile.value);
|
|
102
114
|
return {
|
|
103
115
|
target: raw.target,
|
|
104
116
|
out,
|
|
@@ -115,6 +127,8 @@ export function normalizeSecurityValidateConfig(raw, toolRoot) {
|
|
|
115
127
|
failOnThreshold: failOn.value,
|
|
116
128
|
failOnWasDefault: failOn.wasDefault,
|
|
117
129
|
androidGradleOperationIds,
|
|
130
|
+
androidExternalToolIds,
|
|
131
|
+
androidExternalNetworkPolicy,
|
|
118
132
|
};
|
|
119
133
|
}
|
|
120
134
|
// Parses --android-gradle-operations. Absent by default (empty array — zero
|
|
@@ -134,6 +148,38 @@ function parseAndroidGradleOperationsOption(raw, profile) {
|
|
|
134
148
|
const unique = new Set(parts);
|
|
135
149
|
return ANDROID_GRADLE_OPERATION_IDS.filter((id) => unique.has(id));
|
|
136
150
|
}
|
|
151
|
+
// Parses --android-external-tools. Absent by default (empty array — zero
|
|
152
|
+
// external-tool execution). Rejects unknown tool ids and non-"android"-
|
|
153
|
+
// profile usage before any validation work begins (agents.txt Batch 8
|
|
154
|
+
// section 10.1) — same shape as parseAndroidGradleOperationsOption.
|
|
155
|
+
function parseAndroidExternalToolsOption(raw, profile) {
|
|
156
|
+
if (raw === undefined)
|
|
157
|
+
return [];
|
|
158
|
+
if (profile !== "android") {
|
|
159
|
+
throw new Error(`--android-external-tools requires --profile android (got --profile ${profile}).`);
|
|
160
|
+
}
|
|
161
|
+
const parts = splitAndNormalizeList(raw, "--android-external-tools");
|
|
162
|
+
const invalid = parts.filter((p) => !ANDROID_EXTERNAL_TOOL_CLI_IDS.includes(p));
|
|
163
|
+
if (invalid.length > 0) {
|
|
164
|
+
throw new Error(`Invalid --android-external-tools value(s): ${invalid.join(", ")}. Valid values are: ${ANDROID_EXTERNAL_TOOL_CLI_IDS.join(", ")}.`);
|
|
165
|
+
}
|
|
166
|
+
const unique = new Set(parts);
|
|
167
|
+
return ANDROID_EXTERNAL_TOOL_CLI_IDS.filter((id) => unique.has(id));
|
|
168
|
+
}
|
|
169
|
+
// Parses --android-external-network. Defaults to "deny" (agents.txt Batch 8
|
|
170
|
+
// section 10.2) — network is never authorized unless explicitly requested.
|
|
171
|
+
function parseAndroidExternalNetworkOption(raw, profile) {
|
|
172
|
+
if (raw === undefined)
|
|
173
|
+
return DEFAULT_ANDROID_EXTERNAL_NETWORK_POLICY;
|
|
174
|
+
if (profile !== "android") {
|
|
175
|
+
throw new Error(`--android-external-network requires --profile android (got --profile ${profile}).`);
|
|
176
|
+
}
|
|
177
|
+
const value = raw.trim();
|
|
178
|
+
if (!ANDROID_EXTERNAL_NETWORK_POLICIES.includes(value)) {
|
|
179
|
+
throw new Error(`Invalid --android-external-network value: "${raw}". Valid values are: ${ANDROID_EXTERNAL_NETWORK_POLICIES.join(", ")}.`);
|
|
180
|
+
}
|
|
181
|
+
return value;
|
|
182
|
+
}
|
|
137
183
|
// ---------------------------------------------------------------------------
|
|
138
184
|
// v0.2.2 Batch 5 — profile-aware default checks
|
|
139
185
|
// ---------------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<data-extraction-rules>
|
|
3
|
+
<cloud-backup disableIfNoEncryptionCapabilities="true">
|
|
4
|
+
<include domain="sharedpref" path="." />
|
|
5
|
+
<exclude domain="database" path="fake-secrets.db" />
|
|
6
|
+
</cloud-backup>
|
|
7
|
+
<device-transfer>
|
|
8
|
+
<include domain="root" path="." />
|
|
9
|
+
</device-transfer>
|
|
10
|
+
</data-extraction-rules>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<network-security-config>
|
|
3
|
+
<domain-config>
|
|
4
|
+
<domain>example.org</domain>
|
|
5
|
+
<pin-set expiration="not-a-date">
|
|
6
|
+
<pin>MISSING_DIGEST_ATTRIBUTE_FAKE_PIN=</pin>
|
|
7
|
+
<pin digest="SHA-1">UNSUPPORTED_DIGEST_FAKE_PIN=</pin>
|
|
8
|
+
<pin digest="SHA-256"></pin>
|
|
9
|
+
</pin-set>
|
|
10
|
+
</domain-config>
|
|
11
|
+
</network-security-config>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<network-security-config>
|
|
3
|
+
<base-config cleartextTrafficPermitted="true">
|
|
4
|
+
<trust-anchors>
|
|
5
|
+
<certificates src="system" />
|
|
6
|
+
<certificates src="user" />
|
|
7
|
+
</trust-anchors>
|
|
8
|
+
</base-config>
|
|
9
|
+
<domain-config cleartextTrafficPermitted="true">
|
|
10
|
+
<domain includeSubdomains="true">example.com</domain>
|
|
11
|
+
<pin-set expiration="2030-01-01">
|
|
12
|
+
<pin digest="SHA-256">FAKEPINHASHFAKEPINHASHFAKEPINHASHFAKEPINHASH=</pin>
|
|
13
|
+
</pin-set>
|
|
14
|
+
<domain-config>
|
|
15
|
+
<domain includeSubdomains="false">nested.example.com</domain>
|
|
16
|
+
<trust-anchors>
|
|
17
|
+
<certificates src="@raw/my_custom_ca" />
|
|
18
|
+
</trust-anchors>
|
|
19
|
+
</domain-config>
|
|
20
|
+
</domain-config>
|
|
21
|
+
<debug-overrides>
|
|
22
|
+
<trust-anchors>
|
|
23
|
+
<certificates src="user" overridePins="true" />
|
|
24
|
+
</trust-anchors>
|
|
25
|
+
</debug-overrides>
|
|
26
|
+
</network-security-config>
|