Commit Graph

71 Commits

Author SHA1 Message Date
fatih.bulut fbfa3aece7 feat(market-research): add play.google.com charts scraper
Replaces the AppBrain scraper (Cloudflare-blocked 403, confirmed dead).
play.google.com server-rendered chart HTML works: live-validated, real
Android packages + names + ratings from both /store/apps/top and
/store/apps/category/<CAT>. CLI: <top|category> [--category CAT]
[--region R] [--limit N] [--html-file F]; output source:"google-play",
entries {rank,name,package,rating}. Package link is the stable field;
title (Epkrse class) and rating are best-effort.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 22:42:20 +03:00
fatih.bulut c8eb023712 docs(market-research): drop AppBrain from numeric-sources list 2026-06-23 22:39:15 +03:00
fatih.bulut f1eef0560b docs(market-research): switch Play-chart source AppBrain->play.google.com
AppBrain is Cloudflare-blocked (403) for free scraping. play.google.com's
own /store/apps/top + /store/apps/category HTML works (validated live 46/46).
Rework Task 1, fetch-play-charts.py CLI/output, and all downstream prose.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 22:39:00 +03:00
fatih.bulut 4326bde33f feat(market-research): add AppBrain Play-charts scraper
NOTE: AppBrain returns a Cloudflare JS challenge (HTTP 403) for
automated requests; live fetch degrades gracefully to an error (the
skill falls back to Apple RSS + web search). Offline test passes
against synthetic fixture — that is the test gate per the brief.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 22:25:20 +03:00
fatih.bulut 753c81bff3 docs(market-research): v2 implementation plan (9 tasks, TDD)
Task-by-task plan: AppBrain Play-charts scraper, play.py resolve/count,
best-effort trends.py, numeric-sources rubric, numeric evidence-required
scoring, report w/ verified Play links + citations + saturation, 8-phase
survivor-only-enrichment SKILL flow, smoke/README/suite coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 22:04:18 +03:00
fatih.bulut ecd545bc65 docs(market-research): v2 design — multi-source, numeric scoring, Play links
Approved spec for market-research v2: AppBrain Play charts + Play
resolver/saturation scripts + best-effort Google Trends + curated numeric
sources, numeric source-cited scoring, 1-2 verified Play links per candidate,
survivor-only enrichment for efficiency.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 21:58:58 +03:00
fatih.bulut fa2d095692 docs: update CLAUDE.md for market-research plugin and 3-stage pipeline
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 02:09:19 +03:00
masa2146 0c0224d493
Merge pull request #3 from masa2146/feat/market_research_skill
Feat/market research skill
2026-06-22 01:15:43 +03:00
fatih.bulut cf96d35f1c chore(market-research): ignore __pycache__ and .pyc artifacts
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 01:06:02 +03:00
fatih.bulut 67ac1e6322 test(market-research): add run-all.sh suite aggregator 2026-06-22 01:02:03 +03:00
fatih.bulut 0da572ca1f feat(market-research): add 6-phase SKILL.md orchestrator with clone-app handoff 2026-06-22 00:59:25 +03:00
fatih.bulut c18ed6ab09 feat(market-research): add research-angles, scoring, and report rubrics 2026-06-22 00:56:42 +03:00
fatih.bulut ae840beaa3 feat(market-research): add history.py non-repeat memory with offline test 2026-06-22 00:52:45 +03:00
fatih.bulut 5beb04b43d feat(market-research): add fetch-charts.py App Store RSS fetcher with offline test 2026-06-22 00:49:57 +03:00
fatih.bulut 31502b5bbe feat(market-research): scaffold plugin, command, README, marketplace entry 2026-06-22 00:46:09 +03:00
fatih.bulut 1baf142ae5 docs(market-research): add implementation plan
Six-task TDD plan for the market-research plugin: scaffold + marketplace
entry, fetch-charts.py (App Store RSS), history.py (non-repeat memory),
reference rubrics, 6-phase SKILL.md with clone-app handoff, test aggregator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 00:33:57 +03:00
fatih.bulut f2411edfd8 docs(market-research): add market-to-build pipeline design spec
Umbrella spec for two new plugins (market-research, hermes-build) plus a
one-branch clone-app Phase 7 edit, chaining discover -> analyze -> build.
Decomposes into three implementation plans built in order.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 00:12:35 +03:00
masa2146 d9594eb320
Merge pull request #2 from masa2146/re-skill
feat(clone-app): enhance HTTP request handling and update test cases …
2026-06-21 21:12:01 +03:00
fatih.bulut 0864551bde feat(clone-app): enhance HTTP request handling and update test cases for Play Store scraping 2026-06-21 20:12:31 +03:00
fatih.bulut b34632ef3b feat(clone-app): download APKs via apkeep instead of APKCombo scraping
Replace the hand-rolled two-step APKCombo curl/grep flow with apkeep
(default source apk-pure): no auth, no JavaScript, handles XAPK split
bundling and retries itself, and the per-mirror HTML parsing is no longer
ours to maintain. The script still prints a stable app.apk/app.xapk path.

- download-apk.sh: invoke apkeep, rename artifact to app.<ext>, clear error
  when the binary is missing; CLONE_APP_APKEEP (test stub) and
  CLONE_APP_APKEEP_SOURCE (mirror override) env hooks
- test-download-apk.sh: rewritten against an apkeep stub (xapk/apk/fail/
  missing-binary cases)
- SKILL.md Phase 1: prose updated for apkeep install + source override

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 19:19:54 +03:00
masa2146 adf93bacd1
Merge pull request #1 from masa2146/feat/clone-app-re-subagent
Feat/clone app re subagent
2026-06-21 17:37:30 +03:00
fatih.bulut 5adfa2a1ae docs(clone-app): tie Phase 2 fallback guard to probe RC==0
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 17:29:52 +03:00
fatih.bulut c8e5bb08a4 test(clone-app): register re-digest-contract in smoke structure check 2026-06-21 17:24:03 +03:00
fatih.bulut 567b512e32 feat(clone-app): run RE via isolated subagent with script fallback + payloads 2026-06-21 17:19:29 +03:00
fatih.bulut b6eca91d1d test(clone-app): add RE digest contract, fixtures, structural test 2026-06-21 17:12:58 +03:00
fatih.bulut 73b3b3d8c5 docs(clone-app): implementation plan for RE-via-subagent + payloads
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 14:06:00 +03:00
fatih.bulut ed75876cc1 docs(clone-app): spec RE-via-subagent + payload extraction
Phase 2 rewrite: invoke android-reverse-engineering skill in an isolated
subagent (fallback to direct scripts), extract Tier-2 payloads for
auth/payment/core flows, persist a digest contract to $WORK/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 13:56:36 +03:00
fatih.bulut b1135b82a2 fix(clone-app): switch APK source to APKCombo and guard bash version
The APKPure direct endpoint (d.apkpure.com/b/APK/<pkg>) now returns an HTTP
403 Cloudflare bot challenge for every package, so downloads always failed —
which also blocked the entire reverse-engineering chain downstream (it never
ran because Phase 1 produced no APK).

- download-apk.sh: fetch the APKCombo download page with a browser UA, extract
  the embedded /r2?u=<signed-url> link, and download that. No JS needed; the
  URL slug is ignored so a literal 'app' works for any package. Verified
  end-to-end against com.wonder (Elevate) -> 126MB xapk -> RE fingerprint.
- test-download-apk.sh: model the two-step flow and the page-without-link case.
- resolve-re-scripts.sh: warn (stderr) when bash < 4, since the upstream RE
  scripts use ${VAR,,} and fail with 'bad substitution' on macOS bash 3.2.
