Documentation
¶
Overview ¶
Package importer reads secrets from foreign password managers (Bitwarden, 1Password, LastPass, Chrome, Firefox, Enpass, KeePass, pass, gopass) and converts them into entries suitable for a pass-format store.
The core abstraction is the Importer interface: each format implements Name (for display), Detect (content-based auto-detection), and Import (streaming conversion). A Registry holds all known importers and resolves the right one from an input file.
Entry is the intermediate representation: it carries the richer structure of foreign formats (title, username, URL, group, TOTP, attachments) and is mapped onto a store path and a pass-format secret by the export step.
Usage:
Import a file with auto-detection:
registry := importer.NewRegistry() imp, _, _ := registry.DetectReader(file) entries, _ := importer.ImportAll(imp, file) importer.WriteEntries(store, entries, false)
Plan without writing (dry-run):
plans := importer.Plan(store, entries) fmt.Println(importer.FormatPlan(plans))
Package importer reads secrets from foreign password managers and converts them into entries suitable for a pass-format store.
The core abstraction is the Importer interface: each supported format implements Name (for display), Detect (content-based auto-detection), and Import (streaming conversion). A Registry holds all known importers and resolves the right one from an input file.
Entry is the intermediate representation: it carries the richer structure of foreign formats (title, username, URL, group, TOTP, attachments) and is later mapped onto a store path and a pass-format secret by the export step.
Index ¶
- func DeduplicatePaths(paths []string) []string
- func FormatPlan(plans []PlanEntry) string
- func NormalizePath(group, title string) string
- func ValidatePath(p string) bool
- func WriteEntries(s *store.Store, entries []*Entry, force bool) ([]string, []error)
- type Attachment
- type BitwardenImporter
- type ChromeImporter
- type EnpassImporter
- type Entry
- type Field
- type FirefoxImporter
- type Importer
- type KeePassImporter
- type LastPassImporter
- type OnePasswordImporter
- type PassImporter
- type PlanEntry
- type Registry
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DeduplicatePaths ¶
DeduplicatePaths resolves duplicate paths by appending a numeric suffix. Returns a slice of deduplicated paths in the same order as input. The first occurrence keeps its original name; subsequent ones get "-2", "-3", etc.
func FormatPlan ¶
FormatPlan formats a dry-run plan for display.
func NormalizePath ¶
NormalizePath converts a title and optional group from a foreign format into a valid store path.
Rules:
- Group and title are joined with '/'.
- Whitespace is collapsed and trimmed.
- Characters invalid in filenames are replaced with '_'.
- The result is lowercased for consistency (matching pass conventions).
- Empty segments are dropped.
- The path is cleaned to prevent directory traversal.
func ValidatePath ¶
ValidatePath reports whether p is a safe store path: non-empty, no traversal, no leading slash, and every segment is a valid filename.
Types ¶
type Attachment ¶
type Attachment struct {
// Name is the filename of the attachment.
Name string
// Data is the raw binary content.
Data []byte
}
Attachment is a binary blob from a foreign format, stored as a separate base64-encoded entry in the store.
type BitwardenImporter ¶
type BitwardenImporter struct{}
BitwardenImporter reads CSV exports from the Bitwarden password manager.
Bitwarden CSV columns vary between versions. Known column names are mapped by csvHeaderIndex so that the code works regardless of column order.
func (BitwardenImporter) Detect ¶
func (BitwardenImporter) Detect(r io.Reader) bool
Detect reports whether r looks like a Bitwarden CSV export.
func (BitwardenImporter) Name ¶
func (BitwardenImporter) Name() string
Name returns the format name.
type ChromeImporter ¶
type ChromeImporter struct{}
ChromeImporter reads CSV exports from Google Chrome's password manager.
Chrome exports use: name,url,username,password,note The first line may contain a explanatory prefix that must be skipped.
func (ChromeImporter) Detect ¶
func (ChromeImporter) Detect(r io.Reader) bool
Detect reports whether r looks like a Chrome CSV export.
type EnpassImporter ¶
type EnpassImporter struct{}
EnpassImporter reads CSV exports from Enpass.
Enpass exports vary by version. Common column names include "Title", "User Name", "Password", "Web Site", "Remarks", "Group", and custom fields as additional columns.
func (EnpassImporter) Detect ¶
func (EnpassImporter) Detect(r io.Reader) bool
Detect reports whether r looks like an Enpass CSV export.
type Entry ¶
type Entry struct {
// Title is the entry name as reported by the source. It is used as the
// basis for the store path after normalisation.
Title string
// Path overrides Title as the store path. When set, the export step uses
// it directly instead of deriving a path from Title + Group.
Path string
// Group is the folder or category the entry belongs to in the source
// manager (e.g. "Finance/Banks"). It is joined with Title to form the
// store path when Path is empty.
Group string
// Password is the primary secret.
Password string
// Username is the login name, written as a "username:" field.
Username string
// URL is the associated web address, written as a "url:" field.
URL string
// Notes is free-form text appended after structured fields.
Notes string
// TOTPURI is an otpauth:// URI, if the source carries a TOTP secret.
// Importers that find TOTP secrets in other representations (e.g. a
// separate secret field) must convert them to an otpauth:// URI.
TOTPURI string
// Fields are additional "key: value" pairs not covered by the structured
// fields above.
Fields []Field
// Attachments are binary blobs from the source (e.g. KDBX attachments).
// They are stored as "name.b64" entries with base64-encoded content,
// following gopass convention (§1 of ARCHITECTURE.md).
Attachments []Attachment
}
Entry is a single secret extracted from a foreign format, before it has been mapped to a store path and a pass-format secret.
An importer populates whichever fields its source provides; the export step ignores empty fields. Password is the only truly required field — even an empty password is valid, because the user may intend to edit the entry later.
func ImportAll ¶
ImportAll reads every entry from an importer, stopping at the first error that is not attached to an entry.
func (*Entry) AttachmentStorePath ¶
AttachmentStorePath returns the store path for attachment i, using the gopass convention of "name.b64" (§1 of ARCHITECTURE.md).
type Field ¶
type Field struct {
// Name is the field label.
Name string
// Value is the field content.
Value string
}
Field is a key-value pair that becomes a "key: value" line in the secret.
type FirefoxImporter ¶
type FirefoxImporter struct{}
FirefoxImporter reads CSV exports from Firefox Lockwise / Firefox Password Manager.
Firefox exports use: url,username,password,httpRealm,formActionOrigin,guid,timeCreated,timeLastUsed,timePasswordChanged Only url, username, and password carry secret data; the rest are metadata.
func (FirefoxImporter) Detect ¶
func (FirefoxImporter) Detect(r io.Reader) bool
Detect reports whether r looks like a Firefox CSV export.
type Importer ¶
type Importer interface {
// Name returns the human-readable name of the source format (e.g.
// "Bitwarden", "KeePass").
Name() string
// Detect reports whether r contains this format. The caller must ensure
// that r is positioned at the start; the implementation must not consume
// more bytes than necessary for detection. For binary formats (KDBX), the
// caller should re-open the file for Import.
Detect(r io.Reader) bool
// Import reads r and yields entries. The iterator stops when the reader is
// exhausted or an error makes further progress impossible. A non-nil error
// on the value channel means the entry may be partially usable; the caller
// decides whether to keep it. A non-nil error as the final yield means the
// import aborted.
Import(r io.Reader) iter.Seq2[*Entry, error]
}
Importer reads a foreign format and yields entries as a stream.
type KeePassImporter ¶
type KeePassImporter struct {
// Password is the database master password. It must be set before
// calling Import. The caller is responsible for reading it from the
// terminal.
Password string
}
KeePassImporter reads KDBX (KeePass 2.x) databases.
KDBX is a binary format, so Detect checks for the KDBX magic bytes and Import re-reads the file. The caller should re-open the file for Import.
The database password is read from TTY/stdin, never from argv (§3 of the project rules). Pass the password via the Password field before calling Import.
func (KeePassImporter) Detect ¶
func (KeePassImporter) Detect(r io.Reader) bool
Detect reports whether r looks like a KDBX file by checking the magic signature bytes. The KDBX 3.1 and 4.0 signatures both start with 0x03 0xd9 0xa2 0x96.
type LastPassImporter ¶
type LastPassImporter struct{}
LastPassImporter reads CSV exports from LastPass.
LastPass exports use columns: url,username,password,extra,name,grouping,fav The "extra" field contains notes. "grouping" maps to the store group. LastPass may encode the CSV in the system's default encoding rather than UTF-8, which is why csvReadAll handles encoding detection.
func (LastPassImporter) Detect ¶
func (LastPassImporter) Detect(r io.Reader) bool
Detect reports whether r looks like a LastPass CSV export.
type OnePasswordImporter ¶
type OnePasswordImporter struct{}
OnePasswordImporter reads CSV exports from 1Password.
1Password exports vary by vault type. The "Login" export has columns like Title, Username, Password, URL, OTP, Notes. The "1Password 8" format uses slightly different names.
func (OnePasswordImporter) Detect ¶
func (OnePasswordImporter) Detect(r io.Reader) bool
Detect reports whether r looks like a 1Password CSV export.
func (OnePasswordImporter) Name ¶
func (OnePasswordImporter) Name() string
Name returns the format name.
type PassImporter ¶
type PassImporter struct {
// Dir is the path to the source pass store. If empty, the importer
// reports an error at import time.
Dir string
}
PassImporter reads entries from an existing pass or gopass store on disk. It does not decrypt; it copies the ciphertext, which the export step can then re-encrypt for the target store.
Because pass stores use the same format as binpass, this importer is also the basis for "binpass import pass /path/to/other/store".
func (PassImporter) Detect ¶
func (PassImporter) Detect(_ io.Reader) bool
Detect reports whether the reader looks like a pass store. A pass store is identified by its directory structure (a tree of .gpg or .age files with a .gpg-id or .age-recipients file), not by a single file's content, so Detect always returns false when called on a stream. Use PassImporter with Dir set instead.
func (*PassImporter) Import ¶
Import walks the source store directory and yields entries. It reads the ciphertext from each entry but does not decrypt it; the Password field is left empty and the raw ciphertext is carried in a special field so that the export step can re-encrypt without needing the source store's keys.
type PlanEntry ¶
type PlanEntry struct {
// StorePath is the path where the entry will be stored.
StorePath string
// Title is the original entry title from the source.
Title string
// HasPassword reports whether the entry has a non-empty password.
HasPassword bool
// HasTOTP reports whether the entry carries a TOTP secret.
HasTOTP bool
// AttachmentCount is the number of attachments.
AttachmentCount int
// Conflict reports that an entry with the same path already exists in the
// store.
Conflict bool
}
PlanEntry is the result of planning an import: it shows what will be written without actually writing anything. Used by --dry-run.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds known importers and resolves the right one for an input.
func NewRegistry ¶
func NewRegistry() *Registry
NewRegistry returns a registry with the standard set of importers.
func (*Registry) Detect ¶
Detect reads the beginning of r and returns the first importer that recognises the content. The caller should re-open the file for Import, because Detect consumes an unspecified number of bytes.
If no importer matches, it returns nil.
func (*Registry) DetectReader ¶
DetectReader is like Detect but reads from an io.Reader. It buffers up to peekSize bytes for detection.