Skip to content

feat(exfiltration): add data exfiltration / DLP injector (#313) - #323

Open
SamuelHassine wants to merge 8 commits into
mainfrom
feat/injector-exfiltration
Open

feat(exfiltration): add data exfiltration / DLP injector (#313)#323
SamuelHassine wants to merge 8 commits into
mainfrom
feat/injector-exfiltration

Conversation

@SamuelHassine

@SamuelHassine SamuelHassine commented Jul 14, 2026

Copy link
Copy Markdown
Member

Summary

Adds the data exfiltration / DLP injector, which exercises egress filtering and DLP controls by simulating data exfiltration from the injector container over three channels: DNS tunneling, HTTPS upload and cloud storage upload. Payloads are random bytes, so nothing sensitive ever leaves the environment. A blocked attempt is a valid outcome (prevention worked) and is reported as a completed inject; the DETECTION / PREVENTION expectations are scored by the relevant collectors.

Also wires CI: exfiltration is added to the CircleCI build_docker_images, publish_images and test matrices.

Contracts

  • Exfiltration - DNS tunneling: encodes a random payload into DNS queries against a controlled domain (payload capped so query volume stays reasonable).
  • Exfiltration - HTTPS upload: POSTs a random payload to a controlled endpoint.
  • Exfiltration - Cloud storage upload: PUTs a random payload to a cloud storage URL (e.g. a presigned S3 URL).

Notes

  • Payloads are random bytes - nothing sensitive leaves the environment.
  • Required contract fields fail fast: a missing dns_domain / https_url / cloud_url reports execution_status=ERROR instead of a false PREVENTION success.
  • The DNS payload is capped (4 KB) to keep the number of DNS queries bounded; larger sizes remain available for the HTTPS and cloud paths.
  • The payload size is clamped to the supported contract range (1 KB .. 1 MB, derived from the contract choices) so an out-of-range size_kb cannot drive an oversized os.urandom allocation in the container.
  • The Dockerfile branch-override step uses the POSIX [ ... ] test (not the bash-only [[ ... ]]), so it is portable on the Alpine ash shell, matching the ai-redteam Dockerfile.
  • .circleci/config.yml is kept with LF line endings, matching origin/main and the rest of the repo, so the diff shows only the three real matrix additions rather than a whole-file CRLF rewrite.
  • The icon is a generic exfiltration/DLP placeholder mark (square 512x512, solid background per Connector icons should be square with a solid background (netexec, teams, ...) #305) to be finalized with the product team.

Test plan

  • isort (repo-root --profile black --check .) / black / flake8 clean (CI ensure_formatting + linter green).
  • python -m unittest - contracts + executor + injector lifecycle + size parsing, with socket / requests mocked (17 tests, CI Test exfiltration green).
  • Docker image builds via CircleCI build_docker_images-exfiltration (green).

Closes #313

Simulates data exfiltration over DNS tunneling, HTTPS upload and cloud storage upload with random payloads to exercise egress filtering and DLP. Wires the injector into the CircleCI build, publish and test matrices.
Copilot AI review requested due to automatic review settings July 14, 2026 09:33

Copilot AI 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.

Pull request overview

Adds a new Data Exfiltration / DLP injector to the Python monorepo, intended to exercise egress filtering and DLP controls by simulating exfiltration over DNS tunneling, HTTPS upload, and cloud storage upload using random (non-sensitive) payloads. It also wires the new injector into the existing CircleCI matrices so it is tested and built like the other injectors.

Changes:

  • Introduce the exfiltration/ injector module (contracts, config loader, executor, Docker packaging, samples, and metadata).
  • Add unit tests covering contract generation and executor behaviors (DNS/HTTPS/cloud, with socket/requests mocked).
  • Extend CircleCI matrices to include exfiltration in test, build_docker_images, and publish_images.

Reviewed changes

Copilot reviewed 17 out of 20 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
exfiltration/test/test_exfiltration.py Adds unit tests for contracts and executor egress behaviors (mocked).
exfiltration/test/init.py Marks test package (empty init).
exfiltration/README.md Documents injector purpose, contract behaviors, dev workflow, and icon requirements.
exfiltration/pyproject.toml Defines new injector package metadata/deps, tooling config, and icon path.
exfiltration/manifest-metadata.json Adds integration catalog metadata for the exfiltration injector container.
exfiltration/exfiltration_injector/openaev_exfiltration.py Main injector entrypoint: config loading, icon loading, dispatch, callbacks.
exfiltration/exfiltration_injector/img/README.md Documents required icon asset placement and constraints.
exfiltration/exfiltration_injector/helpers/exfiltration_executor.py Implements DNS/HTTPS/cloud simulated exfiltration with random payloads.
exfiltration/exfiltration_injector/helpers/init.py Marks helpers package.
exfiltration/exfiltration_injector/contracts_exfiltration.py Adds three contracts (DNS/HTTPS/cloud) and expectations under Data Exfiltration domain.
exfiltration/exfiltration_injector/configuration/injector_config_override.py Injector config overrides (id/name/icon path).
exfiltration/exfiltration_injector/configuration/config_loader.py Builds daemon config hints including injector type, contracts, icon filepath.
exfiltration/exfiltration_injector/configuration/init.py Marks configuration package.
exfiltration/exfiltration_injector/init.py Adds package init and version constant.
exfiltration/Dockerfile Builds/runs the injector container, including injector_common build context.
exfiltration/docker-compose.yml Adds a compose service for local container runs.
exfiltration/config.yml.sample Adds sample YAML config for local runs.
exfiltration/.env.sample Adds sample environment variable config for docker-compose runs.
exfiltration/.dockerignore Defines docker ignore rules for the injector build context.
.circleci/config.yml Adds exfiltration to CI test/build/publish matrices.

Comment thread exfiltration/exfiltration_injector/openaev_exfiltration.py
Comment thread exfiltration/exfiltration_injector/openaev_exfiltration.py
Comment thread exfiltration/exfiltration_injector/helpers/exfiltration_executor.py
Comment thread exfiltration/exfiltration_injector/helpers/exfiltration_executor.py
Adds coverage for __init__, process_message dispatch and start to satisfy the codecov patch gate.
…load (#313)

- Commit the icon PNG the injector loads at startup so the container no longer crashes with FileNotFoundError; drop the img placeholder README.
- Fail fast with a clear error when a required contract field (dns_domain, https_url, cloud_url) is missing, so misconfiguration reports ERROR instead of a false PREVENTION success.
- Cap the DNS tunneling payload to 4 KB to keep query volume reasonable (larger sizes remain available for the HTTPS and cloud paths).
- Include the target URL in cloud upload outputs for parity with the HTTPS and DNS paths.
- Cover the new behaviors with unit tests.
@SamuelHassine

Copy link
Copy Markdown
Member Author

Review and fix summary

Reviewed the full changed files (not just the diff) with a senior-reviewer lens. The injector is well structured and consistent with the sibling injectors (config loader, contracts, executor, Docker / CI wiring), and the "a blocked egress is still a completed inject" model is correct for DETECTION / PREVENTION scoring.

Pushed 496970a to address four issues (the first two are the important ones):

  • must-fix: the injector opened exfiltration_injector/img/icon-exfiltration.png, which was not committed (only an img/README.md placeholder), so the container would crash at startup with FileNotFoundError. Committed the actual icon PNG (square 512x512, solid background per Connector icons should be square with a solid background (netexec, teams, ...) #305) and removed the placeholder README, matching how the other injectors ship their icon.
  • must-fix: _dispatch used content.get(...) for mandatory fields, so a missing dns_domain / https_url / cloud_url became a RequestException that was reported as a "blocked" (PREVENTION) success. It now fails fast with a clear ValueError, so misconfiguration reports execution_status=ERROR.
  • should-fix: capped the DNS tunneling payload at 4 KB (_MAX_DNS_KB) so a 1 MB payload no longer emits ~28k DNS queries; larger sizes remain available for HTTPS / cloud.
  • should-fix: exfiltrate_cloud now includes the target url in outputs on both the success and blocked paths, for parity with the HTTPS / DNS paths.

Added unit tests for each of the new behaviors.

Verification

  • Lint clean: black, isort (--profile black), flake8 (--ignore=E,W), ruff.
  • python -m unittest: 13 tests pass (contracts + executor + injector lifecycle, socket / requests mocked).
  • CI is fully green: GitHub Actions checks, all CircleCI test-* and build_docker_images-* jobs (including -exfiltration), and codecov.

Remaining

Copilot AI 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.

Pull request overview

Copilot reviewed 17 out of 21 changed files in this pull request and generated 1 comment.

Comment thread exfiltration/exfiltration_injector/openaev_exfiltration.py Outdated
_size() accepted arbitrary integers from inject_content and passed them
straight to os.urandom(size_kb * 1024), so a large size_kb could drive
excessive memory allocation / OOM in the injector container. Derive
MIN_SIZE_KB / MAX_SIZE_KB from the contract SIZE_CHOICES and clamp the
parsed value to that range (minimum 1). Covered by new unit tests.
The CI ensure_formatting job runs `isort --profile black --check .` from the
repository root, where exfiltration_injector is not on the known_local_folder
list and is therefore treated as a third-party import. Re-sort the touched
files to match that ordering so the repo-root check passes.
@SamuelHassine

Copy link
Copy Markdown
Member Author

Review and fix summary (second pass)

Re-ran the full review-and-fix workflow. Prioritised the newly landed review comment and also did an independent senior-reviewer read of the full changed files (not just the diff).

New review comment addressed

  • _size() accepted arbitrary integers from inject_content and passed them straight to os.urandom(size_kb * 1024), so a large size_kb could drive excessive memory allocation / OOM in the container. Fixed in 68e3428: added MIN_SIZE_KB / MAX_SIZE_KB derived from the contract SIZE_CHOICES (1 KB .. 1 MB) and clamped the parsed value to that range (minimum 1). Deriving the bounds from SIZE_CHOICES keeps the executor and the form in sync. Covered by test_size_clamps_above_max, test_size_clamps_below_min, test_size_defaults_when_invalid and test_size_accepts_valid_list_shape. Thread resolved.

Independent re-review

  • No additional must-fix / should-fix findings. The four earlier issues (missing icon, fail-fast on required fields, DNS payload cap, cloud url in outputs) remain correctly fixed. Contracts, config loader, executor and Docker / CI wiring are consistent with the sibling injectors, and the "a blocked egress is still a completed inject" model is correct for DETECTION / PREVENTION scoring.

Follow-up commit

  • 49f6abb: re-sorted imports in the two touched files so the repo-root isort --profile black --check . job passes (at repo root exfiltration_injector is not on known_local_folder, so it is grouped as third-party). This is why the first ensure_formatting run went red before going green.

Verification

  • Lint clean locally: black, isort (--profile black, both per-injector and repo-root), flake8 (--ignore=E,W), ruff.
  • python -m unittest: 17 tests pass (contracts + executor + injector lifecycle + size parsing, socket / requests mocked).
  • CI is fully green: GitHub Actions checks, all CircleCI test-* and build_docker_images-* jobs (including -exfiltration), codecov, signed-commits check. All 5 commits on the branch are GPG-signed.

Remaining

  • Needs a second maintainer's approval: GitHub blocks self-approval because the reviewing account is the PR author, so I could not --approve. mergeStateStatus is BLOCKED only on the required review, not on CI. My approval notes are captured above for the maintainer.
  • The committed icon is a generic exfiltration/DLP placeholder to be finalized with the product team (tracked under Connector icons should be square with a solid background (netexec, teams, ...) #305).

Copilot AI 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.

Pull request overview

Copilot reviewed 17 out of 21 changed files in this pull request and generated 1 comment.

Comment thread exfiltration/Dockerfile Outdated
…313)

The RUN conditional used the bash-only [[ ... ]] test, but the Alpine
runner shell is ash (POSIX sh), where [[ is not a builtin. Switch to the
POSIX [ ... ] test with the variable quoted, matching the ai-redteam
Dockerfile, so the branch-override step is portable across shells.
@SamuelHassine

Copy link
Copy Markdown
Member Author

Review and fix summary (final pass)

Re-ran the full review-and-fix workflow: independent senior-reviewer read of the full changed files (not just the diff) - contracts, config loader, executor, injector lifecycle, tests, and Docker / CI wiring.

Change in this pass

  • fix(exfiltration): the Dockerfile branch-override step used the bash-only [[ ... ]] test, but the Alpine runner stage uses ash (/bin/sh) where [[ is not a builtin. Switched to the POSIX [ "${PYOAEV_GIT_BRANCH_OVERRIDE}" ] test with the variable quoted, and quoted the git+https://... install target, matching the ai-redteam Dockerfile. Commit 38851c0. Thread resolved.

Independent re-review

  • No additional must-fix / should-fix findings. The earlier fixes remain correct: the committed icon, fail-fast on missing required fields (dns_domain / https_url / cloud_url -> execution_status=ERROR), the 4 KB DNS payload cap, the cloud url in outputs, and the size clamp to the contract range (1 KB .. 1 MB derived from SIZE_CHOICES). The "a blocked egress is still a completed inject" model is correct for DETECTION / PREVENTION scoring.

Verification

  • Lint clean locally: black, isort (--profile black), flake8 (--ignore=E,W).
  • python -m unittest: 17 tests pass (contracts + executor + injector lifecycle + size parsing, socket / requests mocked).
  • CI fully green: GitHub Actions checks, all CircleCI test-* and build_docker_images-* (including -exfiltration), codecov, signed-commits. All commits on the branch are GPG-signed.

Remaining

  • Needs a second maintainer's approval: GitHub blocks self-approval because the reviewing account is the PR author, so --approve could not be used. mergeStateStatus is BLOCKED only on the required review, not on CI. My positive review notes are captured above for the maintainer.
  • The committed icon is a generic exfiltration/DLP placeholder to be finalized with the product team (tracked under Connector icons should be square with a solid background (netexec, teams, ...) #305).

Copilot AI 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.

Pull request overview

Copilot reviewed 17 out of 21 changed files in this pull request and generated 1 comment.

Comment thread exfiltration/exfiltration_injector/__init__.py
#313)

The injector was added to the CircleCI test / build_docker_images /
publish_images matrices, but the file was re-saved with CRLF line endings
while origin/main uses LF. That turned a 3-line change into a whole-file
290/290 rewrite, breaking git blame and mixing EOL styles. Convert the file
back to LF so the diff reflects only the three real matrix additions and the
CI config stays consistent with the rest of the repo (all other files in the
PR are LF).
@SamuelHassine

Copy link
Copy Markdown
Member Author

Review and fix summary

Ran the full review-and-fix workflow: independent senior-reviewer read of the full changed files (not just the diff) - contracts, config loader, executor, injector lifecycle, tests, Dockerfile and CI wiring - plus addressed the remaining open review comment.

New finding fixed this pass (not caught by the bots)

  • .circleci/config.yml had been committed with CRLF line endings while origin/main uses LF. That turned the 3-line matrix change (adding exfiltration to test / build_docker_images / publish_images) into a whole-file 290/290 rewrite, which breaks git blame and mixes EOL styles. Normalized the file back to LF in 273bf49 so the diff now shows only the three real additions, consistent with the rest of the repo. Verified: git diff --ignore-all-space origin/main on the file is exactly 3 insertions / 3 deletions.

Review comment addressed

  • __version__ vs pyproject version mismatch: intentionally keeping __version__ = "1.0.0". The sibling aws injector uses the same pattern (1.0.0 in __init__.py alongside 2.260710.0 in pyproject.toml), the module __version__ is not imported / consumed anywhere, and scripts/release.py only rewrites __version__ lines matching the previous release string. Aligning exfiltration alone would make it inconsistent with the other Python injectors. Replied and resolved the thread.

Independent re-review

  • No additional must-fix / should-fix findings. The earlier fixes remain correct: committed icon (startup no longer raises FileNotFoundError), fail-fast on missing required fields (execution_status=ERROR), 4 KB DNS payload cap, cloud url in outputs, and the size clamp to the contract range (1 KB .. 1 MB from SIZE_CHOICES). The "a blocked egress is still a completed inject" model is correct for DETECTION / PREVENTION scoring.

Verification

  • Lint clean locally: black, isort (repo-root --profile black --check ., as CI runs it), flake8 (--ignore=E,W).
  • python -m unittest: 17 tests pass (contracts + executor + injector lifecycle + size parsing, socket / requests mocked).
  • CI fully green: GitHub Actions checks, all CircleCI test-* and build_docker_images-* (including -exfiltration), codecov, signed-commits. All 8 commits on the branch are GPG-signed. mergeable: MERGEABLE.
  • All review threads are resolved (0 unresolved).

Remaining

  • Needs a second maintainer's approval: GitHub blocks self-approval because the reviewing account is the PR author, so --approve returns "Can not approve your own pull request". mergeStateStatus is BLOCKED only on the required review, not on CI. My positive review notes are captured above for the maintainer.
  • The committed icon is a generic exfiltration/DLP placeholder to be finalized with the product team (tracked under Connector icons should be square with a solid background (netexec, teams, ...) #305).

Copilot AI 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.

Pull request overview

Copilot reviewed 17 out of 21 changed files in this pull request and generated no new comments.

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.

Build the data exfiltration / DLP injector

3 participants