- SKILL.md / README.md / CLAUDE.md: document the APKCombo source and bash 4+ req.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 04:16:25 +03:00
fatih.bulut b7b62d5f14 chore(clone-app): rename marketplace to clone-app-skill and update repo URLs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 03:48:55 +03:00
fatih.bulut e4703a28fe fix(clone-app): correct Phase 2 decompile output dir and tighten package regex 2026-06-21 02:23:34 +03:00
fatih.bulut 919a1fb6af test(clone-app): add structural smoke test 2026-06-21 02:19:07 +03:00
fatih.bulut 91abce60d4 feat(clone-app): add /clone-app command and README 2026-06-21 02:17:56 +03:00
fatih.bulut 42f5020f8b feat(clone-app): add orchestrator SKILL.md (phases 0-7) 2026-06-21 02:15:36 +03:00
fatih.bulut f88da4d2d9 docs(clone-app): add stack/effort/infra/report reference guides 2026-06-21 02:12:51 +03:00
fatih.bulut 10eb027efa test(clone-app): add aggregate test runner 2026-06-21 02:10:40 +03:00
fatih.bulut 87acf85d6f feat(clone-app): add check-appstore.py iTunes search with tests 2026-06-21 02:09:02 +03:00
fatih.bulut 4b010a665e feat(clone-app): add scrape-play-store.py with ld+json parsing and tests 2026-06-21 02:07:14 +03:00
fatih.bulut 052f0e5cad feat(clone-app): add download-apk.sh with retries and xapk/apk detection 2026-06-21 02:05:04 +03:00
fatih.bulut d2d74d02d3 feat(clone-app): add resolve-re-scripts.sh sibling-plugin locator with tests 2026-06-21 02:02:05 +03:00
fatih.bulut bc3066a890 feat(clone-app): add extract-package.sh URL parser with tests 2026-06-21 02:00:26 +03:00
fatih.bulut 9550dc0182 feat(clone-app): scaffold plugin manifest and marketplace entry 2026-06-21 01:58:03 +03:00
fatih.bulut 09dd8bb08e docs: add clone-app spec and implementation plan 2026-06-21 01:56:40 +03:00
Simone Avogadro e8dde9d058 chore: bump plugin version to 1.5.0 2026-06-10 11:49:02 +02:00
Simone Avogadro f68d9ce3be feat: post-filter --urls to drop dictionary noise while keeping IPs and apex hosts
The hardening patch widened STRICT_URL to recover IPv4 literals, apex
2-label domains and internal hosts that the PR's strict-only regex
discarded as collateral while killing Kotlin-stdlib dictionary noise.
Widening alone reopened a narrow noise class: 'word.word' fragments such
as "www.this" / "this.introduction" pass as apex domains.

Keep extraction permissive and add a small awk pass that decides per host:
- IPv4 literal: always keep (dict fragments are words, never dotted-quads)
- >=3 labels: always keep (any TLD; same tolerance as the original regex)
- any host with a :port or /path: always keep (structured = high signal)
- bare 2-label apex: keep only when the TLD is a real one, matched as a
  whole field (so "introduction" != "in" — the prefix-match bug a single
  mega-regex would have)

