Approval prompt spoofing on macOS for AI agent reviews
Approval prompt spoofing on macOS can fool busy reviewers. Learn how to verify app identity, reject lookalike windows, and review AI agent actions safely.

Approval prompts are a target because they turn a hard technical attack into a simple human reflex. An attacker does not need to steal a credential if they can make a reviewer approve a request that looks ordinary, urgent, and familiar.
That problem gets sharper when autonomous coding agents can call APIs or run SSH commands. A reviewer may be looking at a pull request, a terminal, a chat window, and a stack of notifications at once. If the approval surface does not make its origin and scope obvious, the reviewer is doing security work from a thumbnail.
The fix is not teaching people to memorize every macOS dialog. Visual memory loses against a lookalike window made by someone who has studied the same screenshots. Reviewers need a repeatable way to bind an approval to three facts: the process asking, the action about to happen, and the trusted place where the approval is collected.
A familiar window does not prove who owns it
A window that resembles a system prompt is still just pixels until you connect it to a running process and a specific request. Rounded corners, a familiar button order, a menu bar icon, a Touch ID reference, and an Apple-like sentence do not establish origin. They establish that somebody knows what macOS looks like.
This distinction sounds obvious when stated plainly, yet review habits often collapse it. People ask, "Does this look like the prompt I saw last week?" The better question is, "What process caused this request, and where can I inspect that process without relying on this window?"
macOS gives users useful software provenance controls. Apple explains in its App code signing process in macOS documentation that a Developer ID signature lets users verify that software has not been altered since its developer signed it. Apple also separates that claim from notarization, which checks submitted software for known malicious content. Those controls matter, but neither one turns every sentence displayed by a signed app into a trustworthy authorization request.
A malicious program can be unsigned, but it can also be signed. A legitimate program can be compromised. A legitimate program can also ask for an action that makes no sense for the current task. The visual design of the dialog cannot answer those questions.
Treat any approval display as one of four things until you establish otherwise:
- A macOS owned consent or authentication surface.
- A window owned by the app you intended to use.
- A notification that says an app wants attention.
- A web page or overlay made to look like one of the first three.
The first category may use system controls, but do not make approval decisions from appearance alone. The remaining categories are application content. They can be useful, but they need a stronger review path than "I recognize the shape of that button."
Every approval must bind an actor, action, and channel
A reviewer can make a sound decision only when an approval binds together the actor, the action, and the channel. Most weak prompts identify one of those and ask the human to guess the rest.
The actor is the local process making the request. For an agent workflow, that is not merely "the coding assistant." It is the concrete process launched for this run, with an identifiable signing authority where the workflow can provide one. If a request says only "AI assistant needs approval," it has hidden the most useful fact from the reviewer.
The action is the actual operation. "Allow network access" is not an action description when the operation is a request to create a production user at a named API endpoint. "Approve SSH" is not enough when the command could be a read-only status check or a destructive remote deployment. A reviewer needs the verb, the destination, and the consequences that the tool can state honestly.
The channel is where the decision travels. A desktop notification is a delivery mechanism. A browser page might be an application interface. A native app window might be the correct control plane. None of those labels guarantee safety by themselves. The reviewer needs an expected channel that they can reopen independently.
Here is the difference in practice.
A weak request says:
Deployment helper needs permission. Allow?
A reviewable request says:
Process: signed local agent run from Terminal
Action: POST
/v1/releasestoapi.example.internalAuthority: production deployment credential
Scope: this one request
Decision channel: the running approval app
The second request may still deserve rejection. That is fine. Its job is not to make approval feel safe. Its job is to make the requested authority legible enough that a human can decide.
This also exposes a mistake teams make with "trusted agents." Trust in an actor is not permission for every action. A process may be familiar while its requested destination is wrong. The destination may be expected while the credential scope is excessive. A secure review flow puts those facts next to each other instead of turning approval into a general endorsement.
Notifications should summon review, not collect consent
A notification is an interruption mechanism, not a reliable authorization surface. It has limited space, competes with every other alert on the desktop, and can contain text crafted to push a quick click. If a notification says "Urgent: approve before deployment expires," it has already started applying pressure before you know who created the message or whether any deployment exists.
Apple's Notifications settings let users control previews, whether notifications appear on the lock screen, and whether they appear while the display is shared or mirrored. Apple also lets users manage permissions and presentation settings by app. Those controls reduce unwanted exposure, but they do not convert notification text into a trusted transaction record.
Reviewers should use a notification as a pointer only. Read the app name, note the time, then open the expected application from a known location. If the request is real, the application should show the same pending action with enough context to evaluate it. If the notification opens a browser page, a different helper app, or a window with no matching pending request, stop.
Do not teach reviewers to approve because the notification icon looks right. Icons are small, can be unfamiliar, and can be confused with similar names. The important question is whether the notification leads back to the control surface you already expect to use for that workflow.
Turn off previews for tools that may expose sensitive destinations, account names, or request details on a locked screen. Turn off notifications while screen sharing when the meeting audience should not see them. Those are confidentiality settings, not anti-spoofing controls, but they remove a useful source of pressure and leakage.
A notification is especially poor for high impact actions because it forces the reviewer to decide before they have context. The right behavior is slower by design: notification, open the expected app, inspect the request, then approve or deny. That added motion is not friction for its own sake. It breaks the attacker's attempt to control both the message and the click path.
Overlays exploit attention, not a macOS flaw
A lookalike approval window does not need to break macOS security controls to be dangerous. An ordinary application can draw a regular window, position it over another app, use a similar title, and time it to appear after a believable trigger. The attack works because the reviewer supplies the missing attribution.
The common story is too simple: "A fake prompt must be malware with special screen powers." Sometimes malware does seek broad permissions, but a convincing overlay does not always require them. Any app can display its own content. A browser tab can display a web page that resembles a consent screen. A remote support session can ask the user to approve a step while an operator controls the narrative.
Do not confuse the ability to see a screen with the ability to create a window. Apple treats Screen & System Audio Recording as a Privacy & Security permission that users can allow or deny for individual apps and websites. It controls capture of screen and audio content. It does not certify a prompt, and a grant of that permission does not identify who owns the window in front of you.
Accessibility permissions deserve the same discipline. They exist because legitimate software may need to interact with interface elements, but a reviewer should not infer that every window from an app with Accessibility access is system owned. Permission history can inform an investigation. It cannot serve as proof at the moment of approval.
A useful overlay review starts with behavior rather than decoration:
- Did the request appear immediately after an action you initiated?
- Does switching away and reopening the expected application reveal the same request?
- Does the request name the destination and scope in terms that match your work?
- Does it ask for a password, recovery phrase, or secret that this workflow should never need?
- Does it try to stop you from inspecting the request elsewhere?
The last item catches more bad prompts than people expect. A real control flow should survive a few seconds of verification. A deceptive one often invents a countdown, claims the request will fail if you leave the window, or makes the reviewer feel that any hesitation will cause damage.
A believable spoof has a sequence you can recognize
Imagine an engineer asks an agent to update a staging environment. The agent's terminal output says it needs approval for an API call. At nearly the same moment, a notification appears: "Deployment authorization required. Open Security Center."
The engineer clicks it. A window opens with a title that resembles a familiar macOS security panel. It contains a short description, a destination that starts with the expected company domain, and buttons labeled "Cancel" and "Allow." It even mentions Touch ID, although the window never triggers an actual biometric check.
The deception succeeds if the engineer accepts the visual story. Several things may be wrong at once:
- The notification may have come from a different app than the approval app.
- The clicked notification may have opened a browser page or helper tool rather than the actual approval control.
- The displayed destination may be a lookalike domain, a redirect, or a broad account permission rather than the one deployment request.
- The process that started the agent may be different from the process that asks for approval.
- The action may have no durable record after the click, leaving the reviewer unable to reconstruct what they allowed.
This is why a review protocol must survive a visually perfect imitation. The engineer should leave the window open without pressing either button, bring the expected approval application forward from the menu bar or Applications folder, and look for a pending request generated by the current agent run. If there is no matching request, the popup does not become more trustworthy because it is polished.
If there is a matching request, compare the two descriptions. A legitimate request should identify the same action, endpoint, and authority. If the expected application shows an SSH command while the popup describes a generic deployment approval, reject both until you understand the mismatch. Attackers benefit when reviewers treat a partial match as confirmation.
The painful part is that a spoof often uses true details. It may know the project name, the environment name, and the time of a real deployment. Those details prove that the attacker has context. They do not prove that the requested action belongs to the expected process.
Verify the installed app before you need an approval
You cannot do careful identity work while a window claims an action will expire in ten seconds. Establish the expected application identity during setup, record it in team documentation, and test the verification path during an ordinary workday.
For an app installed at a known path, this command prints the signing information that macOS sees:
codesign -dvvv "/Applications/Expected Approval App.app" 2>&1 | grep -E 'Identifier=|TeamIdentifier=|Authority='
The output has this general shape:
Identifier=com.example.approvals
TeamIdentifier=ABCDE12345
Authority=Developer ID Application: Example Software, Inc. (ABCDE12345)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Do not copy those sample values into a checklist. Record the identifier and team identifier for the software your team approved after you obtained it through your normal software process. A display name is weak evidence because multiple apps can use the same name. The bundle identifier and signing team give you facts that a window title does not.
Use a second check for the installed bundle's assessment status:
spctl -a -vv "/Applications/Expected Approval App.app"
Typical output includes an assessment such as accepted, along with the source and origin. The wording can differ between macOS releases and distribution methods, so do not build a script that fails because it expects one exact sentence. Use it as an investigation aid: an unexpected rejection, origin, path, or signing identity is a reason to stop and investigate.
Apple's Gatekeeper documentation explains that macOS checks downloaded software for an identified developer, notarization, and alteration under its default settings. This protects the launch path, which is necessary. It does not replace checking whether the app currently asks for the correct action.
Teams should keep a compact record with the application name, normal install location, bundle identifier, signing team, and who owns updates. Put it in the same internal place where people find environment names and incident contacts. When an update changes any of those facts, review the change as part of the update rather than rediscovering it during an approval event.
Avoid the lazy alternative: "It is in Applications, so it must be the right one." Applications is a location, not an identity assertion. A copied or similarly named bundle can sit there too. The path gives you a stable object to inspect. The signature gives you an identity claim. Your team process decides whether that identity is expected.
Review the request from an independent path
When an approval appears, use the same short routine every time. It should take less than a minute for ordinary requests and it should still work if the first window is completely fake.
- Pause at the first prompt. Do not click allow, dismiss, or follow an embedded link until you know what opened it.
- State the expected action in plain words. For example: "I asked the staging agent to read deployment status." If the request describes a production write, SSH session, or broad account access, you already have a discrepancy.
- Open the approval application independently through its expected menu bar item, Applications location, or known launcher. Do not use the suspicious window as your navigation control.
- Find the pending request and compare the initiating process, action, destination, authority, and scope. A matching process name alone is insufficient.
- Approve only the smallest request that completes the current task. Deny a request that is unclear, broader than necessary, or inconsistent with the work you initiated.
This routine rejects a popular but bad recommendation: "Approve a familiar agent once so it stops interrupting you." It is popular because recurring prompts irritate people and agents often need several related calls. It is wrong because prompt volume is a design problem, not a reason to erase the review boundary.
A better design can authorize a single known process for the life of its current run while keeping sensitive authorities subject to separate confirmation. The reviewer gets fewer repetitive decisions without granting permanent permission to a vague category of work.
Sallyport uses this split directly: a new agent process receives a session authorization request that identifies the process code signing authority, while a credential can be marked to require approval for every use. The agent never receives the secret itself, so the approval decides whether the app performs the action rather than handing credentials to the agent.
The distinction matters in a real incident. If an agent run begins behaving strangely, revoke its session. Do not wait for the agent to reach the next approval point. A session boundary gives the reviewer a way to stop future action from that process without assuming that every previously approved capability remains appropriate.
Approval wording should expose scope before urgency
Teams often spend too much time polishing warning language and too little time deciding what facts the approval must carry. A better sentence in a weak prompt remains weak. A slightly plain sentence with the necessary facts is safer.
Put the action first. "Create a release in production" tells the reviewer what will occur. "Authorize deployment workflow" hides the operation behind an internal label.
Put the destination next. A hostname, repository, account, or remote host tells the reviewer where authority will go. If the system cannot name a destination because it does not know one, it should say so. It should not replace missing context with a reassuring phrase such as "trusted service."
State the scope in human terms. A request that allows one GET request to a known endpoint is materially different from a credential available to every call in a session. A one command SSH approval is different from an unrestricted shell. The prompt should describe the actual boundary, not an aspirational one.
Be honest about what a reviewer cannot see. If a tool only knows that an agent will initiate an SSH connection, it should say that rather than pretending it has inspected every remote command. False precision trains people to trust labels that do not correspond to enforcement.
Avoid vague action names, especially these:
- "Continue"
- "Grant access"
- "Complete setup"
- "Verify identity"
- "Allow agent operation"
Each phrase asks a reviewer to supply meaning from context. If that context came from an attacker controlled terminal message, chat message, notification, or popup, the reviewer has become part of the spoof.
A good approval flow also gives refusal a normal outcome. "Deny" should not read like a dangerous exception. It should stop the action, preserve enough information for the reviewer to inspect later, and let the user retry after correcting the request. When denial feels catastrophic, users will approve just to get unstuck.
Logs turn suspicion into an investigation
After a questionable approval, stop treating the screen as the evidence. Screens vanish, windows can lie, and memory edits itself quickly. Preserve a record of what process ran, what action it requested, what the reviewer decided, and whether the action reached its destination.
Capture the time range first. Then save the agent transcript, terminal history relevant to the run, and the approval tool's action history. Record the exact path of the application that displayed the request. If the suspicious item came through a notification, note its displayed app name and whether opening it led to a browser, a helper, or the expected application.
Do not begin by deleting the suspicious app. If you need to preserve evidence for an internal investigation, note its path and signing details first. If the machine may be compromised, follow your team's incident process and isolate it as appropriate. A prompt spoofing event can be a social attack, a local unwanted app, a browser compromise, or a sign that a legitimate tool has a design flaw. The evidence tells you which problem you have.
For agent actions, the log needs two levels. One record should describe the run and let an operator revoke it. Another should describe individual calls, including the destination and the decision. Without the first, you cannot stop a live actor cleanly. Without the second, you cannot tell what approval actually enabled.
Sallyport projects both session and individual activity records from an encrypted, hash chained audit log, and sp audit verify can verify the chain offline over ciphertext. That does not make a bad approval harmless, but it gives a reviewer and investigator a durable answer to the question that matters after the popup is gone: what action did this process actually perform?
The goal is not to make every reviewer a macOS security specialist. It is to make the safe action ordinary: leave the suspicious surface, reopen the expected control plane, compare the facts, and approve only the request that fits the work in front of you. A prompt that cannot survive that check does not deserve a click.
FAQ
What should I do if I receive an unexpected approval notification on my Mac?
Treat it as untrusted until you reconnect the request to the app and action that caused it. Do not approve from the notification itself. Open the expected app from Applications or the menu bar, find the pending request there, and compare the process, destination, and scope.
Can a real macOS notification still be part of a spoofing attack?
A notification can be delivered by a real app and still contain misleading text, a misleading button label, or a link that opens a fake approval screen. Native delivery only tells you which app posted the notification. It does not prove that the requested action is safe or that the next window is a system dialog.
Does code signing prove an approval prompt is safe?
No. A signed app can still be unwanted, compromised, or simply asking for more access than the situation warrants. Code signing tells you whether the app on disk matches its signer, not whether an approval request makes sense right now.
How can I verify which Mac app is asking for approval?
Check the exact installed application you expect to use, not a similarly named app in Downloads or a copied bundle on the Desktop. Use codesign -dvvv on its path, record the Identifier and TeamIdentifier during setup, and investigate any change before approving actions.
Can a normal app create a fake macOS system dialog?
No. An ordinary application can place a normal window above other windows and imitate familiar wording. Screen recording access lets an app capture display content, while Accessibility access can let it control parts of the interface. Neither permission should become a shortcut for trusting a window.
Should I approve sensitive actions directly from notifications?
Notifications are useful for telling you that attention is needed. They are poor places to authorize money movement, production changes, secret use, or remote commands because the visible space is small and the context can be incomplete.
What information should a secure AI agent approval show?
A good approval names the initiating process, the concrete action, the destination, the credential or authority involved, and the duration of the permission. If the request leaves any of those implicit, it asks the reviewer to fill in the risk from memory.
What should I do after I think I approved a spoofed prompt?
Stop the run, revoke any active authorization if the tool supports it, and preserve the relevant logs before you start cleaning up. Then inspect recent processes, browser tabs, login items, and the action history for the actual destination and command. Do not keep clicking through the prompt while you investigate.
Why is identifying the requesting process not enough?
Separate the actor from the request. A familiar process can make an unfamiliar request, and a request may be reasonable only for a limited time and target. Reviewers should approve a named action with a stated scope, not grant a vague trust label to an agent.
How does Sallyport reduce approval prompt confusion?
It means the app collects the approval decision in the same control plane that handles the action, while the agent never receives the secret itself. The useful part for reviewers is that the first authorization identifies the process signing authority and that sensitive credentials can require confirmation on every use.