Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
55c5b3f
In the middle of updates
monoxgas Mar 5, 2025
80cd3b8
Finalizing scoring->metrics and inputs/params for tasks
monoxgas Mar 14, 2025
ea184c1
Reworking object storage
monoxgas Mar 17, 2025
fd6cbf1
Merge branch 'main' into small-refactor
monoxgas Mar 20, 2025
9c1fd05
shuffling some things prior to merge
monoxgas Mar 20, 2025
8a066fa
Merge commit '7f882b277aadd41cf8fdcad4740adf26bc1e3516' into small-re…
monoxgas Mar 20, 2025
0d41142
Pending work
monoxgas Mar 27, 2025
6573e60
Initial serialization code
monoxgas Apr 9, 2025
e693cbd
Plug serialization and schema generation into log_object. Rename kind…
monoxgas Apr 9, 2025
a05a672
Add logic to write the objects to s3
rdheekonda Apr 10, 2025
26a435e
Merge remote-tracking branch 'origin/main' into v1
monoxgas Apr 14, 2025
90f7e44
Fixes for v1 compat API changes
monoxgas Apr 15, 2025
6e3e865
Fixes for pydantic schema generation failures in serialization
monoxgas Apr 18, 2025
a4b6646
Fixing some serialization behavior related to primitives and Nones. R…
monoxgas Apr 18, 2025
ec00aac
Lots of docstring writing
monoxgas Apr 20, 2025
77bbc93
Add audio and video serialize handlers (#3)
rdheekonda Apr 21, 2025
4ffe2e3
Adjust Task prop extraction for decorator nesting
monoxgas Apr 21, 2025
fdf23e1
README updates
monoxgas Apr 22, 2025
ac579e5
hotfix: Trying to correct endpoint_url issues
monoxgas Apr 22, 2025
4d1ab93
Merge branch 'main' into v1
monoxgas Apr 23, 2025
92a09f0
Fixing @task decorator typing
monoxgas Apr 23, 2025
65f2dc0
Object linking for tasks.
monoxgas Apr 23, 2025
09f252a
Remove dead ext folder
monoxgas Apr 23, 2025
7a0e410
Add Log Artifact for Files and Directories (#18)
rdheekonda Apr 23, 2025
d30cafa
Moved some artifact components around. Fixed typing/linting errors. C…
monoxgas Apr 23, 2025
2a97753
Additional logging updates for the api client
monoxgas Apr 23, 2025
d30438e
Version to v1.0.0-rc.0
monoxgas Apr 23, 2025
aa4b4a4
chore: linting
briangreunke Apr 24, 2025
d9386ee
Merge pull request #21 from dreadnode/brian/eng-1792-chore-linting-fixes
briangreunke Apr 24, 2025
f5e8a1b
chore: merged main
briangreunke Apr 24, 2025
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
8 changes: 0 additions & 8 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ We actively welcome your pull requests.
3. If you've changed APIs, update the documentation.
4. Ensure the test suite passes.
5. Make sure your code lints.
6. If you haven't already, complete the Contributor License Agreement ("CLA").

### PR Description Format

Expand Down Expand Up @@ -54,13 +53,6 @@ Example:
- Deprecated setup scripts
```

## Contributor License Agreement ("CLA")

In order to accept your pull request, we need you to submit a CLA. You only need
to do this once to work on any of Facebook's open source projects.

Complete your CLA here: <https://code.facebook.com/cla>

## Issues

We use GitHub issues to track public bugs. Please ensure your description is
Expand Down
1 change: 1 addition & 0 deletions .github/labeler.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ type/docs:
- changed-files:
- any-glob-to-any-file: "docs/**/*"
- any-glob-to-any-file: "*.md"
- any-glob-to-any-file: "*.mdx"

# Core Files Labels
type/core:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Testing code
notebooks/
examples/.logfire

# Logfire temp
.logfire/
Expand Down
18 changes: 12 additions & 6 deletions .hooks/check_pinned_hash_dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ def __init__(self) -> None:
self.pinned_pattern = re.compile(r"uses:\s+([^@\s]+)@([a-f0-9]{40})")

# Pattern for actions with version tags (unpinned)
self.unpinned_pattern = re.compile(r"uses:\s+([^@\s]+)@(v\d+(?:\.\d+)*(?:-[a-zA-Z0-9]+(?:\.\d+)*)?)")
self.unpinned_pattern = re.compile(
r"uses:\s+([^@\s]+)@(v\d+(?:\.\d+)*(?:-[a-zA-Z0-9]+(?:\.\d+)*)?)",
)

# Pattern for all uses statements
self.all_uses_pattern = re.compile(r"uses:\s+([^@\s]+)@([^\s\n]+)")
Expand All @@ -30,16 +32,18 @@ def format_terminal_link(self, file_path: str, line_number: int) -> str:
def get_line_numbers(self, content: str, pattern: re.Pattern[str]) -> list[tuple[str, int]]:
"""Find matches with their line numbers."""
matches = []
for i, line in enumerate(content.splitlines(), 1):
for match in pattern.finditer(line):
matches.append((match.group(0), i))
matches.extend(
(match.group(0), i)
for i, line in enumerate(content.splitlines(), 1)
for match in pattern.finditer(line)
)
return matches

def check_file(self, file_path: str) -> bool:
"""Check a single file for unpinned dependencies."""
try:
content = Path(file_path).read_text()
except Exception as e:
except (FileNotFoundError, PermissionError, IsADirectoryError, OSError) as e:
print(f"\033[91mError reading file {file_path}: {e}\033[0m")
return False

Expand Down Expand Up @@ -84,7 +88,9 @@ def check_file(self, file_path: str) -> bool:
has_errors = True
print("\033[91m[!] Completely unpinned (no SHA or version):\033[0m")
for match, line_num in unpinned_without_hash:
print(f" |- {match} \033[90m({self.format_terminal_link(file_path, line_num)})\033[0m")
print(
f" |- {match} \033[90m({self.format_terminal_link(file_path, line_num)})\033[0m",
)

# Print summary
total_actions = len(pinned_matches) + len(unpinned_matches) + len(unpinned_without_hash)
Expand Down
26 changes: 19 additions & 7 deletions .hooks/generate_pr_description.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@
import asyncio
import os
import typing as t
from pathlib import Path

import rigging as rg
import typer

TRUNCATION_WARNING = "\n---\n**Note**: Due to the large size of this diff, some content has been truncated."
TRUNCATION_WARNING = (
"\n---\n**Note**: Due to the large size of this diff, some content has been truncated."
)


@rg.prompt # type: ignore
@rg.prompt
def generate_pr_description(diff: str) -> t.Annotated[str, rg.Ctx("markdown")]: # type: ignore[empty-body]
"""
Analyze the provided git diff and create a PR description in markdown format.
Expand All @@ -40,13 +43,19 @@ async def _run_git_command(args: list[str]) -> str:
"""
# Validate git exists in PATH
git_path = "git" # Could use shutil.which("git") for more security
if not any(os.path.isfile(os.path.join(path, "git")) for path in os.environ["PATH"].split(os.pathsep)):
if not any(
Path(path).joinpath("git").is_file() for path in os.environ["PATH"].split(os.pathsep)
):
raise ValueError("Git executable not found in PATH")

# Validate input parameters
if not all(isinstance(arg, str) for arg in args):
raise ValueError("All command arguments must be strings")

def check_return_code(return_code: int):
if return_code != 0:
raise RuntimeError(f"Git command failed: {stderr.decode()}")

# Use os.execv for more secure command execution
try:
# nosec B603 - Input is validated
Expand All @@ -58,12 +67,15 @@ async def _run_git_command(args: list[str]) -> str:
)
stdout, stderr = await proc.communicate()

if proc.returncode != 0:
raise RuntimeError(f"Git command failed: {stderr.decode()}")
check_return_code(proc.returncode)

return stdout.decode().strip()
except Exception as e:
raise RuntimeError(f"Failed to execute git command: {e}") from e
except FileNotFoundError as e:
raise RuntimeError("Git executable not found or invalid command") from e
except asyncio.SubprocessError as e:
raise RuntimeError("Error occurred while running the subprocess") from e
except UnicodeDecodeError as e:
raise RuntimeError("Failed to decode the output of the git command") from e


async def get_diff(base_ref: str, source_ref: str, *, exclude: list[str] | None = None) -> str:
Expand Down
12 changes: 0 additions & 12 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,3 @@ repos:
entry: .hooks/prettier.sh
language: script
types: [json, yaml]

- id: generate-readme
name: Generate README
description: Updates auto-generated sections in README.md
entry: python scripts/generate_readme.py
language: python
files: ^(pyproject\.toml|templates/README\.md\.j2)$
pass_filenames: false
require_serial: true
additional_dependencies:
- jinja2
- tomli>=2.0.1
104 changes: 96 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,99 @@
# sdk
<p align="center">
<img
src="https://d1lppblt9t2x15.cloudfront.net/logos/5714928f3cdc09503751580cffbe8d02.png"
alt="Logo"
align="center"
width="144px"
height="144px"
/>
</p>

<!-- BEGIN_AUTO_BADGES -->
<div align="center">
<h3 align="center">
Dreadnode Strikes SDK
</h3>

[![Pre-Commit](https://github.com/dreadnode/python-template/actions/workflows/pre-commit.yaml/badge.svg)](https://github.com/dreadnode/python-template/actions/workflows/pre-commit.yaml)
[![Renovate](https://github.com/dreadnode/python-template/actions/workflows/renovate.yaml/badge.svg)](https://github.com/dreadnode/python-template/actions/workflows/renovate.yaml)
<h4 align="center">
<img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/dreadnode">
<img alt="PyPI - Version" src="https://img.shields.io/pypi/v/dreadnode">
<img alt="GitHub License" src="https://img.shields.io/github/license/dreadnode/sdk">
<img alt="Tests" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/tests.yaml">
<img alt="Pre-Commit" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/pre-commit.yaml">
<img alt="Renovate" src="https://img.shields.io/github/actions/workflow/status/dreadnode/sdk/renovate.yaml">
</h4>

</div>
<!-- END_AUTO_BADGES -->
Dreadnode SDK
</br>

Strikes is an platform for building, experimenting with, and evaluating AI security agent code.

- **Experiment + Tasking + Observability** in a single place that's lightweight and scales.
- **Track your data** with parameters, inputs, and outputs all connected to your tasks.
- **Measure everything** with metrics throughout your code and anywhere you need them.
- **Scale your code** from a single run to thousands.

```python
import dreadnode as dn
import rigging as rg

from .tools import reversing_tools

dn.configure()

@dataclass
class Finding:
name: str
severity: str
description: str
exploit_code: str

@dn.scorer(name="Score Finding")
async def score_finding(finding: Finding) -> float:
if finding.severity == "critical":
return 1.0
elif finding.severity == "high":
return 0.8
else:
return 0.2

@dn.task(scorers=[score_finding])
@rg.prompt(tools=[reversing_tools])
async def analyze_binary(binary: str) -> list[Finding]:
"""
Analyze the binary for vulnerabilities.
"""
...

with dn.run(tags=["reverse-engineering"]):
binary = "c2/downloads/service.exe"

dn.log_params(
model="gpt-4",
temperature=0.5,
binary=binary
)

findings = await analyze_binary(binary)

dn.log_metric("findings", len(findings))
```

## Installation

We publish every version to PyPi:
```bash
pip install -U dreadnode
```

If you want to build from source:
```bash
poetry install
```

See our **[installation guide](https://docs.dreadnode.io/strikes/install)** for more options.

## Getting Started

Read through our **[introduction guide](https://docs.dreadnode.io/strikes/intro)** in the docs.

## Examples

Check out **[dreadnode/example-agents](https://github.com/dreadnode/example-agents)** to find your favorite use case.
45 changes: 28 additions & 17 deletions dreadnode/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from .main import DEFAULT_INSTANCE
from .score import Scorer
from .task import Task
from .tracing import RunSpan, Score, Span, TaskSpan
from .version import VERSION
from dreadnode.main import DEFAULT_INSTANCE, Dreadnode
from dreadnode.metric import Metric, MetricDict, Scorer
from dreadnode.object import Object
from dreadnode.task import Task
from dreadnode.tracing.span import RunSpan, Span, TaskSpan
from dreadnode.version import VERSION

configure = DEFAULT_INSTANCE.configure
shutdown = DEFAULT_INSTANCE.shutdown
Expand All @@ -12,29 +13,39 @@
task = DEFAULT_INSTANCE.task
task_span = DEFAULT_INSTANCE.task_span
run = DEFAULT_INSTANCE.run
scorer = DEFAULT_INSTANCE.scorer
task_span = DEFAULT_INSTANCE.task_span
push_update = DEFAULT_INSTANCE.push_update

log_metric = DEFAULT_INSTANCE.log_metric
log_param = DEFAULT_INSTANCE.log_param
log_params = DEFAULT_INSTANCE.log_params
log_score = DEFAULT_INSTANCE.log_score
log_input = DEFAULT_INSTANCE.log_input
log_inputs = DEFAULT_INSTANCE.log_inputs
log_output = DEFAULT_INSTANCE.log_output
link_objects = DEFAULT_INSTANCE.link_objects
log_artifact = DEFAULT_INSTANCE.log_artifact

__version__ = VERSION

__all__ = [
"configure",
"shutdown",
"span",
"task",
"run",
"log_metric",
"log_param",
"Dreadnode",
"Metric",
"MetricDict",
"Object",
"Run",
"Task",
"Scorer",
"RunSpan",
"Score",
"TaskSpan",
"Scorer",
"Span",
"RunSpan",
"Task",
"TaskSpan",
"__version__",
"configure",
"log_metric",
"log_param",
"run",
"shutdown",
"span",
"task",
]
Loading