Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9564474
chore(submodule): add rest.mdm-sn-container-runner as git submodule
abdibekbolot Jun 16, 2026
b4d3e8f
fix(package): update pyproject.toml for bot-virus-challenge project
abdibekbolot Jun 16, 2026
5282187
fix(scripts): update version scripts for Python module format
abdibekbolot Jun 16, 2026
3219798
feat(bot): add WebUI automation bot module
abdibekbolot Jun 16, 2026
2971662
feat(infra): add bot-runner service with hardened networking
abdibekbolot Jun 16, 2026
48354c0
chore: pin bot-virus runner adaptation
abdibekbolot Jun 17, 2026
c44277c
feat(api): port humanize challenge API to bot-virus
abdibekbolot Jun 17, 2026
b912872
chore(gitignore): ignore private challenge artifacts
abdibekbolot Jun 20, 2026
889a814
build(scoring): add private detector dependency and score policy
abdibekbolot Jun 20, 2026
88955d9
feat(scoring): add run-scoped eval attribution
abdibekbolot Jun 20, 2026
78d2f71
fix(api): return explicit errors for broken challenge endpoints
abdibekbolot Jun 20, 2026
b6313f2
build(scoring): install bundled private scoring wheels
abdibekbolot Jun 22, 2026
64af1e2
feat(scoring): add public payload shape validation
abdibekbolot Jun 22, 2026
558b4a3
feat(web): add browser behavior collection SDK
abdibekbolot Jun 22, 2026
021f505
chore(bot): replace multi-file boilerplate with two-file contract
abdibekbolot Jun 24, 2026
f079c21
chore(infra): add CHALLENGE_VM_HOST and CHALLENGE_VM_PORT env vars
abdibekbolot Jun 24, 2026
9c5d4fa
feat(challenge): HBv6 non-behavioral runtime integrity backend
abdibekbolot Jun 24, 2026
29b9b6b
feat(sdk): HBv6 non-behavioral runtime integrity collector + encryption
abdibekbolot Jun 24, 2026
71943fb
chore(deps): update scoring wheel for HBv6 non-behavioral scoring
abdibekbolot Jun 24, 2026
82f7c25
test: update tests for HBv6 non-behavioral flow
abdibekbolot Jun 24, 2026
7608d6d
chore(submodule): update rest.mdm-sn-container-runner for HBv6 changes
abdibekbolot Jun 24, 2026
0a72bba
Merge pull request #11 from RedTeamSubnet/dev
BaratovSokhibjon Jul 2, 2026
f117842
feat(bot): add Dockerfile and bot.py for Bot Virus challenge automation
aliyuldashev Jul 6, 2026
cd1ba8d
refactor(config): increase bot and VM timeout values for improved per…
aliyuldashev Jul 6, 2026
407f023
refactor(docker): update Dockerfile and .dockerignore to include bot …
aliyuldashev Jul 6, 2026
992bb52
refactor(scoring): simplify scoring logic by removing Layer 1 gate an…
aliyuldashev Jul 7, 2026
55c3469
refactor: Remove Bot Virus SDK JavaScript file to streamline project …
aliyuldashev Jul 8, 2026
74e2985
refactor: clean up imports and remove unused code in schemas, service…
aliyuldashev Jul 8, 2026
59d645b
feat: update the web content
aliyuldashev Jul 8, 2026
fc02ba8
chore: update rt_bv_score dependency to version 4.3.5 and add new whe…
aliyuldashev Jul 9, 2026
c93ec56
feat: add simple bot service configurations and endpoints for scoring…
aliyuldashev Jul 9, 2026
d640db8
refactor: remove unused configuration options and disabled endpoints …
aliyuldashev Jul 9, 2026
3be81b0
feat: update bot service configurations and remove deprecated settings
aliyuldashev Jul 12, 2026
0494c6d
feat: update bot service environment variables and remove deprecated …
aliyuldashev Jul 12, 2026
54ad1bd
Refactor challenge scoring orchestration by introducing PayloadManager
aliyuldashev Jul 12, 2026
e343317
feat: add requests library to challenge requirements
aliyuldashev Jul 12, 2026
591c4bf
feat: enhance schemas with file reading functionality and update exam…
aliyuldashev Jul 12, 2026
0f2ebf0
feat: add favicon for browser verification page
aliyuldashev Jul 12, 2026
de39dc4
refactor: increase VM timeout to improve remote Docker build/run reli…
aliyuldashev Jul 12, 2026
936bc9c
refactor: clean up logging and feedback formatting in score function
aliyuldashev Jul 12, 2026
f3714a5
build(deps): update pydantic requirement
dependabot[bot] Jul 12, 2026
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
11 changes: 11 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,14 @@ BV_CHALLENGE_API_PORT=10001
# BV_CHALLENGE_API_DOCS_OPENAPI_URL="{api_prefix}/openapi.json"
# BV_CHALLENGE_API_DOCS_DOCS_URL="{api_prefix}/docs"
# BV_CHALLENGE_API_DOCS_REDOC_URL="{api_prefix}/redoc"

## -- MDM challenge / Simple Bot configs -- ##
CHALLENGE_HTTPS_PROXY_PORT=10443
MDM_CHALLENGE_BASE_URL="http://challenge-api:10001"
# MDM_CHALLENGE_SIMPLE_BOT_URL="https://simplebot.theredteam.io"
# MDM_CHALLENGE_BOT_DIR="/app/rest.vm-runner/bot"
# MDM_CHALLENGE_SIMPLE_BOT_NETWORK_NAME="bot-simple-network"
# MDM_CHALLENGE_CHALLENGE_NETWORK_NAME="bot-challenge-network"
# MDM_CHALLENGE_MINER_IMAGE_TAG="redteamsubnet/bv-miner:latest"
# MDM_CHALLENGE_SIMPLE_BOT_POLL_MAX_ATTEMPTS=5
# MDM_CHALLENGE_SIMPLE_BOT_POLL_INTERVAL_SEC=2
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -535,3 +535,16 @@ volumes/configs/**/*.DS_Store
volumes/configs/**/*Thumbs.db
volumes/configs/**/*~
volumes/configs/**/*._*

# Internal design docs (kept local, not published)
docs/superpowers/

# Internal benchmark/test bot fixtures (reveal scoring expectations — keep local)
benchmarks/
tests/fixtures/bots/

# Private detector source (compiled to the rt_bv_score wheel — never publish source)
private/
# Rust build artifacts
target/
Cargo.lock
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "src/modules/rest.mdm-sn-container-runner"]
path = src/modules/rest.mdm-sn-container-runner
url = git@github.com:RedTeamSubnet/rest.hb-bot-executer.git
53 changes: 52 additions & 1 deletion compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ services:
build:
context: ./src/bv_challenge/challenge
restart: unless-stopped
networks:
- bot-virus-challenge-net
environment:
TERM: ${TERM:-xterm}
TZ: ${TZ:-Asia/Seoul}
BV_CHALLENGE_API_PORT: ${BV_CHALLENGE_API_PORT:-10001}
BV_CHALLENGE_API_BOT_RUNNER_URL: ${BV_CHALLENGE_API_BOT_RUNNER_URL:-http://bot-runner:8000}
BV_CHALLENGE_API_BOT_RUNNER_SESSION_COUNT: ${BV_CHALLENGE_API_BOT_RUNNER_SESSION_COUNT:-2}
BV_CHALLENGE_API_BOT_RUNNER_REQUEST_TIMEOUT_SEC: ${BV_CHALLENGE_API_BOT_RUNNER_REQUEST_TIMEOUT_SEC:-900}
env_file:
- path: .env
required: false
Expand All @@ -17,4 +22,50 @@ services:
- "./volumes/storage/rest-bv-challenge/data:${BV_CHALLENGE_API_DATA_DIR:-/var/lib/rest-bv-challenge}"
ports:
- "${BV_CHALLENGE_API_PORT:-10001}:${BV_CHALLENGE_API_PORT:-10001}"
tty: true

bot-runner:
image: redteamsubnet61/bot_virus_bot_runner:latest
build:
context: ./src/modules/rest.mdm-sn-container-runner
restart: unless-stopped
networks:
- bot-virus-challenge-net
environment:
TERM: ${TERM:-xterm}
TZ: ${TZ:-UTC}
VM_RUNNER_API_PORT: ${VM_RUNNER_API_PORT:-8000}
DOCKER_HOST: unix:///docker-socket/docker.sock
CHALLENGE_HTTPS_PROXY_PORT: ${CHALLENGE_HTTPS_PROXY_PORT:-10443}
env_file:
- path: .env
required: false
volumes:
- "./volumes/storage/rest-mdm-sn-container-runner/logs:${VM_RUNNER_API_LOGS_DIR:-/var/log/rest.vm-runner}"
- "./volumes/storage/rest-mdm-sn-container-runner/data:${VM_RUNNER_API_DATA_DIR:-/var/lib/rest.vm-runner}"
- "bot-runner-dind-socket:/docker-socket"
ports:
- "${VM_RUNNER_API_PORT:-8000}:${VM_RUNNER_API_PORT:-8000}"

bot-runner-dind:
image: redteamsubnet61/bot_virus_bot_runner_dind:latest
build:
context: ./src/modules/rest.mdm-sn-container-runner
dockerfile: Dockerfile.dind
restart: unless-stopped
networks:
- bot-virus-challenge-net
privileged: true
environment:
DOCKER_TLS_CERTDIR: ""
CHALLENGE_HTTPS_PROXY_PORT: ${CHALLENGE_HTTPS_PROXY_PORT:-10443}
volumes:
- "bot-runner-dind-socket:/docker-socket"
- "bot-runner-dind-data:/var/lib/docker"

networks:
bot-virus-challenge-net:
name: bot-virus-challenge-net
driver: bridge
volumes:
bot-runner-dind-socket:
bot-runner-dind-data:
22 changes: 12 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,16 @@ classifiers = [
]
dynamic = ["version", "dependencies", "optional-dependencies"]

# [tool.setuptools.packages.find]
# where = ["src"]
# include = ["bv_challenge*"]
# namespaces = false
[tool.setuptools]
include-package-data = true

[tool.setuptools.packages.find]
where = ["src"]
include = ["bv_challenge*"]

[tool.setuptools.dynamic]
version = { attr = "bv_challenge.__version__.__version__" }
dependencies = { file = "./requirements.txt" }
version = { attr = "bv_challenge.__version__" }
dependencies = { file = ["requirements.txt"] }

[tool.setuptools.dynamic.optional-dependencies]
# Options dependencies for DEVELOPMENT
Expand All @@ -62,8 +64,8 @@ dev = { file = [
# venv = ".venv"

[project.urls]
Homepage = "https://github.com/RedTeamSubnet/challenge-template"
Homepage = "https://github.com/RedTeamSubnet/bot-virus-challenge"
Documentation = "https://docs.theredteam.io"
Repository = "https://github.com/RedTeamSubnet/challenge-template.git"
Issues = "https://github.com/RedTeamSubnet/challenge-template/issues"
Changelog = "https://github.com/RedTeamSubnet/challenge-template/blob/main/CHANGELOG.md"
Repository = "https://github.com/RedTeamSubnet/bot-virus-challenge.git"
Issues = "https://github.com/RedTeamSubnet/bot-virus-challenge/issues"
Changelog = "https://github.com/RedTeamSubnet/bot-virus-challenge/blob/main/CHANGELOG.md"
5 changes: 3 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
python-dotenv>=1.0.1,<2.0.0
pydantic[email,timezone]>=2.0.3,<3.0.0
pydantic[email,timezone]>=2.13.4,<3.0.0
pydantic-settings>=2.2.1,<3.0.0
# redteam_core @ git+https://github.com/RedTeamSubnet/RedTeam.git@v4.2.2
./requirements/rt_bv_score-0.1.0-cp310-abi3-manylinux_2_34_x86_64.whl
./requirements/vault_unlock-0.1.0-cp310-abi3-manylinux_2_34_x86_64.whl
4 changes: 2 additions & 2 deletions scripts/bump-version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fi

## --- Variables --- ##
# Load from environment variables:
VERSION_FILE_PATH="${VERSION_FILE_PATH:-./VERSION.txt}"
VERSION_FILE_PATH="${VERSION_FILE_PATH:-./src/bv_challenge/__version__.py}"


_BUMP_TYPE=""
Expand Down Expand Up @@ -130,7 +130,7 @@ main()

echo "[INFO]: Bumping version to '${_new_version}'..."
# Update the version file with the new version:
echo "${_new_version}" > "${VERSION_FILE_PATH}" || exit 2
echo -e "__version__ = \"${_new_version}\"" > "${VERSION_FILE_PATH}" || exit 2
echo "[OK]: New version: '${_new_version}'"

./scripts/sync-versions.sh -a || exit 2
Expand Down
4 changes: 2 additions & 2 deletions scripts/get-version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ cd "${_PROJECT_DIR}" || exit 2

## --- Variables --- ##
# Load from environment variables:
VERSION_FILE_PATH="${VERSION_FILE_PATH:-./VERSION.txt}"
VERSION_FILE_PATH="${VERSION_FILE_PATH:-./src/bv_challenge/__version__.py}"
## --- Variables --- ##


if [ -n "${VERSION_FILE_PATH}" ] && [ -f "${VERSION_FILE_PATH}" ]; then
_current_version=$(cat "${VERSION_FILE_PATH}") || exit 2
_current_version=$(< "${VERSION_FILE_PATH}" grep "__version__ = " | awk -F' = ' '{print $2}' | tr -d '"') || exit 2
else
_current_version="0.0.0"
fi
Expand Down
1 change: 1 addition & 0 deletions src/bv_challenge/challenge/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@ README.md
**/compose.sh
**/compose.override.y*ml
**/Dockerfile*
!bot/Dockerfile
**/Makefile
**/mkdocs.yml
**/pm2-process.json*
Expand Down
3 changes: 3 additions & 0 deletions src/bv_challenge/challenge/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ RUN --mount=type=cache,target=/root/.cache,sharing=locked \
# COPY ./requirements* ./
RUN --mount=type=cache,target=/root/.cache,sharing=locked \
--mount=type=bind,source=requirements.txt,target=requirements.txt \
--mount=type=bind,source=requirements,target=requirements \
python3 -m uv pip install --prefix=/install -r ./requirements.txt


Expand Down Expand Up @@ -140,6 +141,8 @@ FROM base AS app

WORKDIR "${BV_CHALLENGE_API_DIR}"
COPY --chown=${UID}:${GID} ./api ${BV_CHALLENGE_API_DIR}/api
COPY --chown=${UID}:${GID} ./bot ${BV_CHALLENGE_API_DIR}/bot
COPY --chown=${UID}:${GID} ./templates ${BV_CHALLENGE_API_DIR}/templates
COPY --chown=${UID}:${GID} --chmod=770 ./scripts/*.sh /usr/local/bin/

# VOLUME ["${BV_CHALLENGE_API_DATA_DIR}"]
Expand Down
3 changes: 3 additions & 0 deletions src/bv_challenge/challenge/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-

from api.__version__ import __version__


__all__ = ["__version__"]
23 changes: 21 additions & 2 deletions src/bv_challenge/challenge/api/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
# -*- coding: utf-8 -*-

## Third-party libraries
from fastapi import FastAPI

## Internal modules
from api.bootstrap import create_app, run_server
from api.logger import logger
from api.main import main


app: FastAPI = create_app()


def main() -> None:
"""Main function."""

run_server(app="api.__main__:app")
return


if __name__ == "__main__":
logger.info("Starting server from '__main__.py'...")
logger.info(f"Starting server from '__main__.py'...")
main()

__all__ = ["app"]
2 changes: 1 addition & 1 deletion src/bv_challenge/challenge/api/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.0.0"
__version__ = "0.0.2"
51 changes: 29 additions & 22 deletions src/bv_challenge/challenge/api/bootstrap.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
# Standard libraries
from typing import Any
from collections.abc import Callable
# -*- coding: utf-8 -*-

# Third-party libraries
## Standard libraries
import os
from typing import Union

## Third-party libraries
import uvicorn
from uvicorn._types import ASGIApplication
from pydantic import validate_call
from fastapi import FastAPI

from beans_logging_fastapi import add_logger

# Internal modules
from api.__version__ import __version__
## Internal modules
from api.config import config
from api.lifespan import lifespan, pre_init
from api.middleware import add_middlewares
Expand All @@ -31,19 +30,13 @@ def create_app() -> FastAPI:
pre_init()

app = FastAPI(
title=config.api.title,
version=__version__,
title=config.api.name,
version=config.version,
lifespan=lifespan,
default_response_class=BaseResponse,
**config.api.docs.model_dump(exclude={"enabled"}),
)

add_logger(
app=app,
config=config.api.logger,
has_proxy_headers=config.api.uvicorn.proxy_headers,
)

add_middlewares(app=app)
add_routers(app=app)
add_mounts(app=app)
Expand All @@ -53,21 +46,35 @@ def create_app() -> FastAPI:


@validate_call(config={"arbitrary_types_allowed": True})
def run_server(app: FastAPI | ASGIApplication | Callable[..., Any] | str) -> None:
def run_server(app: Union[ASGIApplication, str] = "main:app") -> None:
"""Run uvicorn server.

Args:
app (FastAPI |
ASGIApplication |
Callable[..., Any] |
str , required): FastAPI application instance or ASGI application or import string.
app (Union[ASGIApplication, str], optional): ASGI application instance or module path.
"""

_ssl_keyfile: Union[str, None] = None
_ssl_certfile: Union[str, None] = None

if config.api.security.ssl.enabled:
_ssl_keyfile = os.path.join(
config.api.paths.ssl_dir, config.api.security.ssl.key_fname
)
_ssl_certfile = os.path.join(
config.api.paths.ssl_dir, config.api.security.ssl.cert_fname
)

uvicorn.run(
app=app,
host=config.api.bind_host,
port=config.api.port,
**config.api.uvicorn.model_dump(),
access_log=False,
server_header=False,
proxy_headers=config.api.behind_proxy,
forwarded_allow_ips=config.api.security.forwarded_allow_ips,
ssl_keyfile=_ssl_keyfile,
ssl_certfile=_ssl_certfile,
**config.api.dev.model_dump(),
)

return
Expand Down
54 changes: 16 additions & 38 deletions src/bv_challenge/challenge/api/config.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,24 @@
import os
from typing import TypeVar, Any
# -*- coding: utf-8 -*-

from pydantic import validate_call
import pathlib

from potato_util.io import read_all_configs
from onion_config import ConfigLoader
from beans_logging import logger

from api.core.constants import ENV_PREFIX_API, API_SLUG
from api.core.configs import MainConfig
from api.logger import logger

ConfigType = TypeVar("ConfigType", bound=MainConfig)

config: MainConfig
try:
_parent_dir = pathlib.Path(__file__).parent.resolve()
_config_loader = ConfigLoader(
config_schema=MainConfig, configs_dirs=[str(_parent_dir / "configs")]
)
# Main config object:
config: MainConfig = _config_loader.load()
except Exception:
logger.exception("Failed to load config:")
raise SystemExit(1)

@validate_call
def load_config(
configs_dir: str = os.path.join("/etc", API_SLUG),
env_name: str = f"{ENV_PREFIX_API}CONFIGS_DIR",
config_schema: type[ConfigType] = MainConfig,
) -> ConfigType:
_configs_dir_env = os.getenv(env_name, "")
if _configs_dir_env:
configs_dir = _configs_dir_env

_config_dict: dict[str, Any] = {}
if os.path.isdir(configs_dir):
_config_dict = read_all_configs(configs_dir=configs_dir)

_config: ConfigType | None = None
try:
_config = config_schema(**_config_dict)
except Exception:
logger.exception("Failed to load config:")
raise SystemExit(1)

return _config


config = load_config()


__all__ = [
"MainConfig",
"load_config",
"config",
]
__all__ = ["config"]
Loading