Skip to content

fix/audit2 019 skill frontmatter injection#1337

Open
tsitu0 wants to merge 2 commits into
BlazeUp-AI:mainfrom
tsitu0:fix/audit2-019-skill-frontmatter-injection
Open

fix/audit2 019 skill frontmatter injection#1337
tsitu0 wants to merge 2 commits into
BlazeUp-AI:mainfrom
tsitu0:fix/audit2-019-skill-frontmatter-injection

Conversation

@tsitu0
Copy link
Copy Markdown
Contributor

@tsitu0 tsitu0 commented Jun 3, 2026

Purpose / Description

Fix AUDIT2-019 (related: OBSV-SEC-042): skill slash-command frontmatter could be injected through unescaped generated fields and insufficient validation of verbatim SKILL.md content.

This is intentionally broader than a one-line escaping fix. Slash commands can enter Observal through multiple paths: API requests, registry-direct SKILL.md content, git-fetched SKILL.md, draft edits, component-version publishing, agent composition, install/config generation, and self-learn generated skills. The fix hardens both sides of the boundary:

  • validate unsafe slash commands before storing them
  • validate verbatim SKILL.md frontmatter before storing or emitting it
  • safely serialize generated fallback frontmatter

Approach

Centralized slash-command normalization (schemas/skill_commands.py):

  • accepted form: ^[a-z0-9][a-z0-9_-]{0,63}$
  • one optional leading / is accepted and normalized away for storage
  • empty string normalizes to None for update/clear semantics
  • spaces, newlines, colons, uppercase, extra slashes, YAML delimiters, and other invalid characters are rejected

Strict validation for stored/verbatim SKILL.md frontmatter:

  • malformed YAML is rejected
  • non-mapping frontmatter is rejected
  • unsafe command: values are rejected
  • API slash_command and frontmatter command: must agree when both are present
  • valid verbatim skill_md_content is preserved unchanged

Applied validation at the relevant ingress/egress points:

  • skill submit/draft/update schemas
  • registry-direct submit parsing
  • git SKILL.md validation
  • draft create/update/submit
  • component-version publishing
  • install/config generation
  • agent-builder skill-file generation
  • self-learn generated skill ingestion

Replaced generated fallback frontmatter string interpolation with yaml.safe_dump() in:

  • services/skill_config_generator.py
  • services/config/skill_builder.py
  • services/insights/self_learn.py

Also fixed empty slash_command update behavior so an explicit empty string clears the stored command.

Note: pre-commit hooks added SPDX headers to touched files and normalized CHANGELOG.md EOF in a separate chore: commit.

How Has This Been Tested?

Focused tests:

uv run pytest tests/test_self_learn_skill_md.py tests/test_component_version_extras.py \
  tests/test_field_validation.py tests/test_skill_config_generator.py \
  tests/test_skill_validator.py -q

Result:

130 passed

Lint:

uv run ruff check observal-server/schemas/skill_commands.py observal-server/schemas/skill.py \
  observal-server/services/skill_validator.py observal-server/api/routes/skill.py \
  observal-server/services/skill_config_generator.py observal-server/services/config/skill_builder.py \
  observal-server/services/component_version_extras.py observal-server/services/insights/self_learn.py \
  tests/test_component_version_extras.py tests/test_field_validation.py tests/test_registry_types.py \
  tests/test_self_learn_skill_md.py tests/test_skill_config_generator.py tests/test_skill_validator.py

Result:

All checks passed!

Whitespace:

git diff --check

Result: passed.

Route-level regression coverage was also added in tests/test_registry_types.py. In this local environment, that file does not collect because jwt is missing:

ModuleNotFoundError: No module named 'jwt'

Learning (optional)

The risky pattern was not only string interpolation in generated YAML. The larger issue was that verbatim SKILL.md content can cross several boundaries before install. For that reason, this patch validates both generated and verbatim frontmatter paths.

For generated fallback files, yaml.safe_dump() emits user-controlled fields as YAML data rather than YAML syntax.

Checklist

  • Descriptive commit message with a short title (fix(security): validate skill slash commands, 41 chars)
  • Commented code in hard-to-understand areas
  • Performed a self-review
  • No UI changes (no screenshots needed)

@github-actions github-actions Bot added server Pull request touches server code tests Pull request adds or modifies tests labels Jun 3, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 3, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

server Pull request touches server code tests Pull request adds or modifies tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant