RKNHardering/marketplace/catalog.json
xtclovver dfb46b716c
Рефакторинг: дедупликация и декомпозиция god-классов с сохранением поведения + фикс багов/защиты у профилей (#73)
* fix: Security upgrade & bug fixes for profiles

* fix: bugs, security and warnings

* chore: README update

* test: expand vpn/ characterization coverage

Add VpnAppClassifierTest (marker priority, case sensitivity, go version
extraction) and extend VpnAppCatalogTest with catalog integrity
invariants, VpnDumpsysParserTest with parser corpus cases,
VpnApkInspectorTest with split-apk/path-edge cases,
VpnAppMetadataScannerTest with null-package/permission/format cases.
Tests pin current behavior before refactoring; no production changes.

* test: fix data race in CdnPullingCheckerTest label collection

fetchEndpoint stubs are invoked from parallel coroutines, but the tests
collected labels into a plain ArrayList, causing intermittent
ArrayIndexOutOfBoundsException. Use CopyOnWriteArrayList.

* refactor: single source of truth for interface name patterns

NetworkInterfaceNameNormalizer no longer redeclares its own base-pattern
list; it now uses NetworkInterfacePatterns.STACKED_BASE_INTERFACES, a
deliberate strict subset of STANDARD_INTERFACES (seth/dummy excluded to
keep v4- de-stacking behavior byte-identical). Pinned the divergence and
classification behavior in NetworkInterfaceNameNormalizerTest.

* refactor: extract strict IP-literal validators into probe/IpLiterals

Move the strict charset+InetAddress validators verbatim out of
CdnPullingClient; the client keeps one-line delegates so all call sites
and tests stay untouched. PublicIpClient.looksLikeIp is deliberately NOT
unified: its loose heuristic is required for mail.ru HTML extraction
(now documented). Add IpLiteralsTest pinning edge cases including the
JDK quirk where IPv4-mapped IPv6 literals parse as Inet4Address.

* refactor: deduplicate checker/ plumbing

- Share a single SignalOutcome data class instead of identical nested
  copies in DirectSignsChecker and IndirectSignsChecker.
- GeoIpChecker: extract fetchBuiltinProvider, the common
  fetch/validate/error skeleton of the five builtin providers; each
  provider keeps its own URL construction and field-path cascades.
- BypassChecker: extract startProxyScanAsync/startXrayScanAsync so
  check() is orchestration only; progress-callback wiring unchanged.

No thresholds, signals, finding order or result structures changed.
IndirectSigns/LocationSignals/CallTransport check() bodies were already
step-decomposed and are deliberately left as is.

* refactor: collapse VpnCheckRunner await/publish blocks into publishOnReady

Replace 11 identical await -> throwIfCancelled -> publish wrapper blocks
with a generic publishOnReady helper using CheckUpdate constructor
references. Same supervisorScope receiver, same null contract, same
publish order; BypassProgress, IpConsensusReady and VerdictReady paths
untouched.

* test: golden snapshots and section-parity net for export formatters

Pin the byte-exact output of all three CheckResult formatters (Markdown,
JSON, debug diagnostics) for the rich fixture, privacy mode on and off,
with UTC/Locale.US pinned and a fixed timestamp. Actual outputs are
dumped to build/export-golden-actual/ for easy regeneration after an
intentional format change. The parity test additionally asserts every
populated section appears in every formatter, guarding the three
independent traversals against silent drift.

No shared traversal/visitor is introduced on purpose: the formatters
diverge deliberately (separators, masking policy, flag emission), so
unification would change export bytes.

* test: add markdown golden files missed by the global *.md gitignore

Allow-list app/src/test/resources/export/golden/*.md so golden
regeneration keeps working.

* test: pin custom-check editor read/write semantics before decomposition

Round-trip test (non-default config in every section survives open-save
unchanged), defaults test (untouched new profile saves the exact literal
defaults from saveAndExit), and official-fork test (saving a profile
whose official flag survived the storage trust chain produces a new id,
an edited-suffix name, cleared marketplaceInfo and sourceProfileId).

* refactor: decompose custom-check editor into per-section binders

SettingsCustomCheckEditorFragment (1593 -> 422 lines) now delegates each
checker section to a SectionBinder in customcheck/ui/editor/: bind()
loads the profile slice into the body views, collect() reconstructs it
with the exact legacy defaults, summary() renders the collapsed line.
Shared plumbing extracted alongside: EndpointPills (pill rows),
InlineEndpointSection (add/edit wiring for the three
InlineEndpointEditorController-backed sections), EditorSectionBinders
(visual order). Reachability domains moved into their binder; the
generic custom-domain list, section accordion state, dependency
enforcement and the save/fork flow stay in the fragment.

Read/write semantics are pinned by SettingsCustomCheckEditorRoundTripTest
(previous commit) and were not changed. Dead makeDialogContainer/
makeDialogEdit helpers dropped.

* refactor: replace MainActivity tile id-mappers with a static tile table

ui/main/CategoryTiles declares the twelve category tiles (id, header
title/icon resources, per-tile view ids) in accordion order; MainActivity
category constants now alias the table ids and setupCategoryAccordion/
createTileHolder consume TileSpec directly. Removes the eight mechanical
when-mappers (~130 lines). Tile behavior, the tiles field and TileHolder
member names are unchanged (MainActivityUiRenderingTest reflection net
passes unmodified).

* refactor: extract scan-report export flow into MainExportController

ui/main/MainExportController owns the format/action dialogs, the two
CreateDocument launchers, clipboard copy and document writing, moved
verbatim from MainActivity. The controller is constructed as an activity
field initializer, preserving the pre-STARTED launcher registration
timing; the export snapshot and the debug-clipboard toggle stay in the
activity and are supplied via lambdas.

* fix: NoSuchElementException race in ScanCancellationSignal.cancel

callbacks.entries.toList() takes the size==1 fast path through first(),
which throws NoSuchElementException when a checker unregisters its
callback concurrently. Snapshot through the concurrent map iterator
instead; invocation-after-clear ordering and the register() double-check
contract are unchanged. Surfaced as an intermittent failure of
VpnCheckRunnerTest cancelled-execution test.

* refactor: deduplicate MainActivity loading-stage view mapping

showLoadingCardForStage and markLoadingStagesCancelled repeated the same
stage -> card/icon/status/findings/info-section bundle twice; both now
share a single categoryViewsForStage table. bindCardStoppedState mirrors
bindCardLoadingState, removing the four copies of the stopped-state
icon/status block. Per-stage behavior is unchanged.

Deliberate deviation from the original phase plan: a separate
LoadingStageCoordinator class would have needed ~10 injected callbacks
for the same effect; in-place deduplication wins on risk and size.

* fix: preserve {ip}-placeholder GeoIP provider URLs across profile round trip

UrlSanitizer.sanitizeHttpsUrl runs the URL through java.net.URI, which
throws on the illegal { } chars, so any custom GeoIP provider whose URL
embeds the documented {ip} placeholder was silently dropped on every
profile save/load. Add sanitizeGeoIpProviderUrl, which validates a copy
with {ip} replaced by a public IP literal (8.8.8.8) and returns the
original string when it passes. Wire deserializeGeoIp to it.

The {ip} substitution only exists in GeoIpChecker.fetchCustomProvider;
IpComparison/CdnPulling send URLs verbatim, so they keep the generic
sanitizer. canonicalHash semantics unchanged.

* fix: localize IP-channels markdown export header

CheckResultMarkdownExportFormatter hardcoded the Russian '## IP каналы' section header, leaking Russian into en/fa/zh exports. Use the existing R.string.ip_channels_title resource via the Context the formatter already receives. Goldens regenerated (US locale -> 'IP channels').

* fix: localize remaining Russian literals in Markdown export formatter

Replace hardcoded RU strings in CheckResultMarkdownExportFormatter with
context.getString lookups so en/fa/zh exports no longer leak Russian:
- IP-channels table column headers (Channel/Country/Sources)
- "Flags:" prefix
- operator-whitelist section (title, Detected yes/no, control suffix,
  Duration label/ms unit, available/unavailable)

Add export_* string resources to all four locales (en/ru/fa/zh-rCN) and
drop the object-level AVAILABLE/UNAVAILABLE consts. Regenerate export
golden snapshots (now render under Locale.US in English) and update the
operator-whitelist unit-test assertions accordingly.

* refactor: extract main render environment and FindingViewFactory

* refactor: extract CategoryCardRenderer from MainActivity

* refactor: extract IP-comparison and CDN-pulling renderers

* refactor: extract call-transport, bypass, IP-channels and reachability renderers

* refactor: extract VerdictRenderer and finish MainActivity render decomposition
2026-06-11 19:50:36 +03:00

76 lines
3.3 KiB
JSON

{
"schema_version": 1,
"updated_at": "2026-05-28T00:00:00Z",
"entries": [
{
"id": "default-full-scan",
"name": "Full Scan",
"description": "All checks enabled with full port range",
"author": "xtclovver",
"version": "1.0.0",
"official": true,
"verified": true,
"profile_url": "https://raw.githubusercontent.com/xtclovver/RKNHardering/main/marketplace/checks/full-scan.rkncheck",
"expected_hash": "9c52db2e71cd34b693d1a216ea07456aedd6dec56deb7152f4d5d56ba426b78f",
"tags": ["official", "complete"],
"created_at": "2026-05-20T00:00:00Z",
"updated_at": "2026-05-20T00:00:00Z"
},
{
"id": "default-quick-scan",
"name": "Quick Scan",
"description": "Fast subset — only direct/indirect signs and GeoIP",
"author": "xtclovver",
"version": "1.0.0",
"official": true,
"verified": true,
"profile_url": "https://raw.githubusercontent.com/xtclovver/RKNHardering/main/marketplace/checks/quick-scan.rkncheck",
"expected_hash": "4805913b72eefdb2b4fe3fa34dd6cf8e0a556c0b89154c1a4c421bfa51797591",
"tags": ["official", "quick"],
"created_at": "2026-05-20T00:00:00Z",
"updated_at": "2026-05-20T00:00:00Z"
},
{
"id": "stealth-check",
"name": "Stealth Check",
"description": "Local-only checks without any network requests — direct/indirect signs, native JNI, and localhost proxy scanning",
"author": "xtclovver",
"version": "1.0.0",
"official": true,
"verified": true,
"profile_url": "https://raw.githubusercontent.com/xtclovver/RKNHardering/main/marketplace/checks/stealth-check.rkncheck",
"expected_hash": "bd22df1c205fd992f51d6924a2cd24262d61c9b4a7d720b070cb9d36b1c53918",
"tags": ["official", "stealth", "offline", "no-network"],
"created_at": "2026-05-27T00:00:00Z",
"updated_at": "2026-05-27T00:00:00Z"
},
{
"id": "mtproxy-tg-stun-check",
"name": "MTProxy/TG STUN Check",
"description": "Telegram traffic analysis — STUN reflexive IP, MTProto probing via localhost proxies, underlying network leak checks",
"author": "xtclovver",
"version": "1.0.0",
"official": true,
"verified": true,
"profile_url": "https://raw.githubusercontent.com/xtclovver/RKNHardering/main/marketplace/checks/mtproxy-tg-stun-check.rkncheck",
"expected_hash": "52b54892378044a9238c2824fdc32870fd2923bb1f5bac784fd51740f91718d5",
"tags": ["official", "telegram", "mtproto", "stun"],
"created_at": "2026-05-27T00:00:00Z",
"updated_at": "2026-05-27T00:00:00Z"
},
{
"id": "extended-test",
"name": "Extended Test",
"description": "Maximum sensitivity — all checks enabled, increased ping counts, lowered thresholds, full port scan, domain reachability with blocked/reachable domains",
"author": "xtclovver",
"version": "1.0.0",
"official": true,
"verified": true,
"profile_url": "https://raw.githubusercontent.com/xtclovver/RKNHardering/main/marketplace/checks/extended-test.rkncheck",
"expected_hash": "f9fddd357e1ad2ca6a674fd4d41a471bb881894f7a7716627eee58e964383f3b",
"tags": ["official", "extended", "hardening", "maximum-sensitivity", "domain-reachability"],
"created_at": "2026-05-27T00:00:00Z",
"updated_at": "2026-05-27T00:00:00Z"
}
]
}