# PAM Workflow Security Hardening

## PAM Workflow Security Hardening

This page describes three security fixes shipped in Commander for the PAM workflow subsystem. The changes are transparent to end-users — no command syntax changes — but are important for administrators operating Commander in environments with debug logging enabled or strict enforcement policies.

> **Applies to:** Keeper Commander 17.3+ (included in release branch as of PR #2013)

***

### Summary of changes

| Severity | Area                                | What changed                                                                                                 |
| -------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| HIGH     | Debug log credential redaction      | `turn_password`, `callback_token`, and guacd secret fields are now masked before being written to debug logs |
| HIGH     | Enterprise boolean enforcement      | Empty `booleans=[]` enforcement lists now correctly deny the action, matching web-vault behaviour            |
| MEDIUM   | `pam workflow update` boolean flags | Non-`true`/`false` values now raise an error instead of silently coercing                                    |
| MEDIUM   | HHMM time-range decode              | `divmod(value, 60)` corrected to `divmod(value, 100)` for correct hour/minute extraction                     |

***

### 1. Credential redaction in debug logs

#### Background

When Commander is run with `--debug` or the Python `logging` level is set to `DEBUG`, internal state is written to stdout/log files. Before this fix, WebRTC connection parameters (including `turn_password` and `callback_token`) and guacd tunnel parameters (including `password`, `private-key`, `passphrase`, and `client-key`) were logged verbatim.

#### What changed

**File:** `keepercommander/commands/pam_launch/terminal_connection.py`

The following fields are now replaced with `***redacted***` before any `logging.debug()` call:

* **WebRTC settings:** `turn_password`, `callback_token`
* **guacd connection parameters:** `password`, `private-key`, `passphrase`, `client-key`

All other fields (non-sensitive host, port, protocol values) are logged unchanged so debugging remains useful.

#### Impact

No behavioural change for normal operation. Affects only what is written to debug-level log output. Administrators using SIEM ingestion of Commander logs should no longer see credential values appearing in `DEBUG` log events.

***

### 2. Strict deny for empty boolean enforcement lists

#### Background

Enterprise enforcement policies are delivered by the Keeper backend as a JSON object containing a `booleans` array. A missing key (`booleans` absent) means the policy is not configured and Commander allows the action defensively. An empty array (`booleans: []`) previously had the same allow-through behaviour — this was incorrect.

The web vault treats `booleans: []` as "enterprise context present, no relevant policy keys set" and denies by key-absence, not by list emptiness. Commander's behaviour was inconsistent with the web vault.

#### What changed

**Files:** `keepercommander/commands/workflow/helpers.py`, `keepercommander/commands/discoveryrotation.py`

Both `is_pam_action_allowed_by_enforcement` and `_is_rotation_allowed_by_enforcement` now use the following logic:

```
booleans = enforcements.get('booleans')
if booleans is None:
    # Key fully absent — policy not configured; allow defensively.
    return True
if not isinstance(booleans, list):
    # Malformed payload — allow defensively.
    return True
# Empty list: enterprise context present, no boolean keys set.
# Fall through to key lookup; if key absent → deny (matches web vault).
```

#### Impact

Environments with enterprise enforcement policies configured may see actions blocked that previously allowed through when `booleans` was present but empty. This aligns Commander with web-vault enforcement semantics. If you observe unexpected denials after upgrading, check your enterprise enforcement policy configuration in the Keeper Admin Console.

***

### 3. Strict boolean argument parser for `pam workflow update`

#### Background

The `pam workflow update` command accepts several boolean flags:

```
pam workflow update <uid> --enabled <value> --skip-mfa <value> ...
```

Previously, any truthy string (including `yes`, `1`, `on`) was silently coerced to `True`. This could mask configuration mistakes.

#### What changed

**File:** `keepercommander/commands/workflow/config_commands.py`

The boolean argument parser now accepts **only** the literal strings `true` and `false` (case-insensitive). Any other value raises `argparse.ArgumentTypeError` with a clear message.

**Before (was accepted, now rejected):**

```
pam workflow update <uid> --enabled yes      # rejected
pam workflow update <uid> --skip-mfa 1       # rejected
pam workflow update <uid> --enabled on       # rejected
```

**After (accepted):**

```
pam workflow update <uid> --enabled true
pam workflow update <uid> --enabled false
pam workflow update <uid> --skip-mfa True    # case-insensitive
```

#### Impact

Scripts using non-standard boolean strings (`yes`, `1`, `on`, `no`, `0`, `off`) must be updated to use `true` or `false`. This is a **breaking change** for any automation using these argument values.

***

### 4. HHMM time-range decode fix

#### Background

`pam workflow config show` displays the `allowedTimes` window configured on a workflow — the days and time ranges during which `pam launch` is permitted. Time range start and end values are stored by the server as HHMM integers (e.g. `17:30` → `1730`).

The display formatter used `divmod(value, 60)` to extract hours and minutes. This is the correct formula for seconds-to-minutes conversion but not for HHMM integers: `divmod(1730, 60)` yields `(28, 50)` instead of the correct `(17, 30)`.

#### What changed

**File:** `keepercommander/commands/workflow/helpers.py`

```python
# Before (incorrect):
start_h, start_m = divmod(tr.startTime, 60)

# After (correct):
start_h, start_m = divmod(tr.startTime, 100)
```

#### Impact

The displayed allowed-times window in `pam workflow config show` now shows the correct human-readable time. No runtime behaviour is changed — only the display output. Administrators who previously saw unexpected hours/minutes in the allowed-times display should re-check their workflow configuration to confirm it matches their intent.

***

### Verifying the changes

After upgrading Commander, you can confirm the security fixes are active:

**Credential redaction:** Enable debug logging (`--debug`) and initiate a PAM tunnel session. Confirm that `turn_password`, `callback_token`, and guacd credential fields appear as `***redacted***` in the output.

**Enforcement deny fix:** With an enterprise session, run `pam workflow list` on a configuration whose enterprise enforcement policy has `booleans: []`. Confirm that restricted actions are denied as expected.

**Boolean arg parser:** Run `pam workflow update <uid> --enabled yes` and confirm Commander reports an argument error.

**HHMM display fix:** Run `pam workflow config show <uid>` on a workflow with an `allowedTimes` window and confirm the displayed start and end times match what is configured in the Admin Console.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.keeper.io/keeperpam/commander-cli/command-reference/keeperpam-commands/pam-workflow-security-hardening.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