Trade-off documented inline: a first-party host referenced bare with an
uncommon TLD (e.g. https://foo.store with no path) is dropped; a path or
port keeps it. awk is POSIX (sub/split/~/print) — more portable than the
bash>=4 'declare -A' already used in the summary header.

Verified: dictionary noise dropped; IPs, apex, internal and subdomain
hosts kept; --all on a zero-match tree still exits 0; host list and
full-URL list stay consistent (no orphan hosts).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 11:06:30 +02:00
Simone Avogadro ed97b8508b docs: document PR #16 features in README (Kotlin name recovery, fingerprint, Ktor/Apollo/Koin)
The PR #16 additions were wired into SKILL.md and references/ but the
human-facing README was never updated. Surface them, with prominent
emphasis on first-class Kotlin support:

- Top blurb: callout for R8 Kotlin name recovery + Ktor/Apollo/Koin
- "What it does" table: Phase 0 fingerprint, Kotlin name recovery,
  modern Kotlin/KMP stacks (Ktor, Apollo, Koin, HMAC)
- Usage: fingerprint.sh example, --ktor/--apollo/--paths flags, and a
  dedicated "Kotlin name recovery (R8 deobfuscation)" subsection
- Repository Structure: add the three new scripts + two new references
- Acknowledgments: credit @tajchert (#16)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 10:33:53 +02:00
Simone Avogadro 2047f99d01 fix: harden find-api-calls.sh and recover-kotlin-names.sh from PR #16 review
- find-api-calls.sh: add missing '|| true' on the --paths inventory and
  --urls extraction pipelines; with set -euo pipefail a zero-match grep
  aborted the whole script (including the default --all run) with exit 1.
- find-api-calls.sh: widen STRICT_URL to also match IPv4 literals, apex
  2-label domains and bare single-label hosts followed by :port or /path
  (localhost, internal backends) while still rejecting dictionary-fragment
  noise from the Kotlin stdlib.
- recover-kotlin-names.sh: sanitize the by_package/ filename with
  os.path.basename; a crafted absolute path in untrusted @DebugMetadata
  package names could otherwise escape the output directory.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 10:22:16 +02:00
Michał Tajchert a2a0a97f23 docs: call out BuildConfig.java and adopt a two-tier endpoint doc template
Two small changes that together meaningfully reduce wasted effort:

1. Phase 3 now explicitly tells the agent to read every BuildConfig.java.
   These files are almost never obfuscated and routinely contain the
   single highest-signal constants in the APK — base URLs, flavor names,
   build types, third-party API keys, feature flags. They were not
   mentioned in the previous workflow despite being the cheapest possible
   high-value target. One grep, finds them all.

2. The Phase 5 documentation template was a single per-endpoint block
   asking for path params, query params, request body, response type,
   and call chain. On apps with 100+ endpoints that easily becomes hours
   of work for output the consumer will not read.

   Replace it with two tiers:

     * Tier 1 — flat table covering every endpoint (host, method, path,
       auth required, source file). Always produced. Takes ~5 minutes
       from the --paths output.

     * Tier 2 — the existing detailed block, but explicitly reserved for
       high-value endpoints: the entire auth flow, payment/checkout, and
       anything the user specifically asked about. Default cap of ~10
       Tier-2 entries unless asked for more.

   This matches the natural shape of how analysts actually use this work
   (one inventory table to know the surface area, plus a deep dive on
   auth and a couple of flows) and prevents over-investment in detail
   for endpoints nobody will read about.
2026-04-29 01:40:50 +02:00
Michał Tajchert 627889a4c6 feat: add summary header to find-api-calls.sh
Without an overview the script dumps thousands of file:line: matches
across many sections, leaving the reader to figure out which framework
even applies. A short summary at the top makes the rest of the output
actionable.

The summary counts hits per framework / DI / auth-signal category in a
single grep pass over the source tree (8 separate greps would have
roughly octupled the runtime on a large decompile). Output is a 3-line
table:

  HTTP framework:   Retrofit=N OkHttp=N Ktor=N Apollo=N Volley=N
  DI framework:     Hilt/Dagger=N Koin=N
  Auth signals:     Bearer=N HMAC/Sign=N

A reader can immediately see which framework the app actually uses,
whether auth is bearer-token or signed, and whether to spend time on a
section or skip it. The summary is suppressed when a single section flag
(--retrofit, --ktor, --paths, ...) is given, so the existing single-section
workflows are unchanged.

A reminder of the available section flags is printed below the counts
so the agent does not have to consult --help.
2026-04-29 01:39:55 +02:00
Michał Tajchert ec2b14c171 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.
2026-04-29 01:26:40 +02:00
Michał Tajchert 2e6fc63453 feat: bucketed --urls output with strict regex and third-party denylist
The previous --urls mode was a plain grep for "https?://..." which on a
real APK produced thousands of lines, half of them junk strings extracted
from Kotlin stdlib's compression dictionary ("http://An Introduction to..."
fragments) and the other half SDK URLs (Google, Firebase, AppsFlyer,
Datadog, Sentry, ...) that the analyst is not looking for. The signal —
first-party backend hosts — was buried.

Two changes:

1. Strict URL regex: hostname must have at least one dot and end in a 2+
   letter TLD, with no whitespace / angle brackets / non-printables in the
   path. This eliminates the dictionary-fragment noise.

2. Bucket the surviving URLs into "likely first-party" vs "third-party"
   using references/third_party_hosts.txt — a curated denylist of
   ~80 patterns covering Google/Firebase/Apple/Microsoft/Adobe, attribution
   and observability vendors (AppsFlyer, Datadog, Sentry, Bugsnag, ...),
   payments (Stripe, PayU, Adyen, ...), support/chat SDKs, CAs, and
   standards namespaces (w3.org, etc.).

The new output starts with a frequency-sorted list of likely first-party
hosts — which is the artifact every reverse-engineer wants on the first
page — followed by the collapsed third-party list and the full URL set
for first-party hosts only.

The denylist is a sidecar text file (one regex per line) so users can
extend or override it without editing the script.
2026-04-29 01:23:56 +02:00