Implement exec-file delivery
Write the selected field to a mode-0600 temp file, inject FIELD_FILE for the child only, then overwrite and unlink on every exit path. The value is not copied into the child environment. Assistant: grok Assistant-Session: 01a05f07-ae72-7781-9fcb-19efd61add00
This commit is contained in:
parent
afd1c8e593
commit
ce1790f267
7 changed files with 126 additions and 19 deletions
|
|
@ -10,7 +10,7 @@ Command surface (FR7):
|
|||
verify <catalog-id> [--positive] [--negative] [--field NAME] [--negative-token-file F]
|
||||
handoff <catalog-id> --stage <stage> --role-id-file F --secret-id-file F
|
||||
wrap <catalog-id> --out F [--ttl 15m]
|
||||
exec --catalog <catalog-id> [--field NAME] [--mode auto|npm-config|exec-env] -- CMD...
|
||||
exec --catalog <catalog-id> [--field NAME] [--mode auto|npm-config|exec-env|exec-file] -- CMD...
|
||||
route <catalog-id> [--json]
|
||||
revoke <catalog-id>
|
||||
session revoke --accessor-file F [--stage stage]
|
||||
|
|
@ -840,7 +840,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
ex = sub.add_parser("exec", help="run a command with the secret injected for the child only")
|
||||
ex.add_argument("--catalog", required=True)
|
||||
ex.add_argument("--field", default=None)
|
||||
ex.add_argument("--mode", default="auto", choices=("auto", "npm-config", "exec-env"))
|
||||
ex.add_argument("--mode", default="auto", choices=("auto", "npm-config", "exec-env", "exec-file"))
|
||||
add_token_arg(ex)
|
||||
ex.add_argument("command", nargs=argparse.REMAINDER,
|
||||
help="command after '--'")
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ Supported here:
|
|||
- npm-config: write a temporary .npmrc with the auth token and point the child
|
||||
at it via NPM_CONFIG_USERCONFIG. Preferred for `npm publish`.
|
||||
- exec-env: inject the value as an environment variable for the child only.
|
||||
- exec-file: write the value to a mode-0600 temp file and inject its path.
|
||||
|
||||
The parent shell never sees the value; the value is never logged. Child stdout/
|
||||
stderr is streamed through a redactor as a backstop.
|
||||
|
|
@ -119,10 +120,47 @@ def _npm_userconfig(registry: str, scope: str, token_env: str) -> Iterator[Path]
|
|||
fh.write(f"{authkey}:_authToken=${{{token_env}}}\n")
|
||||
yield path
|
||||
finally:
|
||||
_unlink_secret_file(path)
|
||||
|
||||
|
||||
def _unlink_secret_file(path: Path) -> None:
|
||||
"""Overwrite then unlink a secret file. Best-effort; never raises."""
|
||||
try:
|
||||
if path.is_file():
|
||||
size = path.stat().st_size
|
||||
with path.open("r+b") as fh:
|
||||
fh.write(b"\0" * max(size, 1))
|
||||
fh.flush()
|
||||
os.fsync(fh.fileno())
|
||||
path.unlink()
|
||||
except FileNotFoundError:
|
||||
return
|
||||
except OSError:
|
||||
try:
|
||||
path.unlink()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
except OSError:
|
||||
return
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _secret_file(value: str) -> Iterator[Path]:
|
||||
"""Write the value to a mode-0600 temp file; overwrite and unlink on exit."""
|
||||
fd, name = tempfile.mkstemp(prefix="se-exec-", suffix=".tmp")
|
||||
path = Path(name)
|
||||
try:
|
||||
os.fchmod(fd, 0o600)
|
||||
with os.fdopen(fd, "w", encoding="utf-8") as fh:
|
||||
fd = -1
|
||||
fh.write(value)
|
||||
fh.write("\n")
|
||||
yield path
|
||||
finally:
|
||||
if fd >= 0:
|
||||
try:
|
||||
os.close(fd)
|
||||
except OSError:
|
||||
pass
|
||||
_unlink_secret_file(path)
|
||||
|
||||
|
||||
def _stream_redacted(proc: subprocess.Popen, secret: str) -> None:
|
||||
|
|
@ -156,9 +194,14 @@ def exec_with_secret(
|
|||
|
||||
declared = set(entry.delivery_modes)
|
||||
if mode == "auto":
|
||||
mode = "npm-config" if "npm-config" in declared else (
|
||||
"exec-env" if "exec-env" in declared else ""
|
||||
)
|
||||
if "npm-config" in declared:
|
||||
mode = "npm-config"
|
||||
elif "exec-env" in declared:
|
||||
mode = "exec-env"
|
||||
elif "exec-file" in declared:
|
||||
mode = "exec-file"
|
||||
else:
|
||||
mode = ""
|
||||
if not mode:
|
||||
raise DeliveryError(
|
||||
f"lane '{entry.id}' declares no exec-capable delivery mode "
|
||||
|
|
@ -200,6 +243,12 @@ def exec_with_secret(
|
|||
child_env[env_name] = value
|
||||
return _spawn(command, child_env, value)
|
||||
|
||||
if mode == "exec-file":
|
||||
env_name = f"{field.upper()}_FILE"
|
||||
with _secret_file(value) as secret_path:
|
||||
child_env[env_name] = str(secret_path)
|
||||
return _spawn(command, child_env, value)
|
||||
|
||||
raise DeliveryError(f"unsupported delivery mode '{mode}'")
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue