Skip to content
Open
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
40 changes: 40 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Deploy Docs

on:
push:
branches:
- main
- test-docs # Add your test branch here
Comment on lines +5 to +7
workflow_dispatch:

permissions:
contents: write

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semgrep identified an issue in your code:

GitHub Actions step uses mutable version tag @v4 instead of pinned commit SHA, allowing attackers to silently redirect the action to malicious code.

More details about this

The actions/checkout step uses version tag @v4 instead of a pinned commit SHA. Version tags and branch references in GitHub Actions can be silently repointed by the action's owner to malicious code without any update to your workflow file. This enables supply-chain attacks where a compromised maintainer could redirect @v4 to inject malicious code that exfiltrates repository secrets or modifies your build output.

Exploit scenario:

  1. An attacker gains control of the actions/checkout repository and force-pushes the v4 tag to point to a commit containing malicious code
  2. When your workflow runs, it automatically uses this new malicious version due to the mutable tag reference
  3. The malicious checkout action could execute arbitrary code with access to GITHUB_TOKEN, allowing the attacker to read your repository secrets or push backdoors to your main branch
  4. Your workflow file never changed—the attack is silent and difficult to detect

Pin to a full 40-character commit SHA (e.g., actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608) to ensure you always run the exact version you tested.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
- uses: actions/checkout@v4
# Pinned to commit SHA to prevent supply-chain attacks. See: https://github.com/actions/checkout/commits/v4
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
View step-by-step instructions
  1. Find the commit SHA for the exact version of the action you want to use. You can find this by visiting the action's repository (for example, https://github.com/actions/checkout) and looking at the "commits" tab or in the release/tag list.
  2. Replace uses: actions/checkout@v4 with uses: actions/checkout@<commit-sha> where <commit-sha> is the full 40-character commit hash you found in step 1 (for example: uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608).
  3. Repeat these steps for any other GitHub Actions referenced by branch or tag names (such as v4, v3, main, or similar).
    Pinning to a full commit SHA ensures the workflow always uses the same, reviewed code and protects against supply-chain attacks resulting from upstream changes.
💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by github-actions-mutable-action-tag.

Need help with this issue? Consult our Semgrep Findings Documentation or ask in #help-appsec on Slack.

You can view more details about this finding in the Semgrep AppSec Platform.

with:
fetch-depth: 0 # Fetch all history for proper git info

- name: Set up Python
uses: actions/setup-python@v5
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semgrep identified an issue in your code:

GitHub Actions step actions/setup-python@v5 uses a mutable version tag that could be silently redirected to malicious code by an attacker, enabling supply-chain attacks on your build pipeline.

More details about this

The GitHub Actions workflow uses actions/setup-python@v5, which references a mutable major version tag instead of a pinned commit SHA.

How an attacker could exploit this:

  1. An attacker compromises the GitHub Actions setup-python repository or the account that maintains it.
  2. They force-push the v5 tag to point to a malicious commit containing backdoor code, perhaps a script that exfiltrates repository secrets like GITHUB_TOKEN or environment variables.
  3. When your workflow runs, the runner automatically fetches the latest commit for the v5 tag without any warning or verification.
  4. The malicious setup-python@v5 action runs in your workflow with full access to your repository's secrets and the ability to execute arbitrary commands.
  5. The attacker can steal secrets, modify your codebase, or use your build environment to attack downstream dependencies.

This exact scenario happened with the trivy-action and kics-github-action compromises, where attackers gained control of version tags and silently injected malicious code into thousands of workflows.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
uses: actions/setup-python@v5
uses: actions/setup-python@f67e31fc8fd0c1e758ff2da11321c39dd442cafd # v5.0.0
View step-by-step instructions
  1. Replace the mutable version tag in uses: actions/setup-python@v5 with a specific 40-character commit SHA that corresponds to the version you want to use. For example, change it to uses: actions/setup-python@<commit-sha> and add a comment with the version for reference, like # v5.x.x.
  2. To find the latest commit SHA, visit https://github.com/actions/setup-python/releases, choose your intended release, and copy its commit SHA.
  3. Use the new pinned reference, e.g. uses: actions/setup-python@a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0 # v5.0.0.

Pinning actions to a full-length commit SHA protects against supply-chain attacks because the action code cannot be changed by the action owner without you knowing.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by github-actions-mutable-action-tag.

Need help with this issue? Consult our Semgrep Findings Documentation or ask in #help-appsec on Slack.

You can view more details about this finding in the Semgrep AppSec Platform.

with:
python-version: '3.10'

- name: Install dependencies
run: |
pip install mkdocs-material mkdocstrings[python] pymdown-extensions

- name: Build docs
run: |
mkdocs build
touch site/.nojekyll

- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semgrep identified an issue in your code:

GitHub Actions workflow uses version tag v4 instead of a pinned commit SHA, allowing the action to be swapped by an attacker who compromises the repository. Pinning to a full commit SHA ensures immutability and prevents malicious code injection.

