Turn a Kibana JSON log export into a runnable pytest suite using the secure-log2test CLI. Use when the user has a Kibana or Elasticsearch JSON export of API traffic and wants a regression suite from production logs, when extracting test cases from staging traffic, when scrubbing auth headers or secret-looking body fields before logs leave the laptop, when bridging Kibana-captured requests into a pytest-based suite for CI, when the user mentions Kibana logs, Elasticsearch JSON export, log-to-test conversion, log replay tests, auth header redaction, PII in logs, or regression tests from production traffic.
92
100%
Does it follow best practices?
Impact
93%
1.00xAverage score across 2 eval scenarios
Passed
No known issues
Detailed inputs, outputs, CLI flags, redaction layer rules, validation, limitations, and CI integration. SKILL.md keeps the quick-start and one example. Read this file when you need the full surface.
Input: a Kibana or Elasticsearch JSON export. Each entry needs method and url at minimum. status, duration, headers, and body are picked up when present. Discover view → Share → Export → JSON in Kibana produces the expected shape.
Output: a single pytest module. One test_* function per log entry. The slug filter turns /api/v1/users/42 into a stable function name. Headers and JSON request body are emitted verbatim except for the redaction layer. The HTTP status code from the log is asserted against the live response.
| Flag | Default | Purpose |
|---|---|---|
INPUT_FILE (positional) | required | Kibana JSON export file. |
--output PATH | tests_generated.py | Output .py file path. |
--base-url URL | none | Base URL prepended to relative paths from the log. Can also come from BASE_URL env at test time. |
--max-input-mb N | 100 | Refuse exports above this size in MB. |
--format | pytest | Output format. Currently pytest; json and csv were added by external contributor PR #7. |
--help | - | Full CLI reference. |
--version | - | Print installed version. |
Runs at parse time, before any test code is generated. Two passes, both case-insensitive:
1. Static allowlist of well-known auth headers:
authorization, proxy-authorization, proxy-authenticate, cookie, set-cookie, x-api-key, x-auth-token, x-csrf-token, x-access-token, refresh-token, id-token, x-amz-security-token, authentication.
2. Regex pattern catching custom names:
auth|token|secret|key|session|cookie|credential|bearer|password|passwd.
This regex walks request bodies recursively, so nested fields like {"password": "..."}, {"client_secret": "..."}, and OAuth {"refresh_token": "..."} are scrubbed alongside header names.
Replacement marker: literal string ***REDACTED***. The original input dict is not mutated; the generator copies before redacting.
Adding custom rules: if your team uses an opaque internal name the pattern misses (e.g. X-MyCo-Token), add it to SENSITIVE_HEADERS in core/parser.py before generating output. Rule-file support is tracked in issue #2 for v1.2.
# Count generated test functions
grep -c '^def test_' tests_generated.py
# Confirm log entry count matches
python -c "import json; print(len(json.load(open('data/sample_kibana_export.json'))))"
# Sanity-check no plaintext credentials leaked
grep -E '(authorization|x-api-key).*Bearer\s+[A-Za-z0-9]' tests_generated.py
# Lint the generated module
python -m py_compile tests_generated.pyThe third command should return ZERO matches if redaction worked. If it fires, the header name uses a pattern the redaction layer misses; report it via issue with a redacted sample.
method or url. Either fix the export or open an issue with a redacted sample.--max-input-mb (default 100) refuses exports above the limit. Pass a larger value if the export is genuinely that big, or split it externally first.--base-url matches the host the log came from, and that the API is reachable. The generated module hits real endpoints; it does not mock responses.***REDACTED*** is hardcoded; rule-file support is roadmap.Authorization headers only, redacted at parse time. Token refresh and signature flows are not generated.If a missing feature blocks you, open an issue with a redacted sample.
The redaction layer is a safety net, not a substitute for review. Never commit a generated suite that includes real production tokens. Inspect the generated file before pushing. The pattern is permissive on purpose so it errs toward over-scrubbing; if a non-sensitive header gets caught (e.g. X-Cookie-Banner-Shown), the test still passes because the value is read from the env at run time.
Minimal GitHub Actions workflow that runs the converter and the suite on every push:
name: API tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install secure-log2test
- run: secure-log2test data/kibana_export.json --output tests/test_api.py --base-url $BASE_URL
env:
BASE_URL: ${{ secrets.STAGING_BASE_URL }}
- run: pytest tests/test_api.py -v
env:
BASE_URL: ${{ secrets.STAGING_BASE_URL }}
AUTHORIZATION: ${{ secrets.STAGING_API_TOKEN }}.tessl-plugin
evals
scenario-1
scenario-2
secure_log2test
tests