Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions nac_test/pyats_core/execution/subprocess_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ async def execute_job(
enabled: True
module: nac_test.pyats_core.progress.plugin
order: 1.0
EnvironmentDebugPlugin:
enabled: False
""")

with tempfile.NamedTemporaryFile(
Expand Down Expand Up @@ -249,6 +251,8 @@ async def execute_job_with_testbed(
enabled: True
module: nac_test.pyats_core.progress.plugin
order: 1.0
EnvironmentDebugPlugin:
enabled: False
""")

with tempfile.NamedTemporaryFile(
Expand Down
8 changes: 6 additions & 2 deletions tests/e2e/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
)
from tests.e2e.mocks.mock_server import MockAPIServer

# Sentinel value for credential exposure detection (#689)
# All test passwords use this value so we can detect if credentials leak into artifacts
TEST_CREDENTIAL_SENTINEL = "CRED_SENTINEL_MUST_NOT_APPEAR_IN_ARTIFACTS"


@dataclass
class E2EResults:
Expand Down Expand Up @@ -224,10 +228,10 @@ def _run_e2e_scenario(
else:
class_mocker.setenv(f"{arch}_URL", "http://dry-run.invalid")
class_mocker.setenv(f"{arch}_USERNAME", "mock_user")
class_mocker.setenv(f"{arch}_PASSWORD", "mock_pass")
class_mocker.setenv(f"{arch}_PASSWORD", TEST_CREDENTIAL_SENTINEL)
# IOSXE credentials needed for D2D tests (device access)
class_mocker.setenv("IOSXE_USERNAME", "mock_user")
class_mocker.setenv("IOSXE_PASSWORD", "mock_pass")
class_mocker.setenv("IOSXE_PASSWORD", TEST_CREDENTIAL_SENTINEL)

if extra_env_vars:
for key, value in extra_env_vars.items():
Expand Down
41 changes: 40 additions & 1 deletion tests/e2e/test_e2e_scenarios.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import logging
import re
import xml.etree.ElementTree as ET
import zipfile

import pytest

Expand All @@ -39,7 +40,7 @@
)
from nac_test.robot.reporting.robot_output_parser import RobotResultParser
from tests.conftest import assert_is_link_to
from tests.e2e.conftest import E2EResults
from tests.e2e.conftest import TEST_CREDENTIAL_SENTINEL, E2EResults
from tests.e2e.html_helpers import (
assert_combined_stats,
assert_report_stats,
Expand Down Expand Up @@ -989,6 +990,44 @@ def test_stdout_combined_summary_has_visual_spacing(
f"Content after separator starts with: {repr(after_closing[:20])}"
)

# -------------------------------------------------------------------------
# Security Tests (#689 - Credential Exposure Prevention)
# -------------------------------------------------------------------------

def test_no_credentials_in_output_artifacts(self, results: E2EResults) -> None:
"""Verify that credentials don't appear in any output artifacts.

Regression test for #689 - EnvironmentDebugPlugin was exposing env vars
including passwords in env.txt files within PyATS archives.
"""
offending_files: list[str] = []

for file_path in results.output_dir.rglob("*"):
if not file_path.is_file():
continue

if file_path.suffix.lower() == ".zip":
try:
with zipfile.ZipFile(file_path, "r") as zf:
for name in zf.namelist():
content = zf.read(name).decode("utf-8", errors="ignore")
if TEST_CREDENTIAL_SENTINEL in content:
offending_files.append(f"{file_path}!{name}")
except zipfile.BadZipFile:
pass # Not a valid zip file, skip
else:
# Check all files as text - errors="ignore" safely handles binary
# files by dropping undecodable bytes while preserving readable text.
# No try/except needed: we control output_dir, files won't disappear.
content = file_path.read_text(errors="ignore")
if TEST_CREDENTIAL_SENTINEL in content:
offending_files.append(str(file_path))

assert not offending_files, (
"Credential sentinel found in output artifacts:\n"
+ "\n".join(f" - {f}" for f in offending_files)
)


# =============================================================================
# SUCCESS SCENARIO TESTS
Expand Down
Loading