add private macOS password storage skill

This commit is contained in:
twentyOne2x 2026-07-13 02:06:59 +02:00
parent cf796fea76
commit d6fae7be3c
6 changed files with 787 additions and 0 deletions

View file

@ -0,0 +1,63 @@
---
name: private-password-storage
description: Store or update a private password or credential in the device-local macOS Keychain through a native secure popup, and check presence without retrieval. Use when a user asks to save, store, update, or verify a private credential securely on a Mac.
---
# Private Password Storage
Use the bundled helper so the credential enters only through a native
`NSSecureTextField` and goes directly to Security.framework.
## Choose The Right Authentication Route
Prefer the provider's supported credential manager or login flow when one
exists. Keychain storage does not authenticate an application and cannot
satisfy OAuth, OTP, passkeys, browser sessions, or `gcloud` login by itself.
Never ask the user to paste a credential into chat.
## Store Or Update
Run `--store` only after the user asks to open the secure popup:
```bash
.agents/skills/private-password-storage/scripts/private-password-storage \
--service "com.example.application" \
--account "operator@example.invalid" \
--label "Example application password" \
--store
```
The popup accepts normal typing, Cmd+V, and right-click Paste. Empty input
stays in the popup and is rejected. A successful write prints only `stored`.
The item is stored as `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` with
synchronization disabled.
## Check Presence
```bash
.agents/skills/private-password-storage/scripts/private-password-storage \
--service "com.example.application" \
--account "operator@example.invalid" \
--status
```
Status prints only `present` or `absent`. It never returns the credential.
## Safety Contract
- Keep the credential out of process arguments, shell variables, files,
logs, screenshots, terminal output, clipboard writes, and proof artifacts.
- Do not add retrieval, reveal, echo, export, or fingerprint modes.
- Do not use AppleScript, `osascript`, System Events, `pbcopy`, or the
`security add-generic-password` CLI.
- Treat service, account, and label as non-secret metadata; do not print them.
- Report only the helper status and whether a native popup was used.
## Sanitized Canary
Use only a generated fake value and a service beginning with
`dev.codex.private-password-storage.canary.` plus an account beginning with
`canary-`. The test-only `--delete-test-item` action is prefix-guarded and
deletes only that exact service/account pair. Verify `present`, delete the
exact item, then verify `absent`. Do not retain the fake value anywhere.

View file

@ -0,0 +1,4 @@
interface:
display_name: "Private Password Storage"
short_description: "Store device-local credentials securely"
default_prompt: "Use $private-password-storage to store or update a private credential securely in the macOS Keychain."

View file

@ -0,0 +1,5 @@
#!/bin/sh
set -eu
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
exec python3 "$SCRIPT_DIR/private_password_storage.py" "$@"

View file