More details about this

The GitHub Actions workflow uses peaceiris/actions-gh-pages pinned only to the v4 version tag instead of a specific commit SHA. Version tags can be moved or retagged by the maintainer, meaning the same tag could point to different code over time.

Exploit scenario:

  1. An attacker compromises the peaceiris/actions-gh-pages repository (or gains write access to it).
  2. They force-push a malicious commit and retag it as v4, replacing the code that currently runs in your workflow.
  3. The next time your workflow executes, the uses: peaceiris/actions-gh-pages@v4 line pulls the attacker's code instead.
  4. The malicious action now runs with access to secrets.GITHUB_TOKEN and publish_dir: ./site, allowing the attacker to steal your GitHub token, modify your site content, or compromise your repository and other actions.

By pinning to a full commit SHA (like uses: peaceiris/actions-gh-pages@a3523ae0e4cd1f5299b8aa5e37255467ec0de1ad), you lock to an immutable version that cannot be changed even if the repository is compromised.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
uses: peaceiris/actions-gh-pages@v4
uses: peaceiris/actions-gh-pages@d5007f704b97bcc11256c735197c9abb76aa19e6 # v4.0.0
View step-by-step instructions
  1. Find the specific commit SHA for the version of peaceiris/actions-gh-pages you want to use by checking the GitHub repository, for example at https://github.com/peaceiris/actions-gh-pages/releases or by running $ git ls-remote https://github.com/peaceiris/actions-gh-pages.git v4.
  2. Replace uses: peaceiris/actions-gh-pages@v4 with uses: peaceiris/actions-gh-pages@<commit-sha>, where <commit-sha> is the full 40-character commit hash.
    For example: uses: peaceiris/actions-gh-pages@abcdef1234567890abcdef1234567890abcdef12
  3. Optionally, add a comment after the SHA noting the human-readable version, e.g. # v4.0.0, to clarify which release you are pinning.

Pinning to a specific commit ensures that your workflow cannot be modified by upstream changes or supply chain attacks unless you explicitly update the SHA.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by third-party-action-not-pinned-to-commit-sha.

Need help with this issue? Consult our Semgrep Findings Documentation or ask in #help-appsec on Slack.

You can view more details about this finding in the Semgrep AppSec Platform.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semgrep identified an issue in your code:

The peaceiris/actions-gh-pages@v4 step uses a mutable version tag that could be silently repointed to malicious code, compromising the GITHUB_TOKEN passed to it and enabling repository takeover.

More details about this

The GitHub Actions workflow step uses: peaceiris/actions-gh-pages@v4 references the action using a mutable version tag (v4) instead of a pinned commit SHA.

Exploit scenario:

  1. An attacker compromises the peaceiris/actions-gh-pages repository on GitHub (or socially engineers the maintainer).
  2. The attacker updates the v4 tag to point to a malicious commit containing code that exfiltrates ${{ secrets.GITHUB_TOKEN }} (which is passed to this action).
  3. Your workflow runs and automatically uses the updated malicious version because the tag is mutable.
  4. The attacker now has a valid GITHUB_TOKEN with contents: write permissions and can push malicious code to your repository, modify your documentation site, or access other repositories you have access to.

This is especially dangerous here because the action receives github_token: ${{ secrets.GITHUB_TOKEN }}, giving the compromised action full write access to your repository. Similar attacks have already occurred in the wild (e.g., trivy-action and kics-github-action compromises).

To resolve this comment:

✨ Commit fix suggestion

