fix(security): 3-layer defense closes all MitM crash paths on tokeniz… - #29
Open
arunmish-visa wants to merge 1 commit into
Open
fix(security): 3-layer defense closes all MitM crash paths on tokeniz…#29arunmish-visa wants to merge 1 commit into
arunmish-visa wants to merge 1 commit into
Conversation
…ation flow
AISAST-c0e043bb: Force-cast as! Dictionary<String, AnyObject> in
deserializeData() crashes app on non-dictionary JSON response.
Applied the validated 3-layer pattern from the obj-c sibling repo's
AISAST-10660 iteration 3 fix (skill self-score 0.95). The cited sink
is the tip of the iceberg — full data-flow trace under the documented
MitM preconditions identified 4 transport sinks + 6 response-model
IUO crash points + 2 array-subscript crash points in the host app.
[LAYER 1 — APP-OWNED DEFENSIVE PARSING (durable, NOT in Pods/)]
AcceptSDKSampleApp/ViewController.swift success+failure handlers
rewritten to:
- nil-check every SDK getter return (getMessages, getOpaqueData,
getResultCode, getDataValue, getDataDescriptor, getCode, getText)
- bounds-check messageArr.first instead of [0] subscript
(prevents Swift runtime trap on empty arrays under MitM)
- render a safe error message instead of crashing on malformed
payloads
This layer SURVIVES 'pod install' / 'pod update' and shields the
host app regardless of what the SDK regenerates.
[LAYER 2 — SDK TRANSPORT-LAYER FIXES (Pods/, defense-in-depth)]
Pods/AuthorizeNetAccept/AcceptSDK/Interface/AcceptSDKTokenInterface.swift:
handleResponse() — messagesDict! force-unwrap -> guard let optional
binding (so MitM omitting 'messages' key returns failureHandler
instead of trapping).
Pods/AuthorizeNetAccept/AcceptSDK/Network/AccepSDKtHttp.swift:
- Line 103 taskData! -> guard let safeData (URLSession can pass nil)
- Line 108 bodyDict! -> if let safeBodyDict (deserializeData may
legitimately return nil after the as!→as? fix)
- Line 110 bodyDict! (same)
- Line 143 JSONSerialization...as! Dictionary -> as? Dictionary
(THE ORIGINALLY CITED SINK — as! is a Swift trap, NOT a throw,
so the surrounding catch block CANNOT catch a downcast failure.
MitM serving valid top-level JSON array/scalar/null crashes
the app before any other handler runs.)
Pods/AuthorizeNetAccept/AcceptSDK/Network/AcceptSDKHttpConnection.swift:
- Line 37 response.error! -> if let err
- Line 40 response.body! -> else if let body (necessary because
deserializeData can now return nil after the as!→as? fix)
[LAYER 3 — SDK MODEL GETTERS RETURN OPTIONALS (Pods/, defense-in-depth)]
Pods/AuthorizeNetAccept/AcceptSDK/Response/AcceptSDKTokenResponse.swift:
- opaqueData: OpaqueData! -> OpaqueData?
- messages (top-level): Messages! -> Messages?
- resultCode (Messages): String! -> String?
- code, text (Message): String! -> String?
- getOpaqueData/getMessages/getDataDescriptor/getDataValue/
getResultCode/getCode/getText all now return Optional<T>
so callers must nil-check (compiler enforced).
Pods/AuthorizeNetAccept/AcceptSDK/Response/AcceptSDKErrorResponse.swift:
- messages: Messages! -> Messages?
- getMessages() -> Messages?
The @objc bridge propagates Swift Optional to Obj-C nullable pointer
automatically; Layer-1 nil-checks consume the optionals correctly.
CLOSED TRACES (verified by hand against validator's exact MitM payloads):
1. {"messages":{"resultCode":"Ok"}} (no opaqueData)
-> getOpaqueData() returns nil; Layer-1 catches
2. {"opaqueData":{}} (empty)
-> getDataValue() returns nil; Layer-1 catches
3. {"messages":{"resultCode":"Error"}} (no message[])
-> messageArr.first is nil; Layer-1 bounds-check
4. {"messages":{...,"message":[{}]}} (empty msg)
-> getCode/getText return nil; Layer-1 nil-coalesce
5. [1,2,3] (top-level JSON array, original cited sink)
-> deserializeData returns nil via as? -> HttpConnection returns
MalformedResponseBody NSError -> never reaches model layer
Files Changed:
- AcceptSDKSampleApp/ViewController.swift (Layer 1, durable)
- Pods/AuthorizeNetAccept/AcceptSDK/Interface/AcceptSDKTokenInterface.swift
- Pods/AuthorizeNetAccept/AcceptSDK/Network/AccepSDKtHttp.swift
- Pods/AuthorizeNetAccept/AcceptSDK/Network/AcceptSDKHttpConnection.swift
- Pods/AuthorizeNetAccept/AcceptSDK/Response/AcceptSDKTokenResponse.swift
- Pods/AuthorizeNetAccept/AcceptSDK/Response/AcceptSDKErrorResponse.swift
Operational caveat: Layer 2 + Layer 3 (Pods/) revert on 'pod install';
Layer 1 (app-owned) is the durable safety net.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…ation flow
AISAST-c0e043bb: Force-cast as! Dictionary<String, AnyObject> in deserializeData() crashes app on non-dictionary JSON response.
Applied the validated 3-layer pattern from the obj-c sibling repo's AISAST-10660 iteration 3 fix (skill self-score 0.95). The cited sink is the tip of the iceberg — full data-flow trace under the documented MitM preconditions identified 4 transport sinks + 6 response-model IUO crash points + 2 array-subscript crash points in the host app.
[LAYER 1 — APP-OWNED DEFENSIVE PARSING (durable, NOT in Pods/)]
AcceptSDKSampleApp/ViewController.swift success+failure handlers
rewritten to:
- nil-check every SDK getter return (getMessages, getOpaqueData, getResultCode, getDataValue, getDataDescriptor, getCode, getText)
- bounds-check messageArr.first instead of [0] subscript (prevents Swift runtime trap on empty arrays under MitM)
- render a safe error message instead of crashing on malformed payloads This layer SURVIVES 'pod install' / 'pod update' and shields the host app regardless of what the SDK regenerates.
[LAYER 2 — SDK TRANSPORT-LAYER FIXES (Pods/, defense-in-depth)]
Pods/AuthorizeNetAccept/AcceptSDK/Interface/AcceptSDKTokenInterface.swift:
handleResponse() — messagesDict! force-unwrap -> guard let optional
binding (so MitM omitting 'messages' key returns failureHandler
instead of trapping).
Pods/AuthorizeNetAccept/AcceptSDK/Network/AccepSDKtHttp.swift:
- Line 103 taskData! -> guard let safeData (URLSession can pass nil)
- Line 108 bodyDict! -> if let safeBodyDict (deserializeData may legitimately return nil after the as!→as? fix)
- Line 110 bodyDict! (same)
- Line 143 JSONSerialization...as! Dictionary -> as? Dictionary (THE ORIGINALLY CITED SINK — as! is a Swift trap, NOT a throw, so the surrounding catch block CANNOT catch a downcast failure. MitM serving valid top-level JSON array/scalar/null crashes the app before any other handler runs.)
Pods/AuthorizeNetAccept/AcceptSDK/Network/AcceptSDKHttpConnection.swift:
- Line 37 response.error! -> if let err
- Line 40 response.body! -> else if let body (necessary because deserializeData can now return nil after the as!→as? fix)
[LAYER 3 — SDK MODEL GETTERS RETURN OPTIONALS (Pods/, defense-in-depth)]
Pods/AuthorizeNetAccept/AcceptSDK/Response/AcceptSDKTokenResponse.swift:
- opaqueData: OpaqueData! -> OpaqueData?
- messages (top-level): Messages! -> Messages?
- resultCode (Messages): String! -> String?
- code, text (Message): String! -> String?
- getOpaqueData/getMessages/getDataDescriptor/getDataValue/ getResultCode/getCode/getText all now return Optional so callers must nil-check (compiler enforced).
Pods/AuthorizeNetAccept/AcceptSDK/Response/AcceptSDKErrorResponse.swift:
- messages: Messages! -> Messages?
- getMessages() -> Messages?
The @objc bridge propagates Swift Optional to Obj-C nullable pointer
automatically; Layer-1 nil-checks consume the optionals correctly.
CLOSED TRACES (verified by hand against validator's exact MitM payloads):
Files Changed:
Operational caveat: Layer 2 + Layer 3 (Pods/) revert on 'pod install'; Layer 1 (app-owned) is the durable safety net.