@ -0,0 +1,191 @@
#!/usr/bin/env python3
"""Launch the native macOS Keychain helper without accepting secret arguments."""
from __future__ import annotations
import argparse
import os
import platform
import plistlib
import subprocess
import sys
import tempfile
from collections.abc import Iterator
from contextlib import contextmanager
from pathlib import Path
class SafeArgumentParser(argparse.ArgumentParser):
def error(self, message: str) -> None:
raise ValueError(message)
def parse_args() -> argparse.Namespace:
parser = SafeArgumentParser(allow_abbrev=False)
parser.add_argument("--service", required=True)
parser.add_argument("--account", required=True)
parser.add_argument("--label", default="Private credential")
actions = parser.add_mutually_exclusive_group(required=True)
actions.add_argument("--store", action="store_true")
actions.add_argument("--status", action="store_true")
actions.add_argument(
"--delete-test-item", action="store_true", help=argparse.SUPPRESS
)
return parser.parse_args()
def validate_metadata(args: argparse.Namespace) -> bool:
values = (args.service, args.account, args.label)
return all(value and "\x00" not in value for value in values)
@contextmanager
def native_command() -> Iterator[tuple[list[str], Path | None] | None]:
test_helper = os.environ.get("PRIVATE_PASSWORD_STORAGE_TEST_HELPER")
if test_helper:
yield ([test_helper], None)
return
if sys.platform != "darwin":
yield None
return
discovery = subprocess.run(
["/usr/bin/xcrun", "--find", "swiftc"],
check=False,
capture_output=True,
text=True,
)
if discovery.returncode != 0:
yield None
return
if not discovery.stdout.strip():
yield None
return
target = f"{platform.machine()}-apple-macosx12.0"
with tempfile.TemporaryDirectory(prefix="private-password-storage-") as temp:
app = Path(temp) / "Private Password Storage.app"
macos = app / "Contents" / "MacOS"
macos.mkdir(parents=True)
executable = macos / "private-password-storage-native"
info = {
"CFBundleDisplayName": "Private Password Storage",
"CFBundleExecutable": executable.name,
"CFBundleIdentifier": "dev.codex.private-password-storage",
"CFBundleName": "Private Password Storage",
"CFBundlePackageType": "APPL",
"CFBundleVersion": "1",
"CFBundleShortVersionString": "1.0",
"LSMinimumSystemVersion": "12.0",
"NSHighResolutionCapable": True,
}
with (app / "Contents" / "Info.plist").open("wb") as handle:
plistlib.dump(info, handle)
compilation = subprocess.run(
[
"/usr/bin/xcrun",
"swiftc",
"-target",
target,
str(Path(__file__).with_suffix(".swift")),
"-o",
str(executable),
],
check=False,
capture_output=True,
text=True,
)
if compilation.returncode != 0:
yield None
return
yield ([str(executable)], app)
def action_args(args: argparse.Namespace) -> list[str]:
if args.store:
action = "--store"
elif args.status:
action = "--status"
else:
action = "--delete-test-item"
return [
"--service",
args.service,
"--account",
args.account,
"--label",
args.label,
action,
]
def main() -> int:
try:
args = parse_args()
except (ValueError, SystemExit):
sys.stdout.write("error\n")
return 64
if not validate_metadata(args):
sys.stdout.write("error\n")
return 64
with native_command() as native:
if native is None:
sys.stdout.write("error\n")
return 69
command, app = native
try:
if args.store and app is not None:
result_file = app.parent / "store-status"
completed = subprocess.run(
[
"/usr/bin/open",
"-W",
"-n",
str(app),
"--args",
*action_args(args),
"--result-file",
str(result_file),
],
check=False,
capture_output=True,
text=True,
)
output = (
result_file.read_text(encoding="utf-8").strip()
if result_file.is_file()
else ""
)
else:
completed = subprocess.run(
[*command, *action_args(args)],
check=False,
capture_output=True,
text=True,
)
output = completed.stdout.strip()
except KeyboardInterrupt:
sys.stdout.write("cancelled\n")
return 1
allowed = {
"stored": {"stored", "cancelled"},
"status": {"present", "absent"},
"delete": {"absent"},
}
expected = "stored" if args.store else "status" if args.status else "delete"
if completed.returncode not in (0, 1) or output not in allowed[expected]:
sys.stdout.write("error\n")
return 1
sys.stdout.write(f"{output}\n")
if expected == "stored" and output != "stored":
return 1
return completed.returncode
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,346 @@
import AppKit
import Foundation
import Security
private enum Action {
case store
case status
case deleteTestItem
}
private struct Options {
let service: String
let account: String
let label: String
let action: Action
let resultFile: String?
}
private enum StoreResult {
case stored
case cancelled
case failed
}
private enum PresenceResult {
case present
case absent
case failed
}
private func parseOptions() -> Options? {
var service: String?
var account: String?
var label = "Private credential"
var action: Action?
var resultFile: String?
var index = 1
let arguments = CommandLine.arguments
while index < arguments.count {
let argument = arguments[index]
switch argument {
case "--service", "--account", "--label", "--result-file":
guard index + 1 < arguments.count else { return nil }
let value = arguments[index + 1]
guard !value.isEmpty, !value.contains("\0") else { return nil }
if argument == "--service" {
service = value
} else if argument == "--account" {
account = value
} else if argument == "--label" {
label = value
} else {
resultFile = value
}
index += 2
case "--store", "--status", "--delete-test-item":
guard action == nil else { return nil }
action = argument == "--store" ? .store
: argument == "--status" ? .status : .deleteTestItem
index += 1
default:
return nil
}
}
guard let service, let account, let action, !label.isEmpty else { return nil }
return Options(
service: service,
account: account,
label: label,
action: action,
resultFile: resultFile
)
}
private func deviceLocalQuery(service: String, account: String) -> [CFString: Any] {
[
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: account,
kSecAttrSynchronizable: kCFBooleanFalse as Any,
]
}
private func storeInKeychain(
service: String,
account: String,
label: String,
secretData: Data
) -> Bool {
let query = deviceLocalQuery(service: service, account: account)
var item = query
item[kSecAttrLabel] = label
item[kSecAttrAccessible] = kSecAttrAccessibleWhenUnlockedThisDeviceOnly
item[kSecValueData] = secretData
let addStatus = SecItemAdd(item as CFDictionary, nil)
if addStatus == errSecSuccess {
return true
}
guard addStatus == errSecDuplicateItem else {
return false
}
let updates: [CFString: Any] = [
kSecAttrLabel: label,
kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
kSecValueData: secretData,
]
return SecItemUpdate(query as CFDictionary, updates as CFDictionary) == errSecSuccess
}
private func keychainPresence(service: String, account: String) -> PresenceResult {
var query = deviceLocalQuery(service: service, account: account)
query[kSecMatchLimit] = kSecMatchLimitOne
query[kSecReturnData] = kCFBooleanFalse
let status = SecItemCopyMatching(query as CFDictionary, nil)
if status == errSecSuccess {
return .present
}
if status == errSecItemNotFound {
return .absent
}
return .failed
}
private func deleteExactCanaryItem(service: String, account: String) -> Bool {
guard service.hasPrefix("dev.codex.private-password-storage.canary."),
account.hasPrefix("canary-") else {
return false
}
let query = deviceLocalQuery(service: service, account: account)
let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess || status == errSecItemNotFound
}
@MainActor
private final class SecurePromptController: NSObject, NSWindowDelegate {
private let service: String
private let account: String
private let itemLabel: String
private let window: NSWindow
private let secureField = NSSecureTextField()
private let validationLabel = NSTextField(labelWithString: "")
private var result: StoreResult?
init(service: String, account: String, label: String) {
self.service = service
self.account = account
self.itemLabel = label
self.window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 440, height: 190),
styleMask: [.titled, .closable],
backing: .buffered,
defer: false
)
super.init()
configureWindow()
}
private func configureWindow() {
window.title = "Store Private Credential"
window.isReleasedWhenClosed = false
window.delegate = self
window.center()
let content = NSView()
window.contentView = content
let title = NSTextField(labelWithString: itemLabel)
title.font = NSFont.systemFont(ofSize: 15, weight: .semibold)
title.lineBreakMode = .byTruncatingTail
secureField.placeholderString = "Password"
secureField.font = NSFont.systemFont(ofSize: 14)
validationLabel.stringValue = "Password cannot be empty."
validationLabel.textColor = .systemRed
validationLabel.isHidden = true
let cancelButton = NSButton(
title: "Cancel", target: self, action: #selector(cancelPressed)
)
cancelButton.keyEquivalent = "\u{1b}"
let storeButton = NSButton(
title: "Store", target: self, action: #selector(storePressed)
)
storeButton.keyEquivalent = "\r"
let pasteMenu = NSMenu()
let pasteItem = NSMenuItem(
title: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: ""
)
pasteItem.target = nil
pasteMenu.addItem(pasteItem)
secureField.menu = pasteMenu
[title, secureField, validationLabel, cancelButton, storeButton].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
content.addSubview($0)
}
NSLayoutConstraint.activate([
title.topAnchor.constraint(equalTo: content.topAnchor, constant: 24),
title.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 24),
title.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -24),
secureField.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 16),
secureField.leadingAnchor.constraint(equalTo: title.leadingAnchor),
secureField.trailingAnchor.constraint(equalTo: title.trailingAnchor),
secureField.heightAnchor.constraint(equalToConstant: 28),
validationLabel.topAnchor.constraint(equalTo: secureField.bottomAnchor, constant: 6),
validationLabel.leadingAnchor.constraint(equalTo: secureField.leadingAnchor),
storeButton.trailingAnchor.constraint(equalTo: secureField.trailingAnchor),
storeButton.bottomAnchor.constraint(equalTo: content.bottomAnchor, constant: -20),
cancelButton.trailingAnchor.constraint(equalTo: storeButton.leadingAnchor, constant: -10),
cancelButton.centerYAnchor.constraint(equalTo: storeButton.centerYAnchor),
])
}
private func installPasteMenu() {
let mainMenu = NSMenu()
let editMenuItem = NSMenuItem()
let editMenu = NSMenu(title: "Edit")
let pasteItem = NSMenuItem(
title: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v"
)
pasteItem.keyEquivalentModifierMask = [.command]
pasteItem.target = nil
editMenu.addItem(pasteItem)
editMenuItem.submenu = editMenu
mainMenu.addItem(editMenuItem)
NSApp.mainMenu = mainMenu
}
func run() -> StoreResult {
installPasteMenu()
NSApp.setActivationPolicy(.regular)
NSApp.activate(ignoringOtherApps: true)
window.makeKeyAndOrderFront(nil)
window.makeFirstResponder(secureField)
NSApp.run()
return result ?? .cancelled
}
@objc private func storePressed() {
guard !secureField.stringValue.isEmpty else {
validationLabel.stringValue = "Password cannot be empty."
validationLabel.isHidden = false
NSSound.beep()
window.makeFirstResponder(secureField)
return
}
validationLabel.isHidden = true
var secretData = Data(secureField.stringValue.utf8)
let stored = storeInKeychain(
service: service,
account: account,
label: itemLabel,
secretData: secretData
)
secureField.stringValue = ""
secretData.resetBytes(in: 0..<secretData.count)
guard stored else {
validationLabel.stringValue = "The credential could not be stored."
validationLabel.isHidden = false
return
}
result = .stored
window.close()
}
@objc private func cancelPressed() {
secureField.stringValue = ""
result = .cancelled
window.close()
}
func windowWillClose(_ notification: Notification) {
secureField.stringValue = ""
if result == nil {
result = .cancelled
}
NSApp.stop(nil)
}
}
@MainActor
private func runSecurePrompt(options: Options) -> StoreResult {
let application = NSApplication.shared
let controller = SecurePromptController(
service: options.service,
account: options.account,
label: options.label
)
_ = application
return controller.run()
}
private func emit(_ value: String, code: Int32, resultFile: String? = nil) -> Never {
let data = Data((value + "\n").utf8)
if let resultFile {
do {
try data.write(to: URL(fileURLWithPath: resultFile), options: .atomic)
} catch {
exit(74)
}
} else {
FileHandle.standardOutput.write(data)
}
exit(code)
}
guard let options = parseOptions() else {
emit("error", code: 64)
}
switch options.action {
case .status:
switch keychainPresence(service: options.service, account: options.account) {
case .present:
emit("present", code: 0, resultFile: options.resultFile)
case .absent:
emit("absent", code: 1, resultFile: options.resultFile)
case .failed:
emit("error", code: 1, resultFile: options.resultFile)
}
case .store:
let storeResult = MainActor.assumeIsolated {
runSecurePrompt(options: options)
}
switch storeResult {
case .stored:
emit("stored", code: 0, resultFile: options.resultFile)
case .cancelled:
emit("cancelled", code: 1, resultFile: options.resultFile)
case .failed:
emit("error", code: 1, resultFile: options.resultFile)
}
case .deleteTestItem:
guard deleteExactCanaryItem(service: options.service, account: options.account) else {
emit("error", code: 1, resultFile: options.resultFile)
}
emit("absent", code: 0, resultFile: options.resultFile)
}

View file

@ -0,0 +1,178 @@
from __future__ import annotations
import json
import os
import platform
import subprocess
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
SKILL = ROOT / ".agents" / "skills" / "private-password-storage"
LAUNCHER = SKILL / "scripts" / "private-password-storage"
PYTHON_HELPER = SKILL / "scripts" / "private_password_storage.py"
SWIFT_HELPER = SKILL / "scripts" / "private_password_storage.swift"
def _fake_helper(tmp_path: Path) -> tuple[Path, Path]:
helper = tmp_path / "fake_keychain_helper.py"
capture = tmp_path / "captured_args.json"
helper.write_text(
"""#!/usr/bin/env python3
import json
import os
import sys
with open(os.environ["PRIVATE_PASSWORD_STORAGE_TEST_CAPTURE"], "w", encoding="utf-8") as handle:
json.dump(sys.argv[1:], handle)
if "--store" in sys.argv:
print("stored")
elif "--status" in sys.argv:
print("present")
elif "--delete-test-item" in sys.argv:
print("absent")
else:
print("error")
raise SystemExit(64)
""",
encoding="utf-8",
)
helper.chmod(0o700)
return helper, capture
def _run_fake(tmp_path: Path, action: str) -> tuple[subprocess.CompletedProcess[str], list[str]]:
helper, capture = _fake_helper(tmp_path)
env = os.environ.copy()
env["PRIVATE_PASSWORD_STORAGE_TEST_HELPER"] = str(helper)
env["PRIVATE_PASSWORD_STORAGE_TEST_CAPTURE"] = str(capture)
completed = subprocess.run(
[
str(LAUNCHER),
"--service",
"dev.codex.private-password-storage.canary.ci",
"--account",
"canary-ci",
"--label",
"CI fake credential",
action,
],
check=False,
capture_output=True,
text=True,
env=env,
)
return completed, json.loads(capture.read_text(encoding="utf-8"))
@pytest.mark.parametrize(
("action", "expected", "returncode"),
[
("--store", "stored\n", 0),
("--status", "present\n", 0),
("--delete-test-item", "absent\n", 0),
],
)
def test_launcher_uses_only_metadata_and_action(
tmp_path: Path, action: str, expected: str, returncode: int
) -> None:
completed, captured = _run_fake(tmp_path, action)
assert completed.returncode == returncode
assert completed.stdout == expected
assert completed.stderr == ""
assert captured == [
"--service",
"dev.codex.private-password-storage.canary.ci",
"--account",
"canary-ci",
"--label",
"CI fake credential",
action,
]
def test_scripts_ban_unsafe_secret_paths_and_retrieval() -> None:
implementation = "\n".join(
path.read_text(encoding="utf-8")
for path in (LAUNCHER, PYTHON_HELPER, SWIFT_HELPER)
)
banned = (
"AppleScript",
"osascript",
"System Events",
"NSPasteboard",
"pbcopy",
"security add-generic-password",
"--get",
"--read-secret",
"--retrieve",
"kSecReturnData] = kCFBooleanTrue",
"kSecReturnAttributes",
"print(secret",
"print(password",
)
for token in banned:
assert token not in implementation
def test_swift_enforces_device_local_keychain_and_secure_popup() -> None:
source = SWIFT_HELPER.read_text(encoding="utf-8")
for required in (
"NSSecureTextField",
'title: "Paste"',
"keyEquivalentModifierMask = [.command]",
"guard !secureField.stringValue.isEmpty",
"kSecAttrAccessibleWhenUnlockedThisDeviceOnly",
"kSecAttrSynchronizable: kCFBooleanFalse",
"SecItemAdd",
"SecItemUpdate",
"SecItemCopyMatching",
"SecItemDelete",
'service.hasPrefix("dev.codex.private-password-storage.canary.")',
'account.hasPrefix("canary-")',
):
assert required in source
store_case = source.split("case .store:", 1)[1].split(
"case .deleteTestItem:", 1
)[0]
assert "runSecurePrompt" in store_case
assert "runSecurePrompt" not in source.split("case .status:", 1)[1].split(
"case .store:", 1
)[0]
def test_launcher_builds_an_ephemeral_identifiable_native_app() -> None:
source = PYTHON_HELPER.read_text(encoding="utf-8")
assert 'TemporaryDirectory(prefix="private-password-storage-")' in source
assert '"CFBundleIdentifier": "dev.codex.private-password-storage"' in source
assert '"CFBundlePackageType": "APPL"' in source
assert '"NSHighResolutionCapable": True' in source
assert '"/usr/bin/open"' in source
assert 'result_file = app.parent / "store-status"' in source
assert "secretData.write" not in SWIFT_HELPER.read_text(encoding="utf-8")
@pytest.mark.skipif(sys.platform != "darwin", reason="requires macOS AppKit SDK")
def test_embedded_swift_helper_typechecks() -> None:
target = f"{platform.machine()}-apple-macosx12.0"
completed = subprocess.run(
[
"/usr/bin/xcrun",
"swiftc",
"-target",
target,
"-typecheck",
str(SWIFT_HELPER),
],
check=False,
capture_output=True,
text=True,
)
assert completed.returncode == 0, completed.stderr