Suggested change
uses: peaceiris/actions-gh-pages@v4
uses: peaceiris/actions-gh-pages@13b7eb9eebd81f2444ea0ae2ebc6aa270d9f1a38 # v4.0.0
View step-by-step instructions
  1. Visit the peaceiris/actions-gh-pages GitHub repository and locate the specific release or commit SHA you want to use (see https://github.com/peaceiris/actions-gh-pages/tags).
  2. Copy the full 40-character commit SHA for the desired version (for example, the latest v4 release).
  3. Replace uses: peaceiris/actions-gh-pages@v4 with uses: peaceiris/actions-gh-pages@<commit-sha>, where <commit-sha> is the commit you copied. For example: uses: peaceiris/actions-gh-pages@13b7eb9eebd81f2444ea0ae2ebc6aa270d9f1a38 # v4.0.0.
  4. Optionally, add a comment with the tag information for clarity, like # v4.0.0, after the commit SHA.

Pinning to a commit SHA ensures your workflow always uses the trusted code, even if someone changes or repoints the tag or branch.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by github-actions-mutable-action-tag.

Need help with this issue? Consult our Semgrep Findings Documentation or ask in #help-appsec on Slack.

You can view more details about this finding in the Semgrep AppSec Platform.

with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./site
cname: false # Remove if you want a custom domain
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,5 @@ getting_started/examples/partners/.strands_sessions/

# Local Makefile overrides
Makefile.local
/site/
*.pyc
166 changes: 166 additions & 0 deletions DOCS_CONTROL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Controlling Auto-Generated Documentation

API documentation is automatically generated from Python source code using mkdocstrings. This guide explains how to control what appears in the docs.

## Quick Reference

**To hide something from docs:**
- Add `_` prefix to method/function name (e.g., `_internal_method`)
- Remove from `__all__` list in module's `__init__.py`
- Don't add a docstring (filtered automatically)

**To show something in docs:**
- Add to `__all__` list in module's `__init__.py`
- Make it public (no `_` prefix)
- Add a docstring

## Method 1: Control with __all__ (Module Level)

The `__all__` list in each module's `__init__.py` controls which classes/functions appear in docs.

### Example: Hide a class from channels

```python
# src/tac/channels/__init__.py
__all__ = [
"VoiceChannel",
"SMSChannel",
# "RCSChannel", ← Comment out to hide from docs
"WhatsAppChannel",
"ChatChannel",
]
```

### Files to Edit:

| Module | File | Controls |
|--------|------|----------|
| Core | `src/tac/core/__init__.py` | TAC, TACConfig, get_logger |
| Channels | `src/tac/channels/__init__.py` | VoiceChannel, SMSChannel, etc. |
| Adapters | `src/tac/adapters/__init__.py` | with_tac_memory |
| Server | `src/tac/server/__init__.py` | TACFastAPIServer |
| Models | `src/tac/models/__init__.py` | TwiMLOptions, etc. |

## Method 2: Use Underscore Prefix (Method Level)

Methods/functions starting with `_` are considered private and hidden from docs.

```python
class TAC:
def on_message_ready(self):
"""Public method - shows in docs."""
pass

def _internal_helper(self):
"""Private method - hidden from docs."""
pass
```

### Example: Hide is_orchestrator_enabled

**Before:**
```python
def is_orchestrator_enabled(self) -> bool:
"""Check if orchestrator is enabled."""
```

**After:**
```python
def _is_orchestrator_enabled(self) -> bool:
"""Internal: Check if orchestrator is enabled."""
```

## Method 3: Remove Docstring

Methods without docstrings are automatically hidden (configured in `mkdocs.yml`).

```python
def internal_method(self):
# No docstring = hidden from docs
pass
```

## Current Filters (mkdocs.yml)

These filters are already configured:

```yaml
filters:
- "!^_" # Hide _private methods
- "!^model_" # Hide Pydantic internals (model_dump, model_config, etc.)
show_if_no_docstring: false # Hide undocumented members
```

## Typical Workflow

### 1. Adding a new public class

```python
# src/tac/channels/email.py
class EmailChannel:
"""Send messages via email."""

def __init__(self, tac):
"""Initialize email channel."""
self.tac = tac
```

```python
# src/tac/channels/__init__.py
__all__ = [
"VoiceChannel",
"SMSChannel",
"EmailChannel", # ← Add here to show in docs
]
```

### 2. Hiding an internal method

Just add `_` prefix - no other changes needed:

```python
class TAC:
def retrieve_memory(self):
"""Public API - shows in docs."""
return self._fetch_from_api()

def _fetch_from_api(self):
"""Internal implementation - hidden."""
pass
```

### 3. Marking something as experimental

Keep it public but note in docstring:

```python
def new_feature(self):
"""Experimental feature.

Warning:
This API is experimental and may change in future versions.
"""
```

## Rebuilding Docs

After changing `__all__` or method names:

```bash
# Local preview
uv run mkdocs serve

# Build for deployment
uv run mkdocs build
```

Changes are automatically picked up when you push to the `main` branch (via GitHub Actions).

## Summary

**Most common controls:**
1. **Hide class:** Remove from `__all__` in `__init__.py`
2. **Hide method:** Add `_` prefix
3. **Hide undocumented:** Don't add docstring (automatic)

**All control happens in Python source code** - no need to maintain separate doc files!
36 changes: 36 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# API Reference

## Core

::: tac.core
options:
show_root_heading: false
show_source: false

## Channels

::: tac.channels
options:
show_root_heading: false
show_source: false

## Adapters

::: tac.adapters
options:
show_root_heading: false
show_source: false

## Server

::: tac.server
options:
show_root_heading: false
show_source: false

## Models

::: tac.models
options:
show_root_heading: false
show_source: false
1 change: 1 addition & 0 deletions docs/index.md
23 changes: 23 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
site_name: Twilio Agent Connect Python SDK
site_url: https://twilio.github.io/twilio-agent-connect-python/
repo_url: https://github.com/twilio/twilio-agent-connect-python

theme:
name: material

plugins:
- search
- mkdocstrings:
handlers:
python:
paths: [src]
options:
show_source: false
filters:
- "!^_"
- "!^model_"
show_if_no_docstring: false

nav:
- Home: index.md
- API Reference: api.md