Skip to content

Harden QLC CLI and smoke wiring#6

Merged
SeCuReDmE-main-dev merged 1 commit into
mainfrom
new/security-remediation-refactor-20260625
Jun 25, 2026
Merged

Harden QLC CLI and smoke wiring#6
SeCuReDmE-main-dev merged 1 commit into
mainfrom
new/security-remediation-refactor-20260625

Conversation

@SeCuReDmE-main-dev

Copy link
Copy Markdown
Owner

Summary

  • add optional Bouncy Castle bcctl perimeter adapter for digest-only signing and verification
  • add bc-status, bc-sign, bc-verify, and opt-in protect-workflow perimeter receipt wiring
  • document the provider boundary and add focused adapter/CLI/workflow tests

Validation

  • python -m pytest tests\test_bc_perimeter.py tests\test_workflow.py tests\test_cli_qlc.py
  • python -m pytest
  • live bc-status smoke with local bcctl provider

Public-safe boundary

  • bcctl receives only key IDs and metadata digests
  • no raw payloads, plaintext, passphrases, API keys, private corpus text, or local paths are emitted in public status output

@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an optional Bouncy Castle perimeter signature layer to workflow metadata via a local bcctl provider, adding new CLI commands (bc-status, bc-sign, bc-verify), integration into the workflow generation, documentation, and unit tests. The review feedback highlights several robust improvements for the new bc_perimeter.py module, including generalizing the path redaction regex to support Unix absolute paths, explicitly specifying utf-8 encoding in subprocess.run to prevent platform-specific decoding errors, falling back to shutil.which for command-name configurations, and adding type checks to validation functions to avoid potential AttributeError exceptions.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

_SECRETISH_RE = re.compile(
r"(?i)(api[_-]?key|authorization|bearer|credential|password|passphrase|private[_-]?key|secret|token)"
)
_WINDOWS_PATH_RE = re.compile(r"[A-Za-z]:/[^\s\"']+")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current _WINDOWS_PATH_RE regex only matches Windows-style absolute paths (e.g., C:/Users/...). Absolute Unix paths (e.g., /home/user/... or /var/log/...) are not matched or redacted, which could leak local filesystem paths in public status outputs. Let's generalize this regex to match both Windows and Unix absolute paths.

Suggested change
_WINDOWS_PATH_RE = re.compile(r"[A-Za-z]:/[^\s\"']+")
_PATH_RE = re.compile(r"(?:[A-Za-z]:)?/(?:[^/\s\"\']+/)*[^/\s\"\']+")

Comment on lines +153 to +160
completed = subprocess.run(
command,
shell=False,
check=False,
capture_output=True,
text=True,
timeout=self.timeout_seconds,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Calling subprocess.run with text=True but without specifying an explicit encoding will use the system's default locale encoding (e.g., cp1252 on Windows). If the external bcctl tool outputs UTF-8 encoded JSON, this can lead to a UnicodeDecodeError on non-UTF-8 platforms. Specifying encoding="utf-8" prevents this issue.

Suggested change
completed = subprocess.run(
command,
shell=False,
check=False,
capture_output=True,
text=True,
timeout=self.timeout_seconds,
)
completed = subprocess.run(
command,
shell=False,
check=False,
capture_output=True,
encoding="utf-8",
timeout=self.timeout_seconds,
)

Comment on lines +208 to +213
def _resolve_bcctl_executable() -> str | None:
configured = os.environ.get("FFED_BCCTL_PATH")
if configured:
path = Path(configured)
return str(path) if path.is_file() else None
return shutil.which("bcctl")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If FFED_BCCTL_PATH is configured as a simple command name (e.g., "bcctl-custom") rather than an absolute or relative path to a file, Path(configured).is_file() will return False (unless it happens to exist in the current working directory). This prevents the CLI from resolving custom executable names that are available on the system PATH. Let's fall back to shutil.which for the configured name if it is not a direct file path.

Suggested change
def _resolve_bcctl_executable() -> str | None:
configured = os.environ.get("FFED_BCCTL_PATH")
if configured:
path = Path(configured)
return str(path) if path.is_file() else None
return shutil.which("bcctl")
def _resolve_bcctl_executable() -> str | None:
configured = os.environ.get("FFED_BCCTL_PATH")
if configured:
path = Path(configured)
if path.is_file():
return str(path)
resolved = shutil.which(configured)
if resolved:
return resolved
return None
return shutil.which("bcctl")

Comment on lines +220 to +222
def _validate_key_id(value: str) -> None:
if not _SAFE_TOKEN_RE.fullmatch(value) or _SECRETISH_RE.search(value):
raise ValueError("key_id must be a short public-safe token")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If value is not a string (e.g., None or an integer), calling .fullmatch() or .search() on it will raise an AttributeError. Adding an explicit isinstance(value, str) check ensures safe handling of invalid inputs.

Suggested change
def _validate_key_id(value: str) -> None:
if not _SAFE_TOKEN_RE.fullmatch(value) or _SECRETISH_RE.search(value):
raise ValueError("key_id must be a short public-safe token")
def _validate_key_id(value: str) -> None:
if not isinstance(value, str) or not _SAFE_TOKEN_RE.fullmatch(value) or _SECRETISH_RE.search(value):
raise ValueError("key_id must be a short public-safe token")

Comment on lines +225 to +227
def _validate_hex_digest(name: str, value: str) -> None:
if not _HEX_DIGEST_RE.fullmatch(value):
raise ValueError(f"{name} must be a public hex digest")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If value is not a string, calling .fullmatch() on it will raise an AttributeError. Adding an explicit isinstance(value, str) check ensures safe handling of invalid inputs.

Suggested change
def _validate_hex_digest(name: str, value: str) -> None:
if not _HEX_DIGEST_RE.fullmatch(value):
raise ValueError(f"{name} must be a public hex digest")
def _validate_hex_digest(name: str, value: str) -> None:
if not isinstance(value, str) or not _HEX_DIGEST_RE.fullmatch(value):
raise ValueError(f"{name} must be a public hex digest")


def _sanitize_output_text(value: str) -> str:
compact = " ".join(value.replace("\\", "/").split())
compact = _WINDOWS_PATH_RE.sub("[path]", compact)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Update the path replacement to use the generalized _PATH_RE regex to ensure absolute Unix paths are also redacted.

Suggested change
compact = _WINDOWS_PATH_RE.sub("[path]", compact)
compact = _PATH_RE.sub("[path]", compact)

@SeCuReDmE-main-dev SeCuReDmE-main-dev merged commit d38139e into main Jun 25, 2026
1 check passed
@SeCuReDmE-main-dev SeCuReDmE-main-dev deleted the new/security-remediation-refactor-20260625 branch June 25, 2026 10:08

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 91717e52dc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

def _sanitize_output_text(value: str) -> str:
compact = " ".join(value.replace("\\", "/").split())
compact = _WINDOWS_PATH_RE.sub("[path]", compact)
compact = _SECRETISH_RE.sub("[redacted]", compact)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Redact secret values from provider errors

When bcctl returns a JSON error detail such as API_KEY=sk_live_... or Bearer abc..., this substitution only replaces the secret-ish label and leaves the actual value in the BcctlError/CLI traceback, so bc-sign or protect-workflow --bcctl-sign can expose credentials despite the public-safe boundary. Redact the whole key/value or bearer-token span, not just the keyword.

Useful? React with 👍 / 👎.

Comment on lines +83 to +84
signature_b64 = str(payload.get("signature_b64") or "")
signature_digest = hashlib.sha256(signature_b64.encode("utf-8")).hexdigest()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fail closed when the provider omits the signature

When bcctl returns status: ok but omits or nulls signature_b64, this coerces it to an empty string and publishes the SHA-256 of that empty value as a successful signature. In that provider-output scenario, protect-workflow --bcctl-sign emits a perimeter receipt with no usable signature instead of failing closed, so later verification cannot validate the receipt.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant