feat: detect Koin DI and HMAC request-signing schemes
Two gaps in the previous coverage:
1. Koin was not mentioned anywhere — Hilt/Dagger got a full section in
call-flow-analysis.md but Koin (the dominant DI in KMP and a large
share of Kotlin-only Android apps) had zero patterns. Add a Koin
subsection with the runtime-DSL patterns (module {}, single<>,
factory<>, viewModel<>, by inject, by viewModel) plus the practical
trick for resolving an interface to its impl after R8 obfuscation:
intersect "files that import org.koin.core.module" with "files that
reference the interface name".
2. The --auth mode caught Bearer / API-key / OAuth header patterns but
missed HMAC and other request-signing schemes. A hardcoded HMAC
secret embedded in an APK is a security finding worth surfacing —
the same kind of authority the user gets is the same authority a
decompiler grants to anyone. Add patterns for:
* JCA primitives: HmacSHA{1,256,512}, Mac.getInstance(...),
SecretKeySpec(...), Signature.getInstance(...)
* Header conventions: X-Signature, X-Hmac, X-Amz-Signature,
X-Client-Authorization, AWS4-HMAC, signRequest(), signaturev2/3
* Likely secret-bearing identifiers: app_secret, client_secret,
signing_key, hmac_secret, consumer_secret, private_key
* Ktor BearerTokens / loadTokens / refreshTokens DSL
These survive R8 because the JCA and Ktor APIs are public and not
shrunk. On a real-world app with a homegrown HMAC scheme they pinpoint
the signing class and its hardcoded key directly.
This commit is contained in:
parent
2e6fc63453
commit
ec2b14c171
|
|
@ -84,9 +84,9 @@ Look for:
|
||||||
- Firebase/analytics initialization
|
- Firebase/analytics initialization
|
||||||
- Base URL configuration
|
- Base URL configuration
|
||||||
|
|
||||||
## 5. Dependency Injection (Dagger / Hilt)
|
## 5. Dependency Injection
|
||||||
|
|
||||||
Modern Android apps use DI. Trace bindings to find implementations:
|
### Dagger / Hilt
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Hilt modules
|
# Hilt modules
|
||||||
|
|
@ -102,10 +102,43 @@ grep -rn '@Component\|@Subcomponent' sources/
|
||||||
grep -rn '@Inject' sources/
|
grep -rn '@Inject' sources/
|
||||||
```
|
```
|
||||||
|
|
||||||
To trace a call flow through DI:
|
### Koin
|
||||||
1. Find where an interface is used (e.g., `ApiService` injected into a repository)
|
|
||||||
2. Find the `@Provides` or `@Binds` method that creates the implementation
|
Koin is the dominant DI framework in Kotlin Multiplatform and a large
|
||||||
3. Follow the implementation to the actual HTTP call
|
share of Kotlin-only Android apps. It uses a runtime DSL rather than
|
||||||
|
compile-time generated factories, so the search patterns are different:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Confirm Koin is actually wired up
|
||||||
|
grep -rn 'org\.koin\.' sources/
|
||||||
|
|
||||||
|
# DI module declarations
|
||||||
|
grep -rn 'fun [A-Za-z]\+Module\|module\s*{\|module(' sources/
|
||||||
|
|
||||||
|
# Bindings inside a module DSL
|
||||||
|
grep -rn 'single\s*[<{(]\|factory\s*[<{(]\|viewModel\s*[<{(]\|scoped\s*[<{(]\|singleOf\|factoryOf' sources/
|
||||||
|
|
||||||
|
# Resolution call-sites (where a binding is consumed)
|
||||||
|
grep -rn '\bget\s*<\|\binject\s*<\|by\s\+inject\b\|by\s\+viewModel\b\|getKoin' sources/
|
||||||
|
```
|
||||||
|
|
||||||
|
After R8, every binding lambda becomes an anonymous
|
||||||
|
`Function2<Scope, ParametersHolder, T>` impl. To find the binding for an
|
||||||
|
interface `Foo`, look for files that contain both a Koin import / module
|
||||||
|
DSL marker and a reference to `Foo`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -rln 'org\.koin\.core\.module' sources/ | xargs grep -l 'Foo'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Trace through DI
|
||||||
|
|
||||||
|
1. Find where an interface is used (e.g. `ApiService` injected into a
|
||||||
|
repository).
|
||||||
|
2. Find the `@Provides` / `@Binds` method (Hilt) **or** the
|
||||||
|
`single { ... }` / `factory { ... }` block (Koin) that creates the
|
||||||
|
implementation.
|
||||||
|
3. Follow the implementation to the actual HTTP call.
|
||||||
|
|
||||||
## 6. Find Constants and Configuration
|
## 6. Find Constants and Configuration
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -226,9 +226,27 @@ fi
|
||||||
# --- Auth patterns ---
|
# --- Auth patterns ---
|
||||||
if [[ "$SEARCH_ALL" == true || "$SEARCH_AUTH" == true ]]; then
|
if [[ "$SEARCH_ALL" == true || "$SEARCH_AUTH" == true ]]; then
|
||||||
section "Authentication & API Keys"
|
section "Authentication & API Keys"
|
||||||
run_grep -i '(api[_-]?key|auth[_-]?token|bearer|authorization|x-api-key|client[_-]?secret|access[_-]?token)'
|
run_grep -i '(api[_-]?key|auth[_-]?token|bearer|authorization|x-api-key|client[_-]?secret|access[_-]?token|refresh[_-]?token)'
|
||||||
|
|
||||||
|
# Request-signing schemes: a hardcoded HMAC / RSA secret in an APK is a
|
||||||
|
# security finding worth surfacing prominently. These patterns catch the
|
||||||
|
# common shapes of homegrown / SDK-issued request signers.
|
||||||
|
section "Request Signing (HMAC / signature schemes)"
|
||||||
|
run_grep '(HmacSHA(1|256|512)|Mac\.getInstance\("Hmac|SecretKeySpec\(|Signature\.getInstance\()'
|
||||||
|
run_grep -i '(x-signature|x-client-authorization|x-amz-signature|x-hmac|aws4-hmac|signRequest|signatureFor|computeSignature|signaturev[0-9])'
|
||||||
|
|
||||||
|
# Hardcoded high-entropy strings adjacent to "secret"/"key" assignments
|
||||||
|
# are the canonical leaked-credential pattern.
|
||||||
|
section "Possible Hardcoded Secrets / Keys"
|
||||||
|
run_grep -i '(app[_-]?secret|client[_-]?secret|signing[_-]?key|hmac[_-]?secret|consumer[_-]?secret|private[_-]?key)'
|
||||||
|
|
||||||
section "Base URLs and Constants"
|
section "Base URLs and Constants"
|
||||||
run_grep -i '(BASE_URL|API_URL|SERVER_URL|ENDPOINT|API_BASE|HOST_NAME)'
|
run_grep -i '(BASE_URL|API_URL|SERVER_URL|ENDPOINT|API_BASE|HOST_NAME)'
|
||||||
|
|
||||||
|
# Ktor BearerTokens / refresh DSL — common on Kotlin apps and lives on
|
||||||
|
# Ktor's public API, so it survives R8 unchanged.
|
||||||
|
section "Ktor Auth (Bearer + Refresh)"
|
||||||
|
run_grep '(BearerTokens|loadTokens\s*\{|refreshTokens\s*\{|\bbearer\s*\{)'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue