Native iOS app for A-List (alist.ae) — a UAE creator/influencer marketplace where verified influencers browse and redeem dining offers, submit social-media proof, and track redemptions.
ae.alist.ee (Apple team ASR5MYB7XL).gitlab.com/qubicle.me/alist-ios), so PR review history is lost.AuthKey_V63PAFH55F.p8) is committed at the repo root and git-tracked since 2022. Two Bitbucket app passwords are in README and AppDelegate.swift. Anyone with read access can push notifications to every A-List user and access the Bitbucket workspace and ~12 sibling repos.NSAllowsArbitraryLoads=true with no pinning. Any rogue Wi-Fi can MitM the API — the enabling primitive for the takeover chain.| Category | Technology | Version | Notes |
|---|---|---|---|
| Language | Swift | 5.0 | ~294 .swift files, no Obj-C |
| iOS target | iOS Deployment | 14.0 (Podfile) / 15.0 (proj) | Mismatch — Podfile post-install pins Pods to 14.0 |
| UI | UIKit + Storyboards | — | 6 storyboards (Main/Login/Home/PopUp/Venue/Profile), no SwiftUI |
| Dep manager | CocoaPods | 1.16.2 | Pods/ directory is committed (325 MB total) |
| Networking | Alamofire | 5.6.4 | No certificate pinning |
| Analytics/Crash | Firebase Analytics + Crashlytics + Messaging | 10.4.0 | Project alist-application |
| Auth | GoogleSignIn (+ Sign in with Apple via entitlement) | 7.1.0 | OTP-based email/phone login also used |
| Support | Intercom iOS SDK | 14.0.6 | App id uicijnyh, key hardcoded in SceneDelegate.swift:22 |
| Image loading | Kingfisher + SDWebImage | 7.7.0 / 5.15.0 | Both shipped — duplicated functionality, bloat |
| Charts | Charts (Daniel Gindi) | 4.1.0 | Used on History/Insights screen |
| Forced updates | Siren (custom fork) | commit e52f7ef1 | Fork: qubicle-aswinrnath/Siren-Alist.git |
| Persistence | Core Data + UserDefaults + Keychain | — | Model A-List.xcdatamodeld; auth token mirrored to Keychain keyed by user email |
| Misc UI Pods | SVPinView, FSCalendar, Cosmos 23, DropDown, BSImagePicker, SKPhotoBrowser, BottomPopup, InkPageIndicator, PhoneNumberKit 3.7, ImageCropper, ParallaxHeader, SwiftMessages 10, SkeletonView | — | 27 Pods total — heavy dependency surface |
| CI/CD | None | — | No fastlane, no bitbucket-pipelines.yml, no GitHub Actions |
| Tests | XCTest stubs only | — | Both test targets are empty boilerplate |
Classic storyboard-driven MVC with thin "ViewModel" helpers (7 small VMs in ViewModels/). All API traffic funnels through one APIManager using Alamofire; endpoint catalogue lives in API enum in APIConstants.swift. Global state is in Session (UserDefaults-backed) and AppInfo singletons; the auth token is also mirrored into Keychain by user email. Multi-environment config (Development / Production) is driven by .xcconfig files that inject $(APP_BASE_URL) into Info.plist at build time.
Static security review of the a-list-ios source tree, git history, build configuration and dependency manifests, performed by an A-List consulting security engineer. Each candidate finding was de-duplicated and re-verified against the on-disk code (file:line evidence preserved), rated with CVSS 3.1 and mapped to OWASP/CWE, then classified by exploitation confidence (confirmed / likely / theoretical). Backend-only assertions are flagged as latent design weaknesses where server behaviour could not be observed from the client.
conducting-mobile-app-penetration-test
exploiting-insecure-data-storage-in-mobile
intercepting-mobile-traffic-with-burpsuite
performing-mobile-app-certificate-pinning-bypass
testing-mobile-api-authentication
testing-api-authentication-weaknesses
conducting-api-security-testing
detecting-api-enumeration-attacks
performing-ssl-tls-security-assessment
implementing-secret-scanning-with-gitleaks
detecting-aws-credential-exposure-with-trufflehog
performing-sca-dependency-scanning-with-snyk
analyzing-sbom-for-supply-chain-vulnerabilities
hunting-for-supply-chain-compromise
detecting-supply-chain-attacks-in-ci-cd
implementing-secrets-scanning-in-ci-cd
The A-List iOS app exposes multiple production credentials directly in the git-tracked repository: an Apple APNS token-signing private key (team-wide, since 2022) and two Bitbucket app passwords, with the README conveniently publishing the matching Key Id/Team Id — any repo reader can push spoofed notifications to all users and pivot into the Bitbucket workspace and other A-List repos. The authentication design is fundamentally broken: the login OTP is echoed in the API response body (and harvested/auto-filled client-side), so it provides zero second-factor protection, and this is made trivially observable because App Transport Security is globally disabled with no certificate pinning — a single rogue-Wi-Fi position yields the Bearer token, the OTP, and full account takeover. Stolen tokens are non-expiring and have no server-side revocation, and the token is additionally persisted in cleartext UserDefaults and leaked to device logs via debugPrint in Release. A single-developer personal fork of Siren pinned to a mutable commit runs forced-update logic at every launch, creating a launch-time supply-chain backdoor that no CI/secret-scanning/SCA control would catch. Overall risk is Critical: confirmed credential compromise plus a practical, low-skill account-takeover chain affecting the entire creator base.
| Category | Status | Note |
|---|---|---|
| Mobile M1: Improper Credential Usage | vuln | APNS .p8, Bitbucket app passwords, Intercom key hardcoded/committed (ALI-01/02/16). |
| Mobile M2: Inadequate Supply Chain Security | vuln | Personal Siren fork on mutable commit, vendored QRCode, no CI/SCA (ALI-09/13/14/15). |
| Mobile M3: Insecure Authentication/Authorization | vuln | OTP echoed+auto-filled, no token expiry/revocation, param-driven resetPin auth (ALI-03/07/10). |
| Mobile M4: Insufficient Input/Output Validation | vuln | WebView forwards unvalidated server-supplied URLs/any scheme to UIApplication.open (ALI-08). |
| Mobile M5: Insecure Communication | vuln | ATS fully disabled + no certificate/public-key pinning on all API traffic (ALI-04). |
| Mobile M6: Inadequate Privacy Controls | partial | Sensitive PINs/voucher codes to system pasteboard; PII trove persisted in plaintext plist (ALI-12/06). |
| Mobile M7: Insufficient Binary Protections | n/a | No anti-tamper/obfuscation/jailbreak detection observed, but not in scope of cited findings; not separately assessed. |
| Mobile M8: Security Misconfiguration | vuln | ATS off, Firebase plist committed, dormant applinks/alist:// deep-link surface, dev artifacts in builds (ALI-04/17/18). |
| Mobile M9: Insecure Data Storage | vuln | Token+PIN+PII in UserDefaults plist, Keychain without ThisDeviceOnly, token in device logs (ALI-05/06). |
| Mobile M10: Insufficient Cryptography | clean | No custom/weak crypto primitives observed in cited code; transport weakness tracked under M5. |
| API2:2023 Broken Authentication | vuln | OTP-in-response ATO, no token revocation, param-driven auth header (ALI-03/07/10). |
| API3:2023 Broken Object/Property Level Auth | partial | Pre-OTP account-status/email enumeration via response discrepancy (ALI-11). |
| API others (Mobile-backed) | partial | Findings are client-side observations; full API authz testing requires backend access (out of scope for static iOS review). |
The Apple Push Notification service token-signing private key (.p8) is committed in cleartext at the repository root and is git-tracked (not gitignored), present since 2022. A .p8 APNS auth key is a team-wide token-signing key: combined with the Key Id (V63PAFH55F) and Team Id (ASR5MYB7XL) — both helpfully published in the adjacent README — anyone holding it can sign APNS JWT provider tokens (ES256) for every app under the team. It is a server-side credential that has no business living in a client app repo.
Any party with read access to the Bitbucket repo, any clone/branch, or the prior GitLab mirror can forge APNS provider tokens and push arbitrary notifications to ALL A-List users and to any other app under Team ASR5MYB7XL. Enables mass phishing/smishing ('Tap to verify your account' deep-linking into alist://), notification spoofing, and reputational damage. The key never expires; it must be treated as compromised.
git-filter-repo / BFG) on Bitbucket and the GitLab mirror, coordinate a force-push. 3) Add *.p8 and AuthKey_* to .gitignore. 4) Remove the Key Id/Team Id disclosure from README. 5) Add gitleaks/trufflehog pre-commit + CI scanning.Confirmed on disk and in git. CVSS raised to S:C (team-wide blast radius beyond this app); C set to L because the key's primary power is forging/sending (Integrity), not reading user content. Merged the duplicate mobile_storage and supply_chain raw findings. CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:H/A:N.
Two Bitbucket app passwords are hardcoded in the repository — in README and again as comments at the top of AppDelegate.swift (which ships in source archives). Bitbucket app passwords are user-scoped credentials granting API/git access to every repository the owning account can reach across the ParhamandCo (and historically Qubicle) workspace. The credential protecting the source tree is stored inside the source tree.
Anyone with repo read access (or a historical clone/mirror, or unpacked dSYM/source archive) can authenticate to Bitbucket as the credential owner: source theft, malicious commits poisoning the build pipeline of the iOS app and ~12 other A-List repos, and lateral movement across the workspace. Scope=Changed: access extends to systems beyond this app.
gitleaks rules for Bitbucket app-password patterns and enforce pre-commit + CI gates.Confirmed in both files. Merged the duplicate mobile_storage and supply_chain raw findings. CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N.
The backend returns the one-time login/signup passcode inside the response JSON, and APIManager.validateResponse unconditionally harvests it into the Common.shared.otp singleton on EVERY response (regardless of build flag). The OTP screens auto-populate it when CAN_AUTO_FILL_OTP is true (Dev/pre-prod) and under #if DEBUG. An OTP delivered in the same channel as the request is not a second factor — anyone who can observe the login response obtains the code directly. Combined with ATS-disabled + no pinning (ALI-04), the response is trivially observable on a hostile network. Verification requires only an enumerable user_id plus the otp.
Authentication bypass / account takeover. A network-position attacker who triggers or observes a victim's login response reads the OTP off the wire and completes login as the victim against api.alist.ae. Affects all creator accounts. The OTP factor provides zero added security because it round-trips in the response body. The cached OTP also leaks via the response-logging path.
responseJSON["otp"] harvesting (APIManager.swift:114-117) and all Common.shared.otp auto-fill paths from shipped code (not just build-flag gated). Set CAN_AUTO_FILL_OTP=false in every shipped config and strip the #if DEBUG autofill. Bind OTP verification to a server-issued non-enumerable challenge/session id rather than user_id; rate-limit and expire OTPs. Pair with re-enabled ATS + pinning.Confirmed in all cited files. Merged the mobile_storage P2 and mobile_api_auth P0 raw findings. AC:H reflects the realistic prerequisite of observing the response (network position); the in-channel echo itself is the core defect. Kept P0 due to full ATO with no inbox access. CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N.
ATS is globally disabled with no compensating exception scoping, and the single networking choke-point uses Alamofire's default Session with no ServerTrustManager / pinning and no URLSession challenge handler. Together these remove every OS-level transport guarantee: iOS will permit plain-HTTP (e.g. an attacker-injected redirect to http://), TLS downgrade to 1.0/1.1 or non-FS ciphers, and acceptance of any certificate chained to a trusted root — including an attacker CA installed on a target device. This matches the standard Burp mobile-interception workflow succeeding with no pinning bypass needed.
On a hostile network (rogue Wi-Fi, malicious proxy, ARP/DNS spoof, attacker CA on a managed/jailbroken device) an attacker transparently MITMs all API traffic with no client-side error, capturing the Bearer session token (full account takeover), creator PII and offer/redemption data, and tampering with responses. This is the enabling primitive for the OTP-in-response ATO (ALI-03) and the WebView open-redirect (ALI-08).
NSAllowsArbitraryLoads so default ATS (TLS 1.2+, forward secrecy, valid chain, no cleartext) is enforced; if a single host genuinely needs an exception, scope it narrowly under NSExceptionDomains with NSExceptionMinimumTLSVersion=TLSv1.2 and NSExceptionAllowsInsecureHTTPLoads=false. Configure a dedicated Alamofire Session with a ServerTrustManager pinning the public keys of api.alist.ae and dev-api.alist.ae (PublicKeysTrustEvaluator, with a backup pin) and inject it into APIManager instead of Session.default. Add CI verification that pinning is active.Confirmed: Info.plist, APIManager Session.default, and zero pinning code. Merged three overlapping raw findings (mobile_transport ATS P1, mobile_transport pinning P1, mobile_api_auth ATS+pinning P1) into one combined transport finding. AV:A/AC:H reflects adjacent-network position requirement. CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N.
On login the full UserModel — including the API bearer token, PIN/confirmPin, email, phone, DOB, nationality and social handles — is encoded via PropertyListEncoder into UserDefaults under key 'User'. The line that would null the token before persistence is disabled. UserDefaults is an unencrypted plist in the app sandbox, captured in unencrypted iTunes/iCloud backups. The request layer reads the token from this UserDefaults copy, not the Keychain. The parallel Keychain entry sets no kSecAttrAccessible, so it too is backup/migration-eligible and keyed by the user's mutable email.
An attacker with local access (jailbreak, lost/unlocked device, forensic extraction of an unencrypted iTunes backup) reads the live bearer token from the plaintext plist and fully impersonates the user, plus a PII trove (PIN, email, phone, DOB) at rest. Because there is no server-side revocation (ALI-07), a recovered token is replayable indefinitely.
token (and pin/confirmPin) are removed from the object persisted to UserDefaults; read the token exclusively from Keychain. Add kSecAttrAccessibleWhenUnlockedThisDeviceOnly (and SecAccessControl/biometrics where appropriate) to the Keychain item so it cannot be backed up/migrated. Key the Keychain entry on a stable identifier, not the mutable email. Do not persist PIN material on-device; encrypt any required at-rest PII with a Keychain-held key.Confirmed across Session.swift, UserModel.swift, APIConstants.swift, Keychain.swift. Merged the UserDefaults P1, the Keychain-accessibility P2, and the mobile_api_auth dual-storage P2 raw findings into one credential-storage finding (the Keychain weakness is the same secret's secondary store). CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N.
Authentication uses an opaque server token stored in Session.userDetail.token. The app never parses an expiry, there is no refresh mechanism, and there is no logout or revoke endpoint. Logout — and even account deletion — clear only the local UserDefaults/Keychain copies, so a token captured via MITM (ALI-04), device backup (ALI-06), or logs (ALI-05) remains valid on the server indefinitely. The user cannot invalidate a stolen token.
Any leaked/stolen token is replayable for an unbounded lifetime. Logout gives a false sense of security — the token the user believes invalidated is still accepted by the backend. A single interception becomes long-lived persistent access. PR:L reflects that an attacker needs a (any) valid captured token to exploit.
/auth/logout that revokes the presented token, called on logout and account deletion. Move to short-lived access tokens with rotating refresh tokens or a server-side revocation list. Enforce server-side token expiry and invalidate all tokens on PIN/password change.Confirmed: no logout/refresh endpoint, local-only clear(). Largely a backend/design issue surfaced by the client; CVSS C:H/I:L reflects read-heavy account access with limited integrity impact on its own. CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N.
Instead of the canonical CocoaPods-trunk Siren pod, the app pulls Siren from a personal GitHub fork by git URL. CocoaPods git dependencies lack trunk's published-spec checksum verification; integrity rests solely on the commit hash in Podfile.lock, and the fork is controlled by an individual developer account on infra with no stated branch protection. If the account is compromised or the commit/branch is force-pushed, malicious Swift can be injected on the next pod install/update. Siren executes at every launch and controls the forced-update modal, giving injected code a guaranteed launch-time, all-users execution path.
Compromise of the personal fork (or a malicious maintainer commit) ships arbitrary code into the next signed build that runs at launch for every user and controls the forced-update UX — a software-supply-chain backdoor. The forced-update flow also enables fake 'Update Now' social engineering redirecting users to attacker destinations.
Confirmed Podfile/Podfile.lock fork+commit pin and the launch-time force-update config. Confidence 'likely' (not confirmed) because exploitation requires future fork compromise/force-push; the insecure posture itself is confirmed. CVSS S:C/C-I-A:H reflects worst-case arbitrary-code-at-launch if realized; AC:H for the precondition. CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H.
Every outbound request logs its full HTTPHeaders via debugPrint at APIManager.swift:40, and those headers include Authorization: Bearer <token>. Unlike print (which the app shadows to a no-op in Common.swift), debugPrint is never overridden, so it emits to the unified logging system (os_log/syslog) in production. On-device logs are readable via Console.app while plugged in, sysdiagnose bundles, MDM log collection, and support/telemetry pipelines.
The live session Bearer token plus full request URLs/parameters are written to the device log stream in production builds. Anyone who can pull a sysdiagnose, attach Console, or read captured logs recovers a token that fully impersonates the user against api.alist.ae (and, given no server revocation per ALI-07, indefinitely).
#if DEBUG and redact the Authorization header before any logging. Add a logging wrapper that strips secrets. Audit Crashlytics breadcrumbs to ensure tokens are not captured. Consider defining a no-op debugPrint shadow like the existing print shadow for defense in depth.Confirmed: the print override is a no-op AND its body is commented out; debugPrint is not overridden. AC:H reflects log-capture access requirement. CVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N.
The Terms and Privacy WebViews load a server-controlled URL (WebApiModel.data) with no scheme/host validation, then delegate any in-page navigation whose URL differs from the loaded document to UIApplication.shared.open() with no scheme allow-list. Because ATS is off and the API channel is unpinned (ALI-04), an on-path attacker can serve a MITM'd/plaintext page and inject markup/JS that triggers navigation to arbitrary schemes (tel:, sms:, mailto:, itms-apps://, shortcuts://, or any installed-app custom scheme), which the handler forwards straight into the iOS URL dispatcher — escaping the WebView into other apps. JS is enabled by default, so injected script can also run in the in-app chrome.
A MITM attacker can cause the app to silently launch arbitrary URL schemes (place calls, pre-fill SMS, open App Store pages, deep-link into other apps performing sensitive actions) and run arbitrary JavaScript inside the trusted in-app WebView, enabling phishing/content-spoofing within the app's chrome. Scope=Changed: control crosses from the WebView into other apps / the OS URL dispatcher. Realistic only once the channel is MITM'd (depends on ALI-04).
UIApplication.shared.open forwarding; only allow http(s) navigations to the trusted alist.ae origin (present external links in SFSafariViewController or block). Maintain an explicit https-only scheme allow-list before canOpenURL/open. Validate the initial WebApiModel.data URL (scheme==https, host in alist.ae allow-list). Fix ALI-04 so the page cannot be MITM'd. Set an explicit WKWebViewConfiguration disabling JavaScript for static legal content and disabling file access.Confirmed in both VCs. Merged the two raw transport WebView findings (cleartext-load P3 and decidePolicyFor open-redirect P1) — same code paths. Net P2: real, but gated behind achieving MITM (ALI-04) and limited to static legal screens; CVSS reflects S:C with low C/I. CVSS:3.1/AV:A/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N.
There is no CI/CD pipeline and no automated security gating. Builds and App Store/TestFlight uploads are manual in Xcode. No SCA (Snyk/Dependency-Check), no SBOM generation, no Dependabot, no secret scanning, and no enforced provenance/integrity check on the git/vendored dependencies (Siren fork ALI-09, QRCode copy ALI-13). This systemic gap is the root cause that let the P0 secret leaks persist for years.
New CVEs in the bundled pods/transitive components and in the vendored/forked dependencies go undetected and unpatched; secrets can be committed without alerting; a tampered dependency can reach a signed build with no automated gate. Test coverage is also effectively zero (both test targets are template stubs).
gitleaks/trufflehog) on push and full history; SCA + SBOM (CycloneDX via cyclonedx-cocoapods + Snyk or OWASP Dependency-Check) on Podfile.lock with fail-on-high; automated dependency-update PRs; provenance pinning checks for git/vendored deps. Store CI secrets in the platform secret store, never in repo.Confirmed absence of all CI/security tooling. Merged the no-CI/SCA finding with the prior audit's 'zero test coverage / no CI' quality note. P2 systemic root-cause. CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N.
Unlike every other authenticated endpoint (which derives Authorization from the live session via API.authToken), the resetPin request type builds its Authorization header from a token value carried inside the request parameter dictionary. The credential authorizing a security-sensitive PIN-reset is taken from caller-controlled input rather than the authenticated session. In normal flow the client populates this from a short-lived reset token (AccountViewModel.token), but the pattern means the operation's security hinges entirely on the server strictly binding that arbitrary token to the target account.
Inconsistent, parameter-driven authentication for PIN reset enables token-confusion / authorization-bypass if the backend does not strictly bind the presented token to the authenticated subject, and broadens the blast radius of any leaked/lower-privilege token. Server-side validation cannot be confirmed from the client, so this is a latent design weakness rather than a proven bypass.
API.authToken) like all other endpoints; never let request parameters dictate the Authorization header. Ensure the server binds the PIN-reset operation to the authenticated subject of the presented token and re-verifies an out-of-band OTP before applying the reset.Confirmed the client builds the header from param["token"]. Downgraded from the scanner's P2 to P3 and confidence to 'likely': there is no evidence the client value is attacker-controlled beyond any normal token, and exploitability depends on unverified server behavior. PR:L/AC:H reflect needing a valid token and server mis-binding. CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:N.
Submitting just an email to login/with-otp returns the account's existence and lifecycle status (approved/pending/rejected plus restricted_days) before any OTP challenge, and the client branches into different flows accordingly. The API discloses whether an email is a registered creator and its moderation state to an unauthenticated caller.
Unauthenticated attackers can enumerate valid creator emails and learn approval/rejection status, aiding targeted phishing, credential-stuffing list-building, and competitive intelligence on the invite-only creator base.
Confirmed in LoginViewController and UserModel. Primarily a backend response-design issue observable via the client; rated P3. CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N.
Social-account verification PINs and offer voucher/redemption codes are written to UIPasteboard.general — the system-wide pasteboard readable by any other app while data persists, synced across devices via Universal Clipboard, and on older iOS persistent across reboots. These values are security-relevant: the social PIN is proof-of-ownership for a creator's Instagram/TikTok account, and the voucher code authorizes a venue redemption. No expiration or local-only restriction is set.
A malicious or curious app on the device (or another of the user's devices via Universal Clipboard) can silently read social-verification PINs and voucher codes, enabling impersonation of the verification step or unauthorized voucher redemption. Confidentiality-only; requires the user to copy and an attacker app to be present.
UIPasteboard.general.setItems([[UIPasteboard.typeAutomatic: value]], options:[.localOnly:true, .expirationDate:Date().addingTimeInterval(60)]) so values are not synced and auto-expire. Better, avoid the clipboard for one-time PINs (in-app display / direct share-sheet to the target app). Clear the pasteboard when leaving the screen.Confirmed all four call sites. P3: confidentiality-only, requires a malicious app present and a user copy action. CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N.
The dagronf/QRCode library is bundled by copying its entire source tree into A-List/QRCode/ rather than declared as a versioned dependency. With no version pin or lockfile entry there is no record of the upstream release/commit, no integrity/provenance check, and no path for tooling to surface upstream security fixes. QRCode parses untrusted input (scanned QR codes from venue/offer materials, plus embedded link/contact/SMS decoders), so a parser bug in a frozen, unpatchable copy is a realistic exposure.
The app may run an outdated, unpatchable QR-parsing library that processes attacker-influenceable QR payloads, with no mechanism to detect or remediate upstream vulnerabilities and no provenance verification (a tampered copy would go undetected).
Confirmed vendored tree and absence from Podfile.lock. P3: today's confirmed-exploitable risk is low; concern is the unpatchable/unprovenanced trajectory. CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:L.
Every significant dependency is pinned to a 2022-2023 release with no update mechanism (see ALI-14). No single pinned version maps to a confirmed critical, clearly-reachable published CVE in this app's usage at review time, so this is rated low/informational rather than a concrete exploit. The baseline is ~3 years behind across networking (Alamofire), analytics/push (Firebase/GoogleUtilities/nanopb), auth (GoogleSignIn/AppAuth), and two parallel image-decoding stacks that parse untrusted remote bytes.
Increasing latent exposure to newly disclosed vulnerabilities in stale components (notably image decoders and the networking layer handling untrusted remote data), with no detection or patch path. Today's confirmed-exploitable risk is low; the trajectory and duplicate image-parser surface are the concern.
Confirmed pins against vendored Pods/. P3: no concrete reachable critical CVE confirmed at review; rated on trajectory + duplicate image stacks. No specific CVE assigned to avoid a false positive. CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N.
The Intercom mobile SDK API key (ios_sdk- prefix) and app id are hardcoded in source. Intercom iOS SDK keys are designed to ship in clients and are extractable from any IPA, so this is low-severity, but committing them in source and git history exposes them to repo readers and, if Intercom Identity Verification (HMAC) is not enforced, can permit identity spoofing of conversations / messaging-surface abuse.
If secure identity verification is not enabled in the Intercom workspace, the disclosed client key allows driving the Intercom API surface (e.g. create/spoof user conversations) and contributes to backend fingerprinting. Low because client SDK keys are extractable from any IPA by design.
ios_sdk-) and not an access token. Treat as rotate-on-leak.Confirmed at SceneDelegate.swift:22. Client SDK key; P3 by design. CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N.
The Firebase iOS config plist (with the Google API key, project id, sender id, app id) is committed and also ships inside the IPA — expected for Firebase iOS clients; the API key is not itself a high-value secret. The real risk is if the GCP API key lacks application (iOS bundle-id) and API restrictions, allowing reuse against billed Google APIs. Committing it (vs build-time injection) additionally leaves identifiers in source history/mirrors.
If the key lacks bundle-id/API restrictions, an attacker who extracts it (repo or IPA) could call enabled Google APIs against project alist-application, risking quota/billing abuse and enumeration. Real exploitability depends on key restrictions in the Google Cloud console (not verifiable via static review).
ae.alist.ios and only the Firebase/Identity APIs needed; enable Firebase App Check; lock down Firebase Security Rules so config disclosure alone grants no data access. Prefer injecting the plist at build time from a secrets store and gitignore it; rotate if ever used unrestricted.Confirmed tracked + key contents. P3 informational; impact contingent on unverifiable cloud-side key restrictions. CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N.
Developer test identities are embedded in source. The LoginViewController email prefill is correctly wrapped in #if DEBUG and stripped from Release. However the DummyData enum (test email rahulk2133@gmail.com and PIN 1234) in Constants.swift is NOT compile-gated and ships in every build, disclosing a developer email and a guessable default PIN that may map to a real test account.
Low: discloses a developer/test email and a weak default PIN that an attacker recovering strings from the IPA can try against the auth/PIN flow. Mostly information-disclosure/hygiene and an indicator of dev artifacts reaching production.
DummyData enum (and any test identities) in #if DEBUG or delete them. Add a CI check that fails a Release build if known test emails/PINs are present.Confirmed: DummyData ungated, LoginVC prefill DEBUG-gated. P3 hygiene. CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N.
V63PAFH55F in the Apple Developer portal; issue a new key stored only on the push backend / secrets manager.CAN_AUTO_FILL_OTP=false in every shipped config and remove the response-OTP harvesting.ae.alist.ios; enable Intercom HMAC identity verification.NSAllowsArbitraryLoads; add an Alamofire ServerTrustManager public-key pinning api.alist.ae / dev-api.alist.ae.git-filter-repo/BFG) on Bitbucket and the GitLab mirror; add *.p8/AuthKey_* to .gitignore.debugPrint behind #if DEBUG and redact the Authorization header.DummyData test email/PIN.kSecAttrAccessibleWhenUnlockedThisDeviceOnly./auth/logout + token expiry/revocation; rotating refresh tokens.UIApplication.shared.open forwarding; https-only scheme allow-list and validated WebView origin.gitleaks/trufflehog secret scanning + SCA/SBOM (CycloneDX + Snyk/Dependency-Check) fail-on-high.localOnly+expiring pasteboard items (or avoid the clipboard) for PINs/voucher codes.Views/ hold view controllers; Modals/ (sic) holds models; Networking/*API.swift holds request enums; ViewModels/ are slim helpers, not full MVVM. Reasonable separation for a small team but lots of cross-cutting via Session and storyboard segues.OfferDetailVC (1,936 LOC), SignUpVC (1,127), OffersVC (1,041), UploadStoriesScreeshotVC (780), HistoryVC (760), APIManager.validateResponse (~100-line if/else cascade).Modals typo for Models. Naming switches between VC and ViewController suffixes in the same folder.try! Configuration.value(…) in AppDelegate, API.baseURL uses try! (read on every request), as! CFDictionary in Keychain.swift, and 8+ as! VC casts. A missing xcconfig key or renamed storyboard id becomes an unconditional crash. Prefer try?/guard-let with sane fallbacks.Session, AppInfo.shared, AppDelegate.shared, Common.shared). Static let email capture in Keychain.swift is a subtle bug. Strong reference cycles likely in completion-heavy view controllers.intro.mp4 40 MB, cover.mp4 6 MB) plus the tracked Pods/ dir; deep-link router only handles alist://home and alist://history; Bitbucket history squashed to 2 commits (300+ commits of context live in the old GitLab repo).ViewController.swift appears unused (storyboard scaffolding). registerForPushNotifications() defined but call site commented out (AppDelegate:35). Multiple commented-out base URLs in Development.xcconfig.# Prereqs: macOS, Xcode 15+, CocoaPods 1.16.2, Ruby
# Pods/ is committed so you don't strictly need to run pod install. If you do:
pod install
# Open the workspace (NOT the .xcodeproj)
open A-List.xcworkspace
# Schemes:
# A-List-Development → base URL https://dev-api.alist.ae/, bundle id ae.alist.ios
# A-List-Release → base URL https://api.alist.ae/, bundle id ae.alist.ios (App Store)
# A-List → legacy, prefer the two above
# Service → exists; purpose unclear (no Service target obvious in pbxproj)
# Signing:
# Team: ASR5MYB7XL (Apple)
# Style: Manual
# Profiles required in your local keychain:
# - "Alist Dev" (Debug Development, Release Development)
# - "Alist-Store" (Release Production)
# Entitlements: Push (development for Dev, production for Release), associated domain applinks:alist.ae
# Sign in with Apple capability is also enabled
Debug Development, Release Development, Debug Production, Release Production — driven by A-List/Configuration/{Base,Development,Production}.xcconfig.https://dev-api.alist.ae/; Prod → https://api.alist.ae/. Path suffix v1/ appended in API.baseURL.APIConstants.swift. Auth: Bearer token in Authorization header.alist-application.614397009000-mvv5dciuk684qt72t42436uhlp18ato0.apps.googleusercontent.com).alist://; universal links via applinks:alist.ae entitlement.CAN_AUTO_FILL_OTP = true in Dev, false in Prod (but the OTP is still harvested from responses regardless — see ALI-03).No CI/CD configured (no bitbucket-pipelines.yml, no fastlane/, no GitHub Actions). Releases go App Store → bundle id ae.alist.ios, current marketing version 1.1.09. Distribution is manual via Xcode (Product → Archive → Distribute App → App Store Connect). TestFlight is the implied beta channel. First step for any new release engineering work: add a Bitbucket Pipeline that runs xcodebuild test plus the secret-scanning/SCA gates from ALI-14, and a fastlane beta lane to upload to TestFlight from CI.
A-List.xcworkspace, not .xcodeproj. Standard CocoaPods rule, but the README does not say so.Modals/ means Models. All Codable structs are there; modal UI lives under Views/ (mostly SlideUpVC & storyboards)."Custom Views", "Gif Loader", "Instagram Insight", "Privacy " — yes, trailing space). Or rename them in a single PR.Debug Production and Release Development — newcomers expect Debug=Dev/Release=Prod, but here both axes are independent.API.baseURL crashes if xcconfig is wrong. try! on every request. Verify your scheme has APP_BASE_URL set in Info.plist at runtime (it is reading $(APP_BASE_URL) via Info.plist).Session.userDetail?.token (UserDefaults, plaintext) is what the API actually uses; Keychain.serverToken is a buggy mirror keyed by email. There is no server-side logout/revocation (ALI-06/07) — a leaked token is replayable indefinitely..force and run at every launch from qubicle-aswinrnath/Siren-Alist pinned to a mutable commit (ALI-09). Treat the fork as a supply-chain risk; consider gating via remote config.FirebaseAppDelegateProxyEnabled = false. Don't switch it on — push delegate methods are wired manually in AppDelegate.intro.mp4 is 40 MB. Rebasing the repo or doing git log -p on it is slow.gitlab.com/qubicle.me/alist-ios) holds 300+ commits of context. If you need to git blame anything, ask for access there.