Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
8ed1c37
Add autofill feature: bookmarklet, popup protocol, and autotype field
dbro May 19, 2026
a3ab4a8
Fix autofill: relay popup through BroadcastChannel to avoid re-unlock
dbro May 19, 2026
f0b43a7
Autofill: relay.html becomes popup picker UI, eliminating tab-switch …
dbro May 20, 2026
c0fdc2d
Autofill: single-click record row to autofill, remove radio+button step
dbro May 20, 2026
eadb61a
Autofill bookmarklet: fix popup-close lock, URL save, and timeout
dbro May 20, 2026
d8d9c7b
Autofill picker: show URL match type badges (URL match, similar URL)
dbro May 20, 2026
e422f6d
Autofill: extended autotype codes, literal text, and unknown-code han…
dbro May 20, 2026
67a4bc1
Autofill edit form: always show code legend alongside error or warning
dbro May 20, 2026
6f0b7d7
Autofill UI: add Portpass logo to relay popup, fill overlay, and book…
dbro May 20, 2026
cc232cd
Autofill: block sensitive fields on HTTP, not just password
dbro May 20, 2026
8b7a9d6
Autofill: delegate management UI and IDB module (step 1 of cross-prof…
dbro May 20, 2026
4a57fb1
Autofill: relay.html dual-mode, signature verification, credential dr…
dbro May 20, 2026
b5cfe71
Debug: add [portpass] console logging for autofill mode detection and…
dbro May 20, 2026
8c13aea
Autofill: cross-profile via relay server polling (working end-to-end)
dbro May 20, 2026
d43e228
Autofill: fix cross-profile bad blob with retry; CSP; extended debug …
dbro May 20, 2026
0c597fb
Autofill: ECDSA authentication on same-profile BC path
dbro May 20, 2026
bac00cf
Tests: update bookmarklet and autofill_popup specs for delegate autofill
dbro May 20, 2026
5403367
Tests: fix error-case race in activateBookmarklet
dbro May 20, 2026
3aec39a
Autofill: use localhost:7677, fix relay-not-running error message
dbro May 20, 2026
de0c2e6
Autofill: remove debug logging, revert debug close timeouts
dbro May 20, 2026
c0097e3
Fix syntax errors introduced by debug log cleanup
dbro May 20, 2026
be60e0c
Security: document autofill delegate model and cross-profile channel
dbro May 20, 2026
1fe8c74
Security: clarify bookmarklet theft requires physical/OS access, not …
dbro May 20, 2026
d2eb823
Security: clarify drag-install threat is in the Portpass (FROM) profile
dbro May 20, 2026
cd75c8a
README/SECURITY: document Autofill feature
dbro May 20, 2026
cdbef96
StartPage: read-only fallback for browsers without showOpenFilePicker
dbro May 21, 2026
01240c2
relay: change default port from 7677 to 7577 (avoid IBM service confl…
dbro May 21, 2026
44e3cf3
Autofill: per-channel use counts, relay URL config, Advanced section
dbro May 21, 2026
5746c53
Dashboard: expect 204 from relay /pick when slot is empty
dbro May 21, 2026
baf7750
Dashboard: back off relay polling 30s after ERR_CONNECTION_REFUSED
dbro May 21, 2026
c8c59e9
Revert "Dashboard: back off relay polling 30s after ERR_CONNECTION_RE…
dbro May 21, 2026
a513342
Rename: relay → switchboard throughout JS/Svelte layer
dbro May 21, 2026
5ab0e2a
Autofill: replace HTTP polling with WebSocket for cross-profile path
dbro May 21, 2026
227bbac
Switchboard: replace HTTP /status probe with switchboardConnected store
dbro May 21, 2026
d67eea6
VaultSheet: fix a11y label association for switchboard URL input
dbro May 21, 2026
b8f2ed7
Dashboard: fix switchboard reconnect after restart
dbro May 21, 2026
fa68880
Autofill: cross-profile on/off toggle, default disabled
dbro May 21, 2026
c950c78
Autofill: update to generic pub/sub switchboard protocol
dbro May 21, 2026
ea4e989
README: reference standalone switchboard repo
dbro May 21, 2026
b080296
Autofill: PIN pairing ceremony on first bookmarklet use
dbro May 21, 2026
54ffcf5
Revert "Autofill: PIN pairing ceremony on first bookmarklet use"
dbro May 21, 2026
39701ae
Autofill: position overlay near first visible input when no field foc…
dbro May 21, 2026
a8e5ab0
SECURITY.md: browser extension supply-chain risk and audit guide
dbro May 21, 2026
67eaf43
SECURITY.md: clarify delegate key threat, fix switchboard URL
dbro May 21, 2026
018d50b
Autofill: live delegate use-count updates in VaultSheet
dbro May 21, 2026
ad9f6d2
Autofill: UX fixes for cross-profile toggle and error messages
dbro May 21, 2026
3d5aa26
Autofill: consistent same-profile/cross-profile picker behaviour
dbro May 21, 2026
5d29e25
Autofill UI: add semantic color tokens for redesign
dbro May 22, 2026
058ce59
Autofill UI: redesign bookmarklet install modal (Step 2)
dbro May 22, 2026
9fca533
Autofill UI: redesign page overlay (Step 3)
dbro May 22, 2026
1496f1d
Autofill UI: redesign relay.html picker (Step 4)
dbro May 22, 2026
b214f45
Autofill: popup positioned top-right; relay.html internal scroll fixed
dbro May 22, 2026
d1c940e
fix tooltip
dbro May 22, 2026
f724945
Autofill UI: unified relay.html popup — retire page overlay (Step 4.5)
dbro May 22, 2026
f2b9e3c
Autofill: show done phase after fill; relay accent matches Portpass t…
dbro May 22, 2026
9157dae
Autofill relay: always-available search toggle + URL-save fix + accent
dbro May 22, 2026
15e1d30
Autofill Step 5: RecordEdit visual chip builder for autotype sequences
dbro May 22, 2026
9ffa088
Autofill UI redesign: relay.html chip display + View in Portpass + te…
dbro May 22, 2026
7f32233
Autofill: rename relay.html→autofill.html, unify same/cross-profile t…
dbro May 22, 2026
b5eb0b5
Autofill: fix 'Vault is locked' false positive; improve connection er…
dbro May 23, 2026
f815907
Autofill: shared broadcast channel + event-driven probe
dbro May 23, 2026
c86c231
Detect external file modification before save
dbro May 23, 2026
229c538
Autofill: prefer BC over WS when same instance responds on both channels
dbro May 23, 2026
b293737
Autofill: accent-coloured done circle; fix noconn error for missing P…
dbro May 23, 2026
c5e7941
RecordEdit: reveal/hide notes that were withheld from the record
dbro May 23, 2026
07dfa14
Fix notes-reveal test: use createVault for the no-notes case
dbro May 23, 2026
d4ebd54
Autofill: hide on mobile, show default sequence, info icon when no de…
dbro May 23, 2026
4b239ee
Autofill: gate on pointer:fine instead of viewport width
dbro May 23, 2026
4929cae
Autofill: count fills on done screen, not on handshake
dbro May 23, 2026
c3a13c3
fix timestamp comparison in record view, and improve file-changed-sin…
dbro May 24, 2026
aeaa599
Fix silent truncation of custom fields beyond 9 and stale map-key com…
dbro May 24, 2026
e3d18ad
Fix two pre-existing test failures
dbro May 24, 2026
b6f9d45
Harden crypto code: panic on entropy failure, no partial key mutation…
dbro May 24, 2026
7e6afb6
Show vault format version as hex (e.g. 0x0310) not Password Safe app …
dbro May 24, 2026
3e69841
Debounce search query by 150ms to reduce redundant WASM calls
dbro May 24, 2026
a61ea0d
Change TOTP copy shortcut from Ctrl+T to Ctrl+Y to avoid browser new-…
dbro May 24, 2026
7bc1c8f
record view uses full width of window pane
dbro May 24, 2026
393af68
Supply chain and secondary vault hardening
dbro May 24, 2026
43fa3fa
Preserve unknown custom field property IDs for forward compatibility
dbro May 24, 2026
cad7ea9
Reorder aggregate stats to vaults · passwords · groups
dbro May 24, 2026
2086049
handle too-wide record fields
dbro May 25, 2026
6e98f7d
remove immediate biometric unlock on mobile
dbro May 25, 2026
897dafe
add header to remove interstitial page when running ngrok
dbro May 25, 2026
ab35024
Support read-only vaults on iOS and browsers without File System Acce…
dbro May 26, 2026
4bd90e8
Fix URL navigation opening Portpass instead of destination
dbro May 26, 2026
c364837
Fix autofill UI hidden on Windows touchscreen devices
dbro May 26, 2026
5908b60
Fix duplicate vault open corrupting WASM state
dbro May 26, 2026
b8808ad
Fix postMessage origin validation and remove debug log
dbro May 26, 2026
13eaf96
Add CSP and referrer policy to autofill.html
dbro May 26, 2026
3f909b7
Tighten postMessage targets and CSP base-uri
dbro May 26, 2026
c691b9e
Fix autofill.html CSP blocking its own inline script
dbro May 26, 2026
d05ffe8
Robustly normalize record URLs to absolute before opening
dbro May 26, 2026
4e29569
Make autotype \n action more robust
dbro May 27, 2026
a5b8f34
Minify bookmarklet IIFE via terser Vite plugin
dbro May 27, 2026
a59da6d
Fix Firefox not remembering recently opened vault
dbro May 27, 2026
fe7e26d
Polish read-only chip appearance in record view
dbro May 27, 2026
6381fea
Fix autofill UI hidden on Windows touchscreen devices
dbro May 27, 2026
9090799
Show toast notification when copying a field to clipboard
dbro May 27, 2026
f64d31c
update README with Autofill description and screenshots
dbro May 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ test-results/
design-wip/
CLAUDE.md
AUTOFILL.md
AUTOFILL-RESEARCH.md
AUTOCOPY.md
LAZYSENSITIVE.md
SUGGESTIONS.md
.claude/
/wasm
skills/
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ wasm_exec:
cp "$$(go env GOROOT)/lib/wasm/wasm_exec.js" ./pwa/public/

setup: wasm_exec
cd pwa && npm install
cd pwa && npm ci

build: wasm wasm_exec
cd pwa && npm run build
Expand Down
170 changes: 136 additions & 34 deletions README.md

Large diffs are not rendered by default.

80 changes: 80 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,28 @@ An extension with broad host permissions runs in the same process as every web p

This applies to every browser-based password manager and it is not a flaw specific to Portpass. It is a fundamental property of how browser extensions work.

### The supply-chain / auto-update risk — including Portpass itself

Extensions update silently and automatically. An extension that was safe when you installed it can be bought, transferred to a new owner, or hacked, and a subsequent update can make it malicious — without any visible change and without any action on your part. This attack has occurred with popular, widely-reviewed Chrome extensions. Even careful upfront vetting only tells you the extension was safe at that moment; you are extending ongoing trust in the publisher and in anyone who might later acquire them.

**Portpass is subject to the same risk.** It is a Progressive Web App served from GitHub Pages and it updates automatically when a new version is deployed. A compromised update — whether through the GitHub repository, the build pipeline, or a dependency in the npm or Go module graph — would run in the same browser context as your vault. The mitigations are real: Portpass is open-source and every change is publicly auditable; releases are tagged and deployed by the project maintainer; the encryption core is compiled from Go source whose history is visible. But these reduce risk rather than eliminating it. If you need higher assurance, treat Portpass the same way you would any critical piece of software: review updates before they apply, or use a dedicated profile that you update intentionally.

### How to audit extensions in your regular profile

Not all extensions are equally relevant to your vault. An extension that only has access to `reddit.com` cannot run on the Portpass page and cannot see your vault or your master password — it is simply not in scope. Only extensions with broad host permissions or specific access to the Portpass URL (`dbro.github.io`) matter for vault security.

**In Chrome:** open `chrome://extensions`, click **Details** on each extension, then scroll to **Permissions**. An extension that says "Read and change all your data on all websites" (or similar) can run on the Portpass page. An extension with a narrower list of sites can only run on those sites.

**In Firefox:** open `about:addons`, click an extension, then the **Permissions** tab. "Access your data for all websites" is the broad permission; narrower extensions will list specific domains.

Neither browser provides a consolidated view — you must check each extension individually.

Once you have identified which extensions have broad access, consider two distinct surfaces:

1. **Extensions that can run on the Portpass URL** (`dbro.github.io`): highest impact. They can observe your master password as you type it, read any revealed password in the UI, and — during the brief window when the "New bookmarklet" modal is open — read the delegate private key from the chip's link attribute. If any extension you cannot fully trust has this access, use a dedicated profile for Portpass instead.

2. **Extensions that can run on login pages**: they can read form field values after autofill. This is the same exposure that exists with every password manager, including native apps — an extension that runs on `bank.com` can read whatever was typed or filled into that page. Audit extensions on your sensitive login pages separately from the Portpass audit.

### Mitigation: use a dedicated browser profile

The most effective defense is to use Portpass in a separate browser profile that has no extensions installed.
Expand All @@ -56,6 +78,14 @@ Extensions are installed per-profile. A profile with no extensions has no extens

**Workflow:** Alt-tab to the Portpass window when you need a password, copy it, and paste it in your main browser. The 30-second clipboard autoclear limits the window during which a compromised extension could read it.

**Autofill and the dedicated profile.** Portpass supports two autofill modes that work alongside the dedicated clean-profile setup:

- **Same-profile autofill**: both Portpass and the page being filled are in the same browser profile. The bookmarklet opens a relay popup that communicates with Portpass via BroadcastChannel. This is more convenient but exposes Portpass to any extensions in that profile.

- **Cross-profile autofill**: Portpass runs in a clean, extension-free profile; the bookmarklet runs in your main browsing profile. Credentials travel via [switchboard](https://github.com/dbro/switchboard), a tiny local WebSocket broker (`localhost:7577`). This preserves full extension isolation — Portpass never touches the browsing profile. Requires switchboard to be running as a background service.

Both modes use the **delegate model**: each bookmarklet installation holds an ECDSA P-256 private key; the corresponding public key is registered in Portpass. Every autofill request is signed with the private key and verified by Portpass before any credentials are exchanged. This means a malicious extension on the page cannot impersonate a legitimate bookmarklet and trick Portpass into delivering credentials to an attacker's key.

---

## Vault file security
Expand Down Expand Up @@ -89,16 +119,66 @@ When secondary vaults are linked to a primary vault, their master passwords are

Portpass automatically clears the clipboard 30 seconds after you copy a password, reducing the window during which it can be read by another app.

### Clipboard sniffing is a universal risk for password managers

Any password manager that uses the clipboard to transfer passwords — including native apps such as 1Password and Bitwarden — shares this exposure. A browser extension with `clipboardRead` permission can call `navigator.clipboard.readText()` at any time and capture whatever is currently in the clipboard, regardless of which app placed it there. This applies equally to passwords copied from Portpass, from a native password manager, or typed by hand and then cut.

Portpass's 30-second autoclear and the Autocopy bookmarklet's immediate post-paste clear both reduce the exposure window, but neither can prevent an actively polling adversary from reading the clipboard before the clear fires.

**Checking which extensions have clipboard access.** In Chrome, open `chrome://extensions/` → Details → Permissions for each extension individually. In Firefox, go to `about:addons` → click the extension → Permissions tab. Neither browser provides a consolidated view; you must check each extension one by one. Any extension you do not recognise and trust that lists clipboard read access should be treated as a risk.

### Platform differences

Clipboard access is restricted at the OS level on **iOS, Android, and Linux (Wayland)**. On these platforms, only the foreground app can read the clipboard, so copied passwords are well-protected from background processes.

**macOS** restricts clipboard access for non-browser apps, but browser extensions running in the same profile can still read it. Use a dedicated browser profile with no extensions (see above).

### The better answer: passkeys

Passkeys (WebAuthn) eliminate the clipboard and extension risks entirely — there is no password to copy, intercept, or sniff. Authentication is a cryptographic challenge/response that never leaves your device. If a site you use offers passkey login, using it is the strongest choice available. Portpass is for sites that still require a password; for everything else, prefer your platform's passkey manager (iCloud Keychain, Google Password Manager, Windows Hello, etc.).

**Windows and Linux (X11)** have no OS-level clipboard isolation which means any running process can read the clipboard at any time. Users on these platforms should be especially careful to use the dedicated browser profile mitigation, and be aware that other apps may be able to read a copied password before the clipboard gets cleared.

---

## Autofill security

The Autofill bookmarklet uses a **delegate model** to create a cryptographically authenticated channel between the bookmarklet and Portpass, without any server infrastructure and without browser extensions.

### Trusted islands

- **Bookmarklet URL** — stored in the browser's bookmark store, which no web API can read or modify. Contains an ECDSA P-256 private key unique to this installation.
- **relay.html** — served from the Portpass HTTPS origin, cross-origin isolated from the page being autofilled. Receives the private key from the bookmarklet via `postMessage` with strict `targetOrigin`.
- **Portpass** — holds the registered public key for each delegate; verifies every autofill request signature before acting.

### Authentication

Before exchanging any credentials, relay.html signs a challenge `{relayNonce, ecdhSpki}` (same-profile) or `{url, nonce, ecdh, timestamp}` (cross-profile) with the delegate's ECDSA P-256 private key. Portpass verifies the signature against the registered public keys. A forged or unsigned request is silently rejected.

This prevents the masquerade attack: a malicious extension or page script running at the Portpass origin can observe the BroadcastChannel but cannot forge a valid ECDSA signature for a registered delegate's key.

### Credential encryption in transit

After authentication, credentials are encrypted with ECDH P-256 + AES-256-GCM. The session key is ephemeral — a fresh ECDH key pair is generated for each autofill session. Credentials in transit are ciphertext only; no key material appears on the channel.

### Cross-profile relay server

The switchboard (`localhost:7577`) is a dumb pipe — it stores and forwards encrypted blobs without inspecting content. It binds to `127.0.0.1` only and is not accessible over the network. An attacker who can read the relay server's memory has OS-level access and is outside the threat model.

### What autofill does not protect against

- **Credential in the DOM**: after filling, the credential is in `input.value` and readable by any extension on the page. This is identical to manual typing or any other password manager and cannot be avoided without browser-level APIs.
- **Delegate private key in the bookmark store**: the bookmark store is stored as a plaintext JSON file in the browser profile directory. Any process that can read that directory — including backup software, another user account with filesystem access, or malware with user-level permissions — can extract the private key. The key persists indefinitely, so an exfiltrated copy remains valid until the delegate is explicitly revoked in Portpass.

However, the key is an *authentication token*, not an encryption key. It does not contain or unlock any vault data on its own. An attacker who holds the key can only use it by making a signed request to a running, unlocked Portpass instance — and only via BroadcastChannel (same browser profile) or the local switchboard WebSocket (same machine). Both channels require the attacker to be active on the same machine at the same moment the user has Portpass open. At that point the attacker already has access to more direct attacks.

This makes the key fundamentally different from a password captured off the clipboard: a clipboard password is immediately and permanently usable anywhere; the delegate key requires an ongoing session to exploit and becomes worthless the moment the vault is locked.

The same property that protects the key — the bookmark store is inaccessible to web pages — also means Portpass cannot rotate it automatically. Periodic manual rotation (revoke the old delegate in VaultSheet, drag a new bookmarklet) is good hygiene, especially if you suspect a device may have been accessed by someone else. A future VaultSheet version may display key age to prompt rotation.
- **Extension present at drag-install time**: an extension running in the **Portpass profile** (the clean profile where the bookmarklet is dragged *from*) could modify the bookmarklet's JavaScript or substitute a different key before the drag completes. This is why the clean profile must have no extensions — not just to protect the vault, but to ensure the bookmarklet delivered to the bookmarks bar is genuine. Extensions in the *destination* browsing profile cannot intercept the bookmarklet because it arrives already stored in the bookmark store, which extensions cannot read.

---

## Implementation notes

- **Memory**: the salted password buffer used during key stretching is zeroed immediately after use.
Expand Down
44 changes: 20 additions & 24 deletions cmd/wasm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ func openDB(this js.Value, args []js.Value) interface{} {
}

uuid := vaultUUID(newDB)
databases[uuid] = newDB
if _, exists := databases[uuid]; !exists {
databases[uuid] = newDB
}

result, _ := json.Marshal(map[string]string{"uuid": uuid})
return string(result)
Expand Down Expand Up @@ -95,6 +97,7 @@ func getDBData(this js.Value, args []js.Value) interface{} {
UUID string `json:"uuid"`
Title string `json:"title"`
Group string `json:"group"`
URL string `json:"url"`
HasTOTP bool `json:"hasTOTP"`
}

Expand All @@ -104,6 +107,7 @@ func getDBData(this js.Value, args []js.Value) interface{} {
UUID: uuidHex,
Title: rec.Title,
Group: rec.Group,
URL: rec.URL,
HasTOTP: len(rec.TwoFactorKey) > 0,
})
}
Expand All @@ -128,6 +132,7 @@ type recordView struct {
Username string `json:"Username"`
URL string `json:"URL"`
Email string `json:"Email"`
Autotype string `json:"Autotype"`
ModTime string `json:"ModTime"`
Password *string `json:"Password"`
Notes *string `json:"Notes"`
Expand Down Expand Up @@ -159,7 +164,7 @@ func sensitiveString(s string) *string {
func recordToView(rec pwsafe.Record) recordView {
mt := ""
if !rec.ModTime.IsZero() {
mt = rec.ModTime.Format("2006-01-02")
mt = rec.ModTime.UTC().Format(time.RFC3339)
}

cfViews := make([]customFieldView, len(rec.CustomFields))
Expand All @@ -185,6 +190,7 @@ func recordToView(rec pwsafe.Record) recordView {
Username: rec.Username,
URL: rec.URL,
Email: rec.Email,
Autotype: rec.Autotype,
ModTime: mt,
Password: sensitiveString(rec.Password),
Notes: sensitiveString(rec.Notes),
Expand Down Expand Up @@ -232,21 +238,8 @@ func getDBInfo(this js.Value, args []js.Value) interface{} {
Iter uint32 `json:"iter"`
}

versionMap := map[uint16]string{
0x0300: "3.01", 0x0301: "3.03", 0x0302: "3.09", 0x0303: "3.12",
0x0304: "3.13", 0x0305: "3.14", 0x0306: "3.19", 0x0307: "3.22",
0x0308: "3.25", 0x0309: "3.26", 0x030A: "3.28", 0x030B: "3.29",
0x030C: "3.29", 0x030D: "3.30", 0x030E: "3.47", 0x030F: "3.68",
0x0310: "3.69",
}

versionVal := binary.LittleEndian.Uint16(db.Header.Version[:])
versionStr := versionMap[versionVal]
if versionStr == "" {
versionStr = fmt.Sprintf("Format 0x%04x", versionVal)
} else {
versionStr = "v" + versionStr
}
versionStr := fmt.Sprintf("0x%04X", versionVal)

info := DBInfo{
Version: versionStr,
Expand Down Expand Up @@ -306,6 +299,8 @@ func updateRecordFields(this js.Value, args []js.Value) interface{} {
rec.Email = value
case "Notes":
rec.Notes = value
case "Autotype":
rec.Autotype = value
case "TwoFactorKey":
if value == "" {
rec.TwoFactorKey = nil
Expand Down Expand Up @@ -354,13 +349,14 @@ func updateRecordFields(this js.Value, args []js.Value) interface{} {
cfs[i] = pwsafe.CustomField{Name: inp.Name, Sensitive: inp.Sensitive}
if inp.Value != nil {
cfs[i].Value = *inp.Value
} else {
// null = preserve existing value for this field name
for _, ex := range rec.CustomFields {
if ex.Name == inp.Name {
}
for _, ex := range rec.CustomFields {
if ex.Name == inp.Name {
if inp.Value == nil {
cfs[i].Value = ex.Value
break
}
cfs[i].UnknownProps = ex.UnknownProps
break
}
}
}
Expand Down Expand Up @@ -439,11 +435,11 @@ func searchRecords(this js.Value, args []js.Value) interface{} {
return "database not open"
}
if len(args) != 3 {
return "invalid arguments: expected (vaultUuid, query, namesOnly)"
return "invalid arguments: expected (vaultUuid, query, mode)"
}
query := args[1].String()
namesOnly := args[2].Bool()
uuids := db.Search(query, namesOnly)
mode := args[2].Int() // 0=all fields, 1=names only, 2=URL exact match
uuids := db.Search(query, mode)
jsonData, err := json.Marshal(uuids)
if err != nil {
return fmt.Sprintf("json marshal error: %s", err)
Expand Down
2 changes: 1 addition & 1 deletion pwa/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<!-- CSP: wasm-unsafe-eval required for WebAssembly compilation.
unsafe-inline needed for Svelte's dynamic inline styles. -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' ws://localhost:7577; font-src 'self'; object-src 'none'; base-uri 'none'; form-action 'self'" />
<meta name="referrer" content="no-referrer" />
<title>Portpass</title>
<script src="/wasm_exec.js"></script>
Expand Down
Loading
Loading