Class CommandExternalTokenProvider
- All Implemented Interfaces:
ExternalTokenProvider
This is the escape hatch for every credential shape nobody standardised: a vendor CLI that already knows how to authenticate, a hardware-backed agent, a site-specific broker. It is also, unavoidably, arbitrary code execution with the calling process's privileges, and it is built accordingly.
Five controls, and why each one is here
- An environment-only opt-in.
ANKASECURE_ALLOW_EXTERNAL_TOKEN_COMMANDmust be exactly1in the process environment. It is deliberately NOT readable fromcli.propertiesor any other configuration file, so a configuration file alone can never cause this process to spawn another one. Google gates executable-sourced credentials the same way and for the same reason, with the same environment-only rule; CVE-2026-66902 is what an implementation of the same feature looks like when the gate is missing. - No shell. The configured command is tokenized here into an argv array and handed to
ProcessBuilderdirectly. There is nosh -cand nocmd /c, so a semicolon, a backtick or a pipe character in a configuration value is an argument, not a second command. - A bounded timeout that is always applied. A command that hangs would otherwise hang the calling workload with no diagnosis at all; the process is destroyed forcibly and the failure names the command and the elapsed budget.
- Bounded output. Standard output is read up to the same ceiling any token must satisfy, and standard error is drained concurrently and discarded so a chatty command cannot deadlock on a full pipe buffer.
- Nothing the server returns reaches the command. The argv is fixed at construction and is immutable thereafter. This is the containment property: the SDK executes what the operator configured and can execute nothing else.
The contract with the command
Print the token to standard output and exit 0. Nothing else is parsed: no JSON
envelope, no version field, no expiry. An expiry would have to be believed, and the SDK does not
read the external token's claims - the token is opaque to it, deliberately. The lifetime that
governs caching is the one on the ANKASecure token the platform issues in exchange, which the
platform states outright.
Standard error is not surfaced, and that is a decision
A failure reports the command and the exit status but never the text the command wrote to standard error. Surfacing it is the usual practice and it is genuinely more convenient, but the SDK cannot know that a given command does not echo the credential it just minted, and the rule that secrets are never logged is worth more than a better error message. An operator diagnosing a failing command should run it themselves, where its output is theirs to read.
The whole argv is named, not just the executable
Interpreter-plus-script is the ordinary shape of this setting - bash get-token.sh, a
vendor CLI with a subcommand, a broker with a profile name - so naming only argv[0] tells
the operator the interpreter is broken when the missing artifact is the script. Every element of
the argv is reported instead.
That is not a relaxation of the rule above. The argv is what the operator wrote into their own
configuration and what this process hands to ProcessBuilder; the operating system's own
process listing already shows it verbatim. Standard error is the command's OUTPUT, which the SDK
has no way to vet - the distinction is between the configuration and what running it produced,
and only the second one can carry a minted credential.
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringThe one value that enables execution.static final StringThe environment variable that must be set to1before any command will be executed.static final DurationApplied when the configuration names no timeout.static final DurationA configured timeout above this is lowered to it.static final DurationA configured timeout below this is raised to it; a command needs some room to start. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionargv()The exact argv this provider will execute.describe()A short operator-facing description of WHERE the token comes from - a path, a command name, an issuer - for diagnostics.static booleanTrue when the process environment carries the opt-in.Produces the current external token.kind()Which of the configured source kinds this provider is, ornullfor a consumer-supplied provider that is none of them.timeout()The bounded budget actually in force after clamping.Splits a configured command line into an argv array WITHOUT involving a shell.toString()Names the configured command and the budget; carries nothing the command produced.Methods inherited from class java.lang.Object
equals, getClass, hashCode, notify, notifyAll, wait, wait, waitMethods inherited from interface co.ankatech.ankasecure.sdk.auth.ExternalTokenProvider
close, invalidate
-
Field Details
-
ALLOW_ENV_VARIABLE
The environment variable that must be set to1before any command will be executed. Environment only: it is never read from a properties file.- See Also:
-
ALLOW_ENV_VALUE
The one value that enables execution. Anything else - includingtrue- does not.- See Also:
-
DEFAULT_TIMEOUT
Applied when the configuration names no timeout. -
MIN_TIMEOUT
A configured timeout below this is raised to it; a command needs some room to start. -
MAX_TIMEOUT
A configured timeout above this is lowered to it.
-
-
Constructor Details
-
CommandExternalTokenProvider
- Parameters:
argv- the command and its arguments, already tokenized; copied defensivelytimeout- how long the command may take; clamped to [MIN_TIMEOUT,MAX_TIMEOUT], defaulted whennull- Throws:
NullPointerException- ifargvisnullIllegalArgumentException- ifargvis empty or its first element is blank
-
-
Method Details
-
tokenize
Splits a configured command line into an argv array WITHOUT involving a shell.Whitespace separates arguments; a double-quoted run is one argument and may contain whitespace. Nothing else is interpreted - no variable expansion, no globbing, no operators - because every one of those is a way for a configuration value to mean more than it says.
- Parameters:
commandLine- the configured command line- Returns:
- the argv; never empty
- Throws:
NullPointerException- ifcommandLineisnullIllegalArgumentException- if the line is blank or has an unterminated quote
-
executionAllowed
public static boolean executionAllowed()True when the process environment carries the opt-in.Reads
System.getenv(java.lang.String)directly and takes no argument a caller could substitute: the gate is a property of the process, and a gate that can be handed its own answer is a gate with an override.- Returns:
trueif commands may be executed
-
fetchExternalToken
Description copied from interface:ExternalTokenProviderProduces the current external token.How current it is belongs to the implementation, and the two answers are both correct for their case. A provider that READS an out-of-band token re-reads on every call, because something else refreshes it and a copy held here is a copy that expires without anyone noticing. A provider that OBTAINS the token itself knows the lifetime its issuer stated for it, so it may reuse one inside that lifetime - and when the issuer states no lifetime, it falls back to fetching per call rather than guessing one.
- Specified by:
fetchExternalTokenin interfaceExternalTokenProvider- Returns:
- the token; the caller takes ownership and closes it
-
kind
Description copied from interface:ExternalTokenProviderWhich of the configured source kinds this provider is, ornullfor a consumer-supplied provider that is none of them.- Specified by:
kindin interfaceExternalTokenProvider- Returns:
- the kind, or
nullwhen the provider is the consumer's own
-
describe
Description copied from interface:ExternalTokenProviderA short operator-facing description of WHERE the token comes from - a path, a command name, an issuer - for diagnostics. It must never contain the token.- Specified by:
describein interfaceExternalTokenProvider- Returns:
- the description; never
null
-
argv
The exact argv this provider will execute. Exposed so a containment check can assert that what runs is what was configured.- Returns:
- an immutable copy of the argv; never empty
-
timeout
The bounded budget actually in force after clamping.- Returns:
- the timeout; never
null
-
toString
Names the configured command and the budget; carries nothing the command produced.
-