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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ All notable changes follow [Keep a Changelog](https://keepachangelog.com/en/1.1.
- Terminal, JSON, Markdown, and SARIF 2.1.0 reporting.
- Configurable severity thresholds, rule suppression, severity overrides, and module/entry-point plugins.
- Docker image, typed Python package, tests, CI, CodeQL, dependency review, release workflow, and open-source governance files.
- `.mts`/`.cts` source discovery: these now map to the `typescript` language, so existing
TypeScript-aware rules (AG002, AG004, AG006, AG007, etc.) run against them with no
rule-level changes.

[Unreleased]: https://github.com/amic25/agentguard/commits/main
[0.1.0]: # (never published - no tag exists)
5 changes: 0 additions & 5 deletions docs/GOOD_FIRST_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,3 @@ Create a dependency-free plain reporter for CI logs that disable color. Preserve

**Labels:** `good first issue`, `documentation`
Add a tested GitLab CI example under `docs/integrations/`. Explain exit thresholds and artifact retention without claiming native features AgentGuard does not provide.

## Recognize `.mts` and `.cts`

**Labels:** `good first issue`, `javascript`, `scanner`
Treat modern TypeScript module extensions as TypeScript sources. Add discovery tests and update the supported file list.
2 changes: 2 additions & 0 deletions src/agentguard/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
".cjs": "javascript",
".ts": "typescript",
".tsx": "typescript",
".mts": "typescript",
".cts": "typescript",
".json": "manifest",
".txt": "manifest",
".toml": "manifest",
Expand Down
11 changes: 11 additions & 0 deletions tests/corpus/manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ true_positives:
expect: [AG002]
why: execSync on a caller-supplied command in TypeScript.

agent_prompt.mts:
origin: written
expect: [AG004]
why: >-
web_content interpolated into a template-literal prompt in a `.mts` (ESM TypeScript)
module. `.mts`/`.cts` map to the `typescript` language in LANGUAGES, and every
TypeScript-aware rule must declare `typescript` in RuleMetadata.languages to see
these files at all - this pins that AG004 specifically stays wired up, not just
AG002 (already covered by agent_tools.ts), so a rule that forgets to list
`typescript` fails the benchmark instead of silently skipping .mts/.cts files.

.env:
origin: written
expect: [AG001]
Expand Down
7 changes: 7 additions & 0 deletions tests/corpus/true_positives/agent_prompt.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// ESM TypeScript module (.mts) - web content interpolated directly into a prompt.
import { callModel } from "./model.js";

export async function summarize(web_content: string): Promise<string> {
const prompt = `Summarize the following page for the user: ${web_content}`;
return callModel(prompt);
}
40 changes: 40 additions & 0 deletions tests/test_discovery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Source discovery and language mapping tests."""

from __future__ import annotations

from pathlib import Path

import pytest

from agentguard.config import Config
from agentguard.scanner import LANGUAGES, Scanner


@pytest.mark.parametrize("extension", [".mts", ".cts"])
def test_discovers_modern_typescript_extension(project: Path, extension: str) -> None:
(project / f"agent{extension}").write_text("execSync(command)", encoding="utf-8")
result = Scanner().scan(project)
assert result.files_scanned == 1
assert "AG002" in {finding.rule_id for finding in result.findings}


@pytest.mark.parametrize("extension", [".mts", ".cts"])
def test_modern_typescript_extension_maps_to_typescript(extension: str) -> None:
assert LANGUAGES[extension] == "typescript"


@pytest.mark.parametrize("extension", [".mts", ".cts"])
def test_excludes_vendor_directory_for_modern_typescript(project: Path, extension: str) -> None:
vendor = project / "node_modules"
vendor.mkdir()
(vendor / f"bad{extension}").write_text("execSync(command)", encoding="utf-8")
result = Scanner().scan(project)
assert result.files_scanned == 0
assert result.findings == []


@pytest.mark.parametrize("extension", [".mts", ".cts"])
def test_skips_oversized_modern_typescript_file(project: Path, extension: str) -> None:
(project / f"large{extension}").write_text("x" * 2000, encoding="utf-8")
result = Scanner(Config(max_file_size_kb=1)).scan(project)
assert result.skipped_files == 1