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
5 changes: 5 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@

# Development
**/AGENTS.md @yuvalbenshalom @ShahafBenYakir

# Marketplace Related - Marketplace PMs only
/Config/approved_tags.json @israelpoli @demisto/content-leaders
/Config/approved_usecases.json @israelpoli @demisto/content-leaders
Expand Down Expand Up @@ -188,3 +192,4 @@ Packs/ApiModules/Scripts/DemistoClassApiModule/DemistoClassApiModule.py @dantavo
/Packs/MicrosoftGraphDeviceManagement @Moish-Gilboa
/Packs/MicrosoftIntune @Moish-Gilboa
/Packs/AzureSecurityCenter @Moish-Gilboa

73 changes: 73 additions & 0 deletions .github/scripts/check_invisible_chars.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env python3
"""Detect invisible Unicode characters in files.

Usage: python check_invisible_chars.py <file1> [file2] ...
Exit codes: 0 = clean, 1 = issues found
"""

import sys
import unicodedata

INVISIBLE_RANGES = [
(0x0000, 0x0008), # Control chars
(0x000B, 0x000C), # VT, FF
(0x000E, 0x001F), # Control chars
(0x007F, 0x007F), # DEL
(0x00A0, 0x00A0), # Non-breaking space
(0x2000, 0x200F), # Various spaces + zero-width chars
(0x2028, 0x202F), # Separators + narrow no-break space
(0x2060, 0x206F), # Invisible operators
(0xFEFF, 0xFEFF), # BOM
]


def log(msg: str) -> None:
"""Write message to stderr (allowed by ruff)."""
sys.stderr.write(msg + "\n")


def check_file(path: str) -> tuple[list[tuple[int, int, str]], str | None]:
"""Return (issues, error) where issues is list of (line, col, char_desc)."""
issues: list[tuple[int, int, str]] = []
try:
with open(path, encoding="utf-8", errors="strict") as f:
for line_num, line in enumerate(f, 1):
for col, char in enumerate(line, 1):
code = ord(char)
if any(start <= code <= end for start, end in INVISIBLE_RANGES):
try:
name = unicodedata.name(char)
except ValueError:
name = "UNKNOWN"
issues.append((line_num, col, f"U+{code:04X} ({name})"))
except UnicodeDecodeError as e:
return [], f"UTF-8 decode error: {e}"
except OSError as e:
return [], f"File error: {e}"
return issues, None


def main() -> int:
if len(sys.argv) < 2:
log("No files provided")
return 0

failed = False
for path in sys.argv[1:]:
log(f"Checking: {path}")
issues, error = check_file(path)
if error:
log(f" ::error file={path}::{error}")
failed = True
elif issues:
failed = True
for line, col, desc in issues:
log(f" ::error file={path},line={line},col={col}::{desc}")
else:
log(" ✓ Clean")

return 1 if failed else 0


if __name__ == "__main__":
sys.exit(main())
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@ pipeline_jobs_folder/
# Ignore windsurf config
.windsurf

# Ignore AI configuration file
AGENTS.md
# Ignore AI generated dump files
plan.md
report.md
plans/
Expand Down
6 changes: 6 additions & 0 deletions .pre-commit-config_template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ repos:

- repo: local
hooks:
- id: check-invisible-chars
name: Check AGENTS.md for invisible characters
entry: python3 .github/scripts/check_invisible_chars.py
language: system
files: "(^|/)AGENTS\\.md$"

- id: mdx-validation
name: Validate README.md files with MDX
entry: node .hooks/validate-mdx.js
Expand Down
91 changes: 91 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Cortex Platform Content Repository

This repository contains the content (Integrations, Scripts, Playbooks, Reports, Modeling Rules, Parsing Rules) for the Cortex Platform (XSOAR, XSIAM, etc.).

This file provides guidance to agents when working with code in this repository.

## Codebase Introduction

This project uses `demisto-sdk` as the primary CLI for all development tasks.

- **Linting & Testing**: `demisto-sdk pre-commit -i <path>` (runs in Docker, replaces `lint`)
- **Formatting**: `demisto-sdk format -i <path>` (fixes style/lint issues)
- **Validation**: `demisto-sdk validate -i <path>`

## Commands

```bash
# Run lint, tests, and validation (single file/dir) - MUST run in Docker
demisto-sdk pre-commit -i Packs/MyPack/Integrations/MyInt/

# Format code (fixes many lint errors automatically)
demisto-sdk format -i Packs/MyPack/Integrations/MyInt/MyInt.py

# Validate content against XSOAR standards (also run by pre-commit)
demisto-sdk validate -i Packs/MyPack/Integrations/MyInt/

# Run pre-commit hooks on all files
demisto-sdk pre-commit -a
```

## Non-Obvious Project Rules

- **Runtime Injection**: `CommonServerPython` is injected at runtime and must be imported: `from CommonServerPython import *`. Note: `CommonServerUserPython` is also injected but is typically not imported explicitly by integrations.
- **Imports**: MUST import `demistomock as demisto` at the top of every integration/script.
- **Docker Dependency**: Dependencies are managed via Docker images defined in the `.yml` file, not local `pip`. You cannot `pip install` in the runtime environment.
- **Test Execution**: Standard `pytest` often fails due to missing runtime context. Use `demisto-sdk pre-commit` which sets up the correct Docker environment.
- **Error Handling**: Use `return_error("message")` for user-facing errors. Only raise exceptions for unexpected failures.
- **Outputs**: Use `CommandResults` objects with `return_results()`: `return_results(CommandResults(outputs_prefix='MyPrefix', outputs=data))`. Output keys should be CamelCase.
- **Logging**: Use `demisto.debug()` and `demisto.info()`. Avoid `print()`.

## Architecture Notes

- **Pack Structure**: `Packs/<PackName>/<Entity>/<EntityName>/`. Entities include `Integrations`, `Scripts`, `Playbooks`, `ModelingRules`, `ParsingRules`, `XDRCTemplates`, etc. Flat structures are forbidden.
- **Metadata**: Every pack requires `pack_metadata.json`. Every entity requires a YAML/JSON configuration file.
- **Versioning**: Changes require a new entry in `Packs/<PackName>/ReleaseNotes/<Version>.md`.
- **Isolation**: Integrations are stateless and run in isolated containers. No shared state between executions.
- **Source of Truth**: `xsoar.pan.dev` is the official documentation.

## Instructions

### Code Style

- **Variable Names**: Use descriptive, self-explanatory names.
- **Type Hints**: Always use type hints. `mypy` is enforced.
- **Formatting**:
- `demisto-sdk format` is MANDATORY. It fixes YAML structure, JSON formatting, and Python style.
- **Parameters**: Use `demisto.params()` for configuration and `demisto.args()` for command arguments.
- **Function Size**: Keep functions small (~30 lines) and focused on a single responsibility.
- **Conditionals**: Use early returns (guard clauses) to avoid deep nesting.

### Workflow for complex tasks

1. **Explore**
- Understand existing implementation and patterns.
- Check `Templates/` for reference implementations.

2. **Plan**
- List files to create/modify.
- Identify necessary Docker image dependencies.
- Plan for `pack_metadata.json` and `ReleaseNotes`.

3. **Implement**
- Start with core logic in `.py` file.
- Define commands/args in`.yml` file.

4. **Test**
- Create `_test.py` file.
- Use `demistomock` and `requests_mock`.
- Run `demisto-sdk pre-commit -i <path>`.

5. **Self-review**
- Check for hardcoded values.
- Ensure `CommandResults` and `return_results()` are used correctly.
- Verify `dockerimage` in YAML matches requirements.

## Principles

- **Statelessness**: Integrations must be stateless.
- **Isolation**: Each execution is independent.
- **Security**: No hardcoded credentials. Use `demisto.params()`.
- **Clarity**: Human-readable outputs (`tableToMarkdown`) are as important as machine-readable context.
Loading