Skip to content

Commit fe9d0de

Browse files
committed
feat(frontend): refine Findings Explorer UI and update example report
1 parent 5bf90bc commit fe9d0de

2 files changed

Lines changed: 111 additions & 20 deletions

File tree

apps/frontend/src/app/globals.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,18 @@ body {
758758
margin-top: 4px;
759759
}
760760

761+
.tree-finding-evidence {
762+
margin: 4px 0;
763+
padding: 8px;
764+
background: rgba(15, 24, 46, 0.04);
765+
border-radius: 8px;
766+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
767+
font-size: 11px;
768+
color: rgba(11, 11, 22, 0.6);
769+
white-space: pre-wrap;
770+
overflow-x: auto;
771+
}
772+
761773
@media (max-width: 720px) {
762774
.nav {
763775
flex-direction: column;

apps/frontend/src/app/page.tsx

Lines changed: 99 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,45 +21,115 @@ export default function Home() {
2121

2222
const examplePayload = {
2323
report: {
24-
ruleset_version: "0.8.1",
25-
generated_at_unix: 0,
26-
total_duration_ms: 842,
24+
ruleset_version: "0.8.2",
25+
generated_at_unix: 1710604800,
26+
total_duration_ms: 1240,
2727
cache_stats: {
28-
nested_bundles: { hits: 1, misses: 0 },
29-
usage_scan: { hits: 2, misses: 0 },
28+
nested_bundles: { hits: 2, misses: 1 },
29+
usage_scan: { hits: 0, misses: 4 },
3030
private_api_scan: { hits: 0, misses: 1 },
3131
},
3232
results: [
33+
{
34+
rule_id: "RULE_XCODE_26_MANDATE",
35+
rule_name: "Xcode 26 / iOS 26 SDK Mandate",
36+
category: "Compliance",
37+
severity: "Error",
38+
status: "Fail",
39+
message: "App was built with Xcode 15.4 (15F31d) and iOS 17.5 SDK",
40+
recommendation: "From April 2026, all apps must be built with Xcode 26 and the iOS 26 SDK.",
41+
duration_ms: 5,
42+
},
3343
{
3444
rule_id: "RULE_PRIVACY_MANIFEST",
3545
rule_name: "Missing Privacy Manifest",
3646
category: "Privacy",
3747
severity: "Error",
3848
status: "Fail",
39-
message: "Missing PrivacyInfo.xcprivacy",
40-
recommendation: "Add a PrivacyInfo.xcprivacy manifest to the bundle.",
41-
duration_ms: 12,
49+
message: "PrivacyInfo.xcprivacy was not found in the main bundle",
50+
recommendation: "Add a PrivacyInfo.xcprivacy file to your app bundle to declare data collection and and accessed APIs.",
51+
duration_ms: 8,
4252
},
4353
{
44-
rule_id: "RULE_USAGE_DESCRIPTIONS",
45-
rule_name: "Missing Usage Description Keys",
54+
rule_id: "RULE_PRIVACY_SDK_CROSSCHECK",
55+
rule_name: "Privacy Manifest vs SDK Usage",
4656
category: "Privacy",
47-
severity: "Warning",
57+
severity: "Error",
58+
status: "Fail",
59+
message: "Detected GoogleAnalytics and FirebaseSDK but they are not declared in the manifest.",
60+
recommendation: "Ensure PrivacyInfo.xcprivacy declares data collection and accessed APIs for all included third-party SDKs.",
61+
duration_ms: 450,
62+
},
63+
{
64+
rule_id: "RULE_ENTITLEMENTS_MISMATCH",
65+
rule_name: "Debug Entitlements Present",
66+
category: "Entitlements",
67+
severity: "Error",
68+
status: "Fail",
69+
message: "Found get-task-allow=true in app entitlements",
70+
recommendation: "Remove the get-task-allow entitlement for App Store production builds.",
71+
duration_ms: 15,
72+
},
73+
{
74+
rule_id: "RULE_BUNDLE_RESOURCE_LEAKAGE",
75+
rule_name: "Sensitive Files in Bundle",
76+
category: "Bundle",
77+
severity: "Error",
78+
status: "Fail",
79+
message: "Found .env and development.p12 inside the app bundle",
80+
recommendation: "Remove certificates, provisioning profiles, or secret files from the app bundle before submission.",
81+
duration_ms: 25,
82+
},
83+
{
84+
rule_id: "RULE_CAMERA_USAGE",
85+
rule_name: "Missing Camera Usage Description",
86+
category: "Privacy",
87+
severity: "Error",
4888
status: "Fail",
49-
message: "Missing required usage description keys",
50-
recommendation: "Add NS*UsageDescription keys to Info.plist.",
51-
duration_ms: 9,
89+
message: "NSCameraUsageDescription is missing from Info.plist",
90+
recommendation: "Add NSCameraUsageDescription with a clear, user-facing reason why your app needs camera access.",
91+
duration_ms: 4,
5292
},
5393
{
54-
rule_id: "RULE_ATS_GRANULARITY",
55-
rule_name: "ATS Exceptions Too Broad",
94+
rule_id: "RULE_ATS_AUDIT",
95+
rule_name: "ATS Exceptions Detected",
5696
category: "Ats",
5797
severity: "Warning",
5898
status: "Fail",
59-
message: "AllowsArbitraryLoads is enabled",
60-
recommendation: "Scope ATS exceptions to specific domains.",
61-
duration_ms: 8,
99+
message: "NSAllowsArbitraryLoads is enabled globally",
100+
recommendation: "Remove global ATS exceptions or scope them to specific domains with strong justification.",
101+
duration_ms: 12,
102+
},
103+
{
104+
rule_id: "RULE_LSAPPLICATIONQUERIES_SCHEMES_AUDIT",
105+
rule_name: "LSApplicationQueriesSchemes Audit",
106+
category: "Metadata",
107+
severity: "Warning",
108+
status: "Fail",
109+
message: "Found 5+ potentially generic or private schemes in allowlist",
110+
recommendation: "Keep LSApplicationQueriesSchemes minimal and aligned with actual app handoff requirements.",
111+
duration_ms: 10,
62112
},
113+
{
114+
rule_id: "RULE_EXPORT_COMPLIANCE",
115+
rule_name: "Export Compliance Declaration",
116+
category: "Metadata",
117+
severity: "Warning",
118+
status: "Fail",
119+
message: "ITSAppUsesNonExemptEncryption is not set",
120+
recommendation: "Explicitly set ITSAppUsesNonExemptEncryption in Info.plist to avoid App Store Connect prompts.",
121+
duration_ms: 5,
122+
},
123+
{
124+
rule_id: "RULE_PRIVATE_API",
125+
rule_name: "Private API Usage Detected",
126+
category: "Private API",
127+
severity: "Warning",
128+
status: "Fail",
129+
message: "Potential usage of _GSSystemAdditions detected in binary",
130+
recommendation: "Remove private API usage or replace with public alternatives to avoid rejection.",
131+
duration_ms: 600,
132+
}
63133
],
64134
},
65135
};
@@ -464,11 +534,20 @@ export default function Home() {
464534
</div>
465535
<div className="tree-finding-meta">
466536
<span>Rule: {item.rule_id}</span>
467-
{item.duration_ms && <span>{item.duration_ms}ms</span>}
537+
{typeof item.duration_ms === "number" && (
538+
<span>{item.duration_ms}ms</span>
539+
)}
468540
</div>
469541
<div className="tree-finding-desc">
470542
{item.message}
471543
</div>
544+
{item.evidence && (
545+
<pre className="tree-finding-evidence">
546+
{typeof item.evidence === "string"
547+
? item.evidence
548+
: JSON.stringify(item.evidence, null, 2)}
549+
</pre>
550+
)}
472551
{item.recommendation && (
473552
<div className="tree-finding-rec">
474553
{item.recommendation}

0 commit comments

Comments
 (0)