Level up your business security with free, on-demand training and certification. Explore 1Password Academy today →
Forum Discussion
zondi
6 days agoFrequent Contributor
op item get --format json returns CONCEALED field values without --reveal flag
Hi,
We are using the 1Password CLI heavily in scripted and AI-agent workflows where command output is captured into logs. Today we discovered a behavior in op item get that surprised us, and we'd like to confirm whether it is intentional or a candidate for change.
What we observed
We ran the same item retrieval six different ways against a CONCEALED field on a test API Credential item we created specifically for this verification:
| # | Command | Returned the secret value? |
|---|---|---|
| 1 | op read "op://<vault>/<test-item>/credential" | YES (as designed) |
| 2 | op item get "<test-item>"` (default text format, no --reveal) | NO — output: credential: [use op item get ID --reveal to reveal]` |
| 3 | op item get "<test-item>" --format json (no --reveal) | YES — JSON output included "value": "<plaintext>" for the CONCEALED field |
| 4 | op item get "<test-item>" --fields credential (no --reveal) | NO — output: [use op item get ID --reveal to reveal] |
| 5 | op item get "<test-item>" --fields credential --reveal | YES (as expected — --reveal was passed) |
| 6 | op item get "<test-item>" --reveal | YES (as expected --reveal was passed) |
The surprise is #3: --format json silently returned the plaintext value even though no --reveal flag was passed. The text format (#2 and #4) clearly conceals the value with the message [use op item get ID --reveal to reveal], which strongly implies that --reveal is required to see the value. JSON output behaves differently.
Why this matters to us
Many agent and scripting workflows use --format json for parseability. A developer or AI agent might reasonably:
- Read the documentation, see that --reveal is needed to expose secrets
- Write op item get <id> --format json expecting metadata only
- Pipe the output to a JSON parser
- Inadvertently leak the secret value into logs, conversation context, or downstream tools
We discovered this in the context of investigating a credential-exposure incident where an AI agent was capturing tool output into conversation logs. We ran the test against a throwaway test credential to verify the behavior before drawing any conclusions. The text-format default was safe; the JSON format was not.
Our questions
- Is --format json returning plaintext for CONCEALED fields without --reveal an intentional design choice? If yes, what is the rationale? The asymmetry between text and JSON output is what surprised us.
- If intentional, would you consider documenting it more prominently? The current op item get documentation does not (as far as we can tell) warn that JSON format bypasses the --reveal requirement that the text format implies.
- If unintentional, would you consider concealing values in JSON output too (e.g., omitting the value field for CONCEALED fields, or replacing it with a sentinel like `"value": "<concealed>") unless --reveal is explicitly passed?
Secondary feedback (lower priority)
- op read always returns the value to stdout. This is documented and we understand it is the command's purpose. But for AI-agent contexts, it would be useful to have a --check or --exists mode that verifies a path resolves without fetching the value. (Workaround: `op item get` with the vault/item portion of the path resolves the existence question without exposing the value, as long as we don't use --format json — see issue above.)
- op item list` is correctly metadata-only. We tested this exhaustively and confirmed it does not return values. However, the metadata it returns (item names, IDs, vault names, edit timestamps) is sensitive reconnaissance data in agent contexts. Could you consider adding a --minimal or --counts-only mode that returns just count: N for the matched query without surfacing item names? This would let workflows verify that a credential exists in a given vault without enumerating the inventory.
- op run masking is excellent and we love it. The automatic <concealed by 1Password> replacement on stdout/stderr from wrapped subprocesses is the gold standard. We've made op run the canonical pattern for any subprocess that needs credentials in our workspace. The --no-masking flag is correctly an opt-in rather than the default. Thank you for the careful design here.
What we are doing in the meantime
We have updated our workspace policy and AI-agent rule files to:
- Treat op item get --format json as a value-leaking command, on par with --reveal
- Use op run --env-file=<file> as the default credential injection pattern
- Use op read --out-file <path> whenever a credential needs to land on disk, never piping op read to stdout
- Never use op item list or any enumeration command in tool-output-capturing contexts
We would still very much like your guidance on item #3 specifically, JSON format silently returning concealed values is the behavior that most worries us, because it is the easiest one to get wrong by accident.
Thanks
No RepliesBe the first to reply