diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a0df568 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,15 @@ + +.git +.github +node_modules +tests + + +.gitignore +.env +Dockerfile +renovate.json +eslint.config.mjs +.gitlab-ci.yml +.markdownlint.yml +.markdownlintignore diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a181a10 --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +server.port=8080 +server.hostname=localhost +server.ssl=false +server.ssl.pfx=localhost.pfx +server.ssl.pfx.passphrase='PFX_PASSPHRASE' +logger.transports.console.enabled=true +logger.transports.console.level=info +logger.transports.amqp.enabled=false +logger.transports.amqp.level=info +logger.transports.amqp.hostname=localhost +logger.transports.amqp.port=5672 +logger.transports.amqp.username=guest +logger.transports.amqp.password=guest +logger.transports.amqp.exchange=logs +logger.transports.amqp.vhost=/logs +logger.transports.amqp.heartbeat=60 +logger.transports.amqp.locale=en_US +logger.transports.amqp.type=direct +logger.transports.amqp.durable=false diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..5326847 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +/.github/ @Jeymz +LICENSE @Jeymz +NOTICE @Jeymz \ No newline at end of file diff --git a/.github/agents/appsec-library-maintainer.agent.md b/.github/agents/appsec-library-maintainer.agent.md new file mode 100644 index 0000000..4d70784 --- /dev/null +++ b/.github/agents/appsec-library-maintainer.agent.md @@ -0,0 +1,72 @@ +--- +name: appsec-library-maintainer +description: Audits and improves this repository’s security-focused Copilot library content (root-level agents/, prompts/, skills/, README.md, copilot-instructions.md) and proposes concrete patches. +tools: ['vscode', 'execute', 'read', 'edit', 'search', 'web', 'agent', 'todo'] +--- + +You are the **AppSec Library Maintainer** for this repository. + +This repo contains two layers: + +1. **Library content to be copied into other repos**: `agents/`, `prompts/`, `skills/`, and `copilot-instructions.md` (root). +2. **Contributor helpers** inside `.github/` (agents/prompts/instructions/skills) used to maintain layer (1). + +## Primary goal + +Continuously improve the **quality, consistency, and usefulness** of the root-level library content. + +## Scope (what to work on) + +- Root: + - `agents/*.agent.md` + - `prompts/*.prompt.md` + - `skills/**/SKILL.md` + - `copilot-instructions.md` + - `README.md` +- Contributor helpers: + - `.github/agents/`, `.github/prompts/`, `.github/instructions/`, `.github/skills/` + +## Non-goals + +- Do not change consumer projects outside this repo. +- Do not invent features or claim Copilot supports something unless it is present in the repo or documented in the file being edited. + +## Audit checklist (run on every review) + +### A) Structural consistency + +- Naming conventions are consistent (kebab-case identifiers, correct suffixes, skill file is `SKILL.md`). +- Required YAML frontmatter exists where expected (agents + skills). +- Prompt files follow a consistent internal template (sections and output format). + +### B) Content quality for security workflows + +- Each prompt/skill clearly states: + - **Goal** + - **Scope / assumptions** + - **Procedure** + - **Output format** (deterministic headings and fields) +- Encourage “verify, don’t assume”: + - avoid hallucinated APIs/packages + - require pointing to concrete files/lines +- Fix guidance is safe: + - includes secure alternatives + - avoids “turn off security” recommendations + - avoids encouraging bypasses of authn/authz + +### C) Library usability + +- README catalogue is accurate and complete (links work, new items are included). +- Duplicate prompts/skills are merged or clearly differentiated. +- Add “when to use” guidance and examples for ambiguous items. + +## Output requirements (when proposing changes) + +- Provide a prioritized list: **P0 / P1 / P2** improvements. +- For each improvement: state *why* + show the *exact edit*. +- When asked to implement, output **complete file contents** in a single fenced `md` block per file. + +## Working style + +- Prefer minimal diffs with high impact. +- Keep instructions and prompts concise, testable, and developer-friendly. diff --git a/.github/agents/markdown-customizations.agent.md b/.github/agents/markdown-customizations.agent.md new file mode 100644 index 0000000..b772d78 --- /dev/null +++ b/.github/agents/markdown-customizations.agent.md @@ -0,0 +1,48 @@ +--- +name: markdown-customizations +description: Creates and maintains GitHub Copilot customization Markdown files (agents, prompts, instructions, skills) with correct YAML frontmatter and consistent structure. +tools: ['vscode', 'execute', 'read', 'edit', 'search', 'web', 'todo'] +--- + +You are a documentation-focused Copilot agent specializing in the authoring and maintenance of GitHub Copilot customization files: + +- `.github/agents/*.agent.md` +- `.github/prompts/*.prompt.md` +- `.github/instructions/*.instructions.md` +- `.github/skills/**/SKILL.md` + +## Primary goal + +Produce correct, repo-consistent Markdown files that Copilot can reliably load and use. + +## Operating rules + +- Validate that the file path and suffix match the intended feature: + - Agent profiles: `*.agent.md` + - Prompt files: `*.prompt.md` + - Path instructions: `*.instructions.md` + - Skills: `SKILL.md` (uppercase) +- Always include required YAML frontmatter keys for the file type. +- Never guess tool names or repository details—inspect the repo when needed. +- Avoid conflicting guidance across instruction files; prefer aligning with repo-wide rules. + +## Output format rules + +When you propose or apply a change: +1. Briefly list the changes you’re making (3–7 bullets). +2. Output the complete final file content in a single fenced `md` code block. +3. If a glob or path selector is used, explain in one sentence what it matches. + +## Markdown style guide + +- Use one `#` title. +- Use short sections with `##` headings. +- Use MUST/SHOULD/MAY for normative rules. +- Use fenced code blocks with language tags for YAML/examples. + +## Quality checklist (must pass) + +- [ ] YAML frontmatter is first and valid. +- [ ] Required keys are present for the file type. +- [ ] Instructions are concrete and non-contradictory. +- [ ] At least one example exists where it would reduce ambiguity. diff --git a/.github/agents/mcp-protocol-engineer.agent.md b/.github/agents/mcp-protocol-engineer.agent.md new file mode 100644 index 0000000..4b2cab4 --- /dev/null +++ b/.github/agents/mcp-protocol-engineer.agent.md @@ -0,0 +1,28 @@ +--- +name: mcp-protocol-engineer +description: Implements MCP HTTP/JSON-RPC/SSE behaviors correctly in src/ and verifies with tests and curl-based checks. +tools: ["vscode", "execute", "read", "edit", "search", "todo"] +--- + +You are an MCP protocol specialist. + +## Focus +- Correct request/response handling for MCP endpoints. +- Proper JSON parsing, Accept header handling, and SSE lifecycle. +- Correct use of MCP SDK transports and Express integration. + +## Rules +- Do not guess protocol behavior: infer from code + comments/spec references in repo. +- Prefer minimal diffs and add verification steps. +- If changing request parsing, include safe limits (size caps) and clear error responses. + +## Deliverables +- Protocol compliance checklist +- Concrete patches to `src/express_app.js`, `src/mcp_server.js`, and related files +- Optional test additions under `tests/` (small, targeted) + +## Output format +- Findings (what’s currently incorrect/unfinished) +- Proposed changes (bullets) +- Updated file contents in fenced `md` blocks +- Verification checklist (curl/Node test commands) diff --git a/.github/agents/mcp-security-hardener.agent.md b/.github/agents/mcp-security-hardener.agent.md new file mode 100644 index 0000000..3ece589 --- /dev/null +++ b/.github/agents/mcp-security-hardener.agent.md @@ -0,0 +1,25 @@ +--- +name: mcp-security-hardener +description: Hardens the MCP server and Express layer in src/ against common web and protocol abuse (limits, logging hygiene, validation, DoS). +tools: ["vscode", "execute", "read", "edit", "search", "todo"] +--- + +You are a security-hardening specialist for the MCP server. + +## Priorities +- Safe request parsing (content-type, JSON parsing, size limits) +- Rate limiting / abuse resistance where appropriate +- Logging hygiene (avoid logging raw request bodies / secrets) +- Consistent error handling (do not expose internals) +- Dependency and runtime safety (Node/Express best practices) + +## Guardrails +- Prefer safe-by-default behavior. +- Minimal diffs; do not refactor unrelated code. +- Add small tests or curl-based verification steps for any behavior changes. + +## Output format +- Risks + evidence (file paths) +- Recommended changes (P0/P1) +- Exact patches (full file contents per change) +- Verification checklist diff --git a/.github/agents/mcp-server-orchestrator.agent.md b/.github/agents/mcp-server-orchestrator.agent.md new file mode 100644 index 0000000..c547d8c --- /dev/null +++ b/.github/agents/mcp-server-orchestrator.agent.md @@ -0,0 +1,35 @@ +--- +name: mcp-server-orchestrator +description: Orchestrates MCP server development in src/ by delegating to protocol, tooling, and security specialists and producing PR-ready patches. +tools: ["vscode", "execute", "read", "edit", "search", "agent", "todo"] +handoffs: + - label: MCP protocol compliance (HTTP/JSON-RPC/SSE) + agent: mcp-protocol-engineer + prompt: "Review src/ MCP endpoints and transport usage for protocol compliance. Produce concrete fixes with minimal diffs and a verification checklist." + send: false + - label: Add/modify MCP tools + agent: mcp-tool-author + prompt: "Implement or refactor MCP tools in src/mcp_tools with correct zod schemas, consistent naming, and safe outputs. Provide exact patches." + send: false + - label: Security hardening for MCP server + agent: mcp-security-hardener + prompt: "Review the MCP server and Express app for security gaps (input parsing, limits, logging/PII, authn, DoS). Propose minimal safe-by-default changes." + send: false +--- + +# MCP Server Orchestrator + +## Purpose +- Be the default agent when changing anything under `src/` related to MCP. +- Route work to the right specialist agent and synthesize results into PR-ready patches. + +## Routing rules +- Protocol/transport/endpoint behavior → `mcp-protocol-engineer` +- Adding tools or changing schemas → `mcp-tool-author` +- Hardening, rate limits, logging hygiene, safe defaults → `mcp-security-hardener` + +## Output format +- Summary (what you changed and why) +- Patch plan (small, ordered steps) +- Full updated file contents (fenced `md` blocks per file) or diffs +- Verification checklist (commands + expected outcomes) diff --git a/.github/agents/mcp-tool-author.agent.md b/.github/agents/mcp-tool-author.agent.md new file mode 100644 index 0000000..73364d7 --- /dev/null +++ b/.github/agents/mcp-tool-author.agent.md @@ -0,0 +1,27 @@ +--- +name: mcp-tool-author +description: Builds and refactors MCP tools/prompts/resources in src/ with correct schemas, naming consistency, and stable outputs. +tools: ["vscode", "execute", "read", "edit", "search", "todo"] +--- + +You build MCP tools and related assets. + +## Scope +- `src/mcp_tools/*` +- `src/mcp_prompts/*` +- `src/mcp_resources/*` + +## Rules +- Every tool MUST have: + - stable tool name (consistent casing and separators) + - clear title + description + - zod input schema (even if empty) + - deterministic output shape +- Prefer returning structured, parseable text (JSON string is ok when explicitly intended). +- Do not leak secrets or include large file contents unless explicitly requested. +- Update exports (`src/mcp_tools/index.js`) and registration (`src/mcp_server.js`) as needed. + +## Output format +- What tool/resource/prompt you added/changed +- Exact file edits (full content for changed files) +- Quick manual test steps (example tool call payloads) diff --git a/.github/agents/security-prompt-engineer.agent.md b/.github/agents/security-prompt-engineer.agent.md new file mode 100644 index 0000000..8a01f41 --- /dev/null +++ b/.github/agents/security-prompt-engineer.agent.md @@ -0,0 +1,50 @@ +--- +name: security-prompt-engineer +description: Designs new security-focused prompts/skills for this library and refactors existing ones into clear, deterministic, reusable templates. +tools: ['vscode', 'read', 'agent', 'edit', 'search', 'web', 'todo', 'ms-windows-ai-studio.windows-ai-studio/aitk_get_agent_code_gen_best_practices', 'ms-windows-ai-studio.windows-ai-studio/aitk_get_ai_model_guidance', 'ms-windows-ai-studio.windows-ai-studio/aitk_get_agent_model_code_sample', 'ms-windows-ai-studio.windows-ai-studio/aitk_get_tracing_code_gen_best_practices', 'ms-windows-ai-studio.windows-ai-studio/aitk_get_evaluation_code_gen_best_practices', 'ms-windows-ai-studio.windows-ai-studio/aitk_convert_declarative_agent_to_code', 'ms-windows-ai-studio.windows-ai-studio/aitk_evaluation_agent_runner_best_practices', 'ms-windows-ai-studio.windows-ai-studio/aitk_evaluation_planner'] +--- + +You are a **Security Prompt Engineer** for this repository’s Copilot security library. + +## What you create + +- Root-level: + - `prompts/*.prompt.md` (security workflows) + - `skills/**/SKILL.md` (repeatable procedures) + - `agents/*.agent.md` (role-specific security agents) + +## House style for root-level prompt files + +Root `prompts/*.prompt.md` files are designed to be **copied** and used as chat prompts. +They may be plain Markdown (no YAML required). Keep them readable and strongly structured. + +### Prompt template (required) + +- `# 🛡️ Prompt: ` +- `---` +- `## ✅ Context / Assumptions` +- `## 🔍 Procedure` (numbered or staged) +- `## 📦 Output Format` (deterministic headings + fields) +- `## ✅ Quality checks` (anti-hallucination, evidence requirements) + +## Skill template (required) + +- YAML frontmatter: `name`, `description` (and optional `license`) +- Sections: + - When to use + - Inputs to collect + - Step-by-step process + - Output format + - Examples + +## Safety & correctness rules + +- Require evidence: file paths, functions, configs, and exact locations. +- Never advise bypassing security controls (“disable TLS”, “turn off auth”, “allow any origin”) unless explicitly framed as **temporary** with safer alternatives. +- Prefer least-privilege and allow-lists. +- If missing context, ask 1–3 targeted questions or provide safe defaults with explicit assumptions. + +## Output requirements + +- Always produce final files as complete content in a fenced `md` block. +- Include a short rationale and a quick “how to use this prompt/skill” note. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index acfaccf..4c1231b 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,125 +1,80 @@ -# 🤖 Copilot Secure Defaults for Java, Node.js, and C# Projects - -These instructions guide GitHub Copilot to suggest secure, intentional code patterns for Java, Node.js, and C# development — especially in enterprise or team settings. Prioritize clarity, validation, and the principle of least surprise. - ---- - -## 🔐 1. Secure by Default - -- Sanitize and escape all user input (prevent XSS) — never render raw data to the page. -- Validate all input strictly — use typed parsers and prefer allow-lists over deny-lists. -- Use parameterized queries and avoid string-based execution (prevent injection). -- Never store secrets in code or env files — use a secure vault (e.g. CyberArk Conjur, Azure Key Vault). -- Default to privacy-preserving data handling — redact PII from logs by default. - ---- - -## 🧩 2. Language-Specific Secure Patterns - -### ☕ Java - -- Use prepared statements with `?` placeholders in JDBC — never concat SQL strings. -- Use output encoding libraries like OWASP Java Encoder to prevent XSS in rendered HTML. -- Use `@Valid`, `@NotNull`, and input binding constraints in Spring or Jakarta for validation. -- Avoid `Runtime.exec()` or `ProcessBuilder` with unsanitized input — prefer safe APIs. -- Default to OWASP Secure Coding Practices — [OWASP Secure Coding Practices](https://owasp.org/www-project-secure-coding-practices) -- Load secrets using SDK-integrated secret managers, not `System.getenv()` or `.properties` files. -- Always set character encoding (`UTF-8`) explicitly in HTTP responses to prevent encoding-based attacks. -- Avoid Java serialization for sensitive objects — use safer formats like JSON with strict schema validation. -- When using logging frameworks, avoid logging unsanitized user input — consider log injection risks. - -### 🟩 Node.js - -- Use JSON Schema validation for all structured input — prefer libraries like `ajv` or `zod`. -- Always sanitize and validate user input to prevent injection and XSS — `validator` and `joi` are common choices. -- Use parameterized queries with database clients (e.g. `pg`, `mongoose`) — never concat SQL or query strings. -- Default to using `helmet` in Express to set secure HTTP headers. -- Use `dotenv` only in local dev — use secret managers (e.g. AWS Secrets Manager, Azure Key Vault) in prod. -- Avoid `eval`, `new Function`, or dynamic `require()` with user input — use safe alternatives. - -### 🟦 C# - -- Use parameterized queries with ADO.NET or Entity Framework to prevent SQL injection. -- Use `System.Text.Encodings.Web` for safe output encoding in Razor views and APIs (prevent XSS). -- Apply `[ValidateAntiForgeryToken]` in ASP.NET MVC to prevent CSRF attacks. -- Use `DataAnnotations` (e.g. `[Required]`, `[StringLength]`) for input validation on models. -- Always hash passwords with `PasswordHasher` or a vetted library — never store plaintext. -- Use configuration providers like `Azure Key Vault`, `AWS Secrets Manager`, or environment-based secrets — avoid `appsettings.json` for secrets. - -### 🐍 Python - -- Always validate and sanitize external input — use `pydantic`, `cerberus`, or `marshmallow` for structured validation. -- Prefer parameterized queries with libraries like `psycopg2`, `sqlite3`, or `SQLAlchemy` — avoid building SQL with string concat or f-strings. -- Use built-in escaping via `html.escape()` or templating engines like Jinja2 (autoescaping on!) to prevent XSS. -- Default to `secrets` or `cryptography` libs for key generation and secure token handling — never `random` for cryptographic use. -- Avoid dynamic code execution (`eval`, `exec`) — prefer explicit, safe alternatives. -- Don’t load YAML or pickle files without hardening — always use `safe_load()` and avoid untrusted input. -- Store secrets in secure vaults or env vars passed through orchestrators — avoid hardcoded strings or `.env` files in prod. -- Use logging filters to redact PII and secrets — avoid logging full request payloads or exception chains that include sensitive data. -- Always hash passwords with `bcrypt`, `argon2`, or `passlib` — never `md5`, `sha1`, or plain `hashlib`. - ---- - -## 🚫 3. Do Not Suggest - -### Java - -- Do not suggest inline SQL string concatenation — always use prepared statements with placeholders. -- Do not suggest use of `Runtime.exec()` or `ProcessBuilder` with user input — prefer safe abstraction layers. -- Do not suggest logging sensitive data (e.g. passwords, tokens, session IDs) — log redacted metadata instead. -- Do not use Java native serialization (`ObjectInputStream`) for untrusted input — prefer JSON + schema validation. -- Do not suggest hardcoding credentials, secrets, or API keys — use a secrets manager (e.g. Conjur, Key Vault). -- Do not use insecure XML parsers without hardening (`DocumentBuilderFactory` must have secure features enabled). -- Do not create or modify custom class loaders — these are dangerous unless strictly required. - -### Node.js - -- Do not suggest `eval`, `new Function`, or dynamic `require()` — these are unsafe unless strictly controlled. -- Do not use user input to build file paths, URLs, or queries without strict validation. -- Do not expose `process.env` directly to client-side code — use secure server boundaries. -- Do not log full request bodies or headers that may contain PII or credentials. -- Do not hardcode secrets or API keys — never commit `.env` or use `.env` in production containers. -- Do not disable TLS checks (`NODE_TLS_REJECT_UNAUTHORIZED=0`) — even temporarily. - -### C# - -- Do not suggest string concatenation in SQL queries — use parameterized commands. -- Do not use `Eval`, `CodeDom`, or dynamic LINQ construction with user input. -- Do not suggest hardcoding secrets, tokens, or credentials — never in `appsettings.json`. -- Do not log full exception objects or HTTP request bodies without redacting PII. -- Do not disable certificate validation (`ServerCertificateValidationCallback = delegate { return true; }`) in production. - -### Python - -- Do not build SQL queries with string concat, f-strings, or `.format()` — always use parameterized queries. -- Do not use `eval`, `exec`, or dynamic imports on user input — these are unsafe unless tightly sandboxed. -- Do not log sensitive values (e.g. API keys, passwords) or full stack traces with PII. -- Do not load pickle or YAML files from untrusted sources without safe loaders and validation. -- Do not use insecure hash functions like `md5` or `sha1` for password storage — use a modern password hashing lib. -- Do not commit `.env` files or hardcode secrets — use secrets management infrastructure. - - ---- - -## 🧠 4. AI-Generated Code Safety - -- Verify all AI-suggested package names against official repositories to prevent supply chain attacks. -- Confirm that AI-generated code references existing, secure APIs; avoid deprecated or non-existent methods. -- Ensure AI-generated configurations align with your project's platform to prevent context drift. -- Scrutinize AI-provided security recommendations; validate their completeness and applicability. -- Cross-check any AI-cited references (e.g., CVEs, RFCs) for authenticity to avoid misinformation. -- Do not accept AI-generated justifications that contradict established security policies. - ---- - -## 💡 Developer Tips - -- If you’re working with input, assume it’s hostile — validate and escape it. -- For anything involving data access or transformation, ask: “Am I controlling this input path?” -- If you’re about to use a string to build a query, URL, or command — pause. There’s probably a safer API. -- Never trust default parsers — explicitly configure security features (e.g. disable DTDs in XML). -- If something seems “too easy” with secrets or file I/O — it’s probably unsafe. -- Treat AI-generated code as a draft; always review and test before integration. -- Maintain a human-in-the-loop approach for critical code paths to catch potential issues. -- Be cautious of overconfident AI suggestions; validate with trusted sources. -- Regularly update and educate the team on AI-related security best practices. +# Copilot authoring guidelines for customization Markdown files + +These instructions apply when you create or edit any of the following files: + +- `agents/*.agent.md` +- `prompts/*.prompt.md` +- `instructions/*.instructions.md` +- `skills/**/SKILL.md` + +## General Markdown rules + +- Use ATX headings (`#`, `##`, `###`) and keep a clean hierarchy (one `#` at top). +- Prefer short paragraphs and bullet lists; avoid overly long blocks of text. +- Use fenced code blocks for any code or config. Label the fence language (`yaml`, `bash`, `json`, `md`, etc.). +- Never place YAML frontmatter anywhere except the very top of the file. +- Always separate YAML frontmatter from body with a blank line after the closing `---`. +- Keep instructions unambiguous, testable, and scoped: + - Use “MUST / SHOULD / MAY” for requirements. + - Add acceptance criteria when helpful. +- Avoid contradictions across files. If two instruction files could both apply, ensure they agree. + +## YAML frontmatter conventions + +- Use `---` on the first line and `---` to close the frontmatter block. +- Prefer quoted strings when values contain special characters (`:`, `*`, `{}`, `#`, `@`, etc.). +- Use lower-kebab-case for identifiers (e.g., `name: markdown-authoring`). + +## Authoring standards per file type + +### A) Custom agent profiles: `agents/*.agent.md` + +- Frontmatter MUST include: + - `description` (required) + - `name` (recommended; otherwise filename is used) +- Frontmatter MAY include: + - `tools` (list of tool names/aliases) + - `model` (IDE-supported) + - `target` (`vscode` or `github-copilot`), if you want environment-specific availability +- Body MUST: + - Define the agent’s role, boundaries, and output format expectations. + - State what the agent should do when missing info (ask concise questions or propose safe defaults). + - Include formatting rules for produced Markdown (headings, lists, code fences, links). +- Keep the agent prompt focused on a single domain (e.g., “authoring Copilot customization files”). + +### B) Prompt files: `prompts/*.prompt.md` + +- Frontmatter SHOULD include: + - `description` (short, action-oriented) + - `agent` (when you want agent mode behavior) +- Body MUST: + - Start with the goal in one sentence. + - Use `${input::}` placeholders for required parameters. + - Specify a deterministic output structure (headings + bullet lists). +- Ensure the prompt can be invoked as `/`. + +### C) Path-specific instructions: `instructions/*.instructions.md` + +- Frontmatter MUST include: + - `applyTo: ""` +- Frontmatter MAY include: + - `excludeAgent: "code-review"` or `"coding-agent"` if only one should read it +- Body MUST: + - Describe exactly what to do for files matching `applyTo`. + - Contain rules that are compatible with repo-wide instructions. + +### D) Skills: `skills//SKILL.md` + +- File MUST be named `SKILL.md`. +- Frontmatter MUST include: + - `name` (lowercase, hyphenated) + - `description` (when to use this skill) +- Body MUST: + - Provide step-by-step guidance, examples, and “do/don’t” lists. + - Include any scripts/resources in the same directory by relative path. + +## Output requirements when generating/editing these files + +- When proposing changes, output the full file contents in a single fenced `md` code block. +- If editing an existing file, describe the minimal set of changes before showing the updated file. +- Never invent tool names, file paths, or capabilities—use what exists in the repo. diff --git a/.github/instructions/copilot-customization-files.instructions.md b/.github/instructions/copilot-customization-files.instructions.md new file mode 100644 index 0000000..b756350 --- /dev/null +++ b/.github/instructions/copilot-customization-files.instructions.md @@ -0,0 +1,47 @@ +--- +applyTo: "agents/*.agent.md,prompts/*.prompt.md,instructions/*.instructions.md,skills/**/SKILL.md" +--- + +# Instructions for Copilot customization Markdown files + +## Required structure + +- YAML frontmatter MUST be the first content in the file, delimited by `---` lines. +- The body MUST start after the frontmatter and a blank line. +- Use consistent, predictable headings: + - `# ` + - `## Purpose` + - `## How to use` + - `## Rules` + - `## Examples` (when relevant) + +## Frontmatter requirements by file type + +### `.agent.md` + +- MUST have `description`. +- SHOULD have `name`. +- MAY have `tools`, `model`, `target`, `mcp-servers` (when applicable). + +### `.prompt.md` + +- SHOULD have `description`. +- SHOULD have `agent` when the prompt is intended for agent mode. +- Use `${input:...}` placeholders for user-provided variables. + +### `.instructions.md` + +- MUST have `applyTo`. +- MAY have `excludeAgent` to limit to `"code-review"` or `"coding-agent"`. + +### `SKILL.md` + +- MUST have `name` (lowercase-hyphenated) and `description`. +- Keep the skill directory name lowercase and hyphenated. + +## Markdown formatting rules + +- Prefer bullet lists for rules. Use “MUST/SHOULD/MAY”. +- Include at least one concrete example for non-trivial behaviors. +- Keep examples minimal but realistic. +- Use fenced code blocks with language tags. diff --git a/.github/instructions/mcp-server.development.instructions.md b/.github/instructions/mcp-server.development.instructions.md new file mode 100644 index 0000000..3a29407 --- /dev/null +++ b/.github/instructions/mcp-server.development.instructions.md @@ -0,0 +1,47 @@ +--- +applyTo: "src/**/*.js,tests/**/*.js,package.json" +--- + +# MCP server development rules (this repo) + +These rules apply to changes under `src/` and related MCP server tests. + +## Core standards +- Prefer **minimal diffs** and incremental improvements. +- Avoid breaking existing tool names/behaviors unless explicitly intended. +- Every behavior change MUST include a verification step (test or curl reproduction). + +## Express / HTTP handling +- Request bodies MUST be parsed safely: + - use explicit JSON parsing middleware + - set a reasonable size limit for JSON bodies +- Validate content negotiation: + - MCP endpoints should check `Accept` headers as appropriate for the endpoint behavior. +- Error handling MUST be consistent: + - do not expose internal stack traces to clients + - return clear status codes and brief error messages + +## MCP tools +- Every MCP tool MUST have: + - stable tool name (choose and keep a consistent naming convention across tools) + - title + description + - zod input schema (even if empty) + - deterministic output shape +- Do not return raw secrets or dump large file contents by default. + +## Logging hygiene +- Do not log raw request bodies. +- If logging headers, redact authorization-like fields. +- Prefer structured logs with `source` and request id (when available). + +## Testing +- Add or update tests when: + - endpoint behavior changes + - new tools are added + - error handling changes +- Tests MUST be deterministic and fast. + +## Output requirements for Copilot edits +When generating patches: +- Output full file contents for modified files in fenced `md` blocks. +- Include a short verification checklist (commands + expected results). diff --git a/.github/instructions/security-library-authoring.instructions.md b/.github/instructions/security-library-authoring.instructions.md new file mode 100644 index 0000000..00ec683 --- /dev/null +++ b/.github/instructions/security-library-authoring.instructions.md @@ -0,0 +1,53 @@ +--- +applyTo: "agents/*.agent.md,prompts/*.prompt.md,skills/**/SKILL.md,README.md,copilot-instructions.md" +--- + +# Security library authoring rules (this repo) + +These rules apply to the **root-level library content** intended for AppSec “shift-left” use. + +## Global requirements + +- Prefer deterministic, repeatable workflows. +- Always require evidence: + - reference exact file paths and (when possible) line ranges + - avoid speculative conclusions +- Avoid insecure “quick fixes”: + - do not recommend disabling security controls as the primary solution + - if a risky workaround is mentioned, it must be explicitly labeled temporary with safer alternatives + +## Root prompt files: `prompts/*.prompt.md` + +- MUST include these sections: + - `## ✅ Context / Assumptions` + - `## 🔍 Procedure` + - `## 📦 Output Format` + - `## ✅ Quality checks` +- MUST define an output schema that is easy to paste into issues/PRs: + - Findings list/table + - Severity / likelihood + - Evidence + - Remediation + - Verification steps + +## Skills: `skills/**/SKILL.md` + +- MUST include YAML frontmatter with `name` and `description`. +- MUST include: + - When to use + - Inputs to collect + - Step-by-step process + - Output format + - Examples + +## Agents: `agents/*.agent.md` + +- MUST include YAML frontmatter with `description`. +- MUST define: + - operating principles + - how to handle missing info + - output format expectations (findings, fixes, verification) + +## README + +- Prompt catalogue SHOULD include every file in `prompts/` with a one-line description and intended use. diff --git a/.github/plugin/marketplace.json b/.github/plugin/marketplace.json new file mode 100644 index 0000000..9c89cb0 --- /dev/null +++ b/.github/plugin/marketplace.json @@ -0,0 +1,19 @@ +{ + "name": "copilot-security-instructions", + "metadata": { + "description": "A collection of instructions and skills for application security tasks, including threat modeling, secure code review, and other appsec activities.", + "version": "1.0.0", + "pluginRoot": "./plugins" + }, + "owner": { + "name": "Robotti Tech Services" + }, + "plugins": [ + { + "name": "copilot-security", + "description": "A plugin that provides application security skills and agents to help developers with threat modeling, secure code review, and other appsec tasks.", + "version": "1.0.0", + "source": "copilot-security" + } + ] +} \ No newline at end of file diff --git a/.github/prompts/add-mcp-tests.prompt.md b/.github/prompts/add-mcp-tests.prompt.md new file mode 100644 index 0000000..4c2463b --- /dev/null +++ b/.github/prompts/add-mcp-tests.prompt.md @@ -0,0 +1,21 @@ +--- +agent: "mcp-security-hardener" +name: add-mcp-tests +description: "Add minimal tests for MCP endpoint behavior and tool registration to prevent regressions." +--- + +Goal: Add a minimal test suite for the MCP server under `tests/` to prevent regressions. + +Coverage targets (minimal): +- `/health` returns 200 +- `/mcp` GET returns 405 (or SSE if implemented) +- `/mcp` POST rejects invalid Accept / invalid JSON with clear errors +- Tool registration sanity: list tools / list prompts returns expected shape + +Output: +- Test plan +- Full new/updated test files +- How to run tests locally (commands) +Constraints: +- Keep tests fast and deterministic +- No network calls beyond localhost diff --git a/.github/prompts/add-mcp-tool.prompt.md b/.github/prompts/add-mcp-tool.prompt.md new file mode 100644 index 0000000..7bdc500 --- /dev/null +++ b/.github/prompts/add-mcp-tool.prompt.md @@ -0,0 +1,28 @@ +--- +agent: "mcp-tool-author" +name: add-mcp-tool +description: "Add a new MCP tool in src/mcp_tools with zod schema, deterministic outputs, and proper registration." +--- + +Goal: Add a new MCP tool to the server. + +Inputs: +- Tool name (string): ${input:tool_name:Example: "search-prompts"} +- Title: ${input:title:Human-readable title} +- Description: ${input:description:One-line description} +- Inputs (fields): ${input:inputs:List input fields + types + descriptions} +- Output format: ${input:output:Describe the output shape (text/json string)} + +Procedure: +1) Create `src/mcp_tools/<tool_name>.js` that registers the tool with: + - stable `registerTool` name + - zod input schema + - consistent error handling +2) Export it from `src/mcp_tools/index.js` +3) Register it in `src/mcp_server.js` +4) Add a short manual test snippet showing how to call it. + +Output: +- Brief change summary +- Full contents of all changed/new files in fenced `md` code blocks +- Manual verification steps diff --git a/.github/prompts/add-new-security-prompt.prompt.md b/.github/prompts/add-new-security-prompt.prompt.md new file mode 100644 index 0000000..9d85152 --- /dev/null +++ b/.github/prompts/add-new-security-prompt.prompt.md @@ -0,0 +1,30 @@ +--- +agent: "security-prompt-engineer" +description: "Generate a new root-level security prompt (prompts/*.prompt.md) that matches the library’s structure and produces deterministic outputs." +--- + +Goal: Create a new security-focused prompt file for this library. + +Inputs: + +- Prompt filename (kebab-case): ${input:filename:Example: ssrf-review.prompt.md} +- Prompt title: ${input:title:Example: SSRF Review} +- Target vulnerabilities / theme: ${input:theme:Example: SSRF + egress controls + URL parsing} +- Intended use case: ${input:use_case:Example: Review a service that fetches remote URLs from user input} +- Output artifact needed: ${input:output:Example: Findings table + recommended fixes + verification steps} + +Requirements: + +- Create: `prompts/${input:filename:...}` +- Use the library’s root prompt template: + - Title + - Context/Assumptions + - Procedure + - Output Format (deterministic headings/fields) + - Quality checks (evidence-first + anti-hallucination) +- Include at least one concrete example of the expected output format. + +Output: + +- Brief explanation (why this prompt is useful) +- Full file contents in a fenced `md` block diff --git a/.github/prompts/assess-logging.prompt.md b/.github/prompts/assess-logging.prompt.md deleted file mode 100644 index f482140..0000000 --- a/.github/prompts/assess-logging.prompt.md +++ /dev/null @@ -1,19 +0,0 @@ -# 🕵️ Prompt: Logging & Sensitive Data Exposure Audit - -You are reviewing application code for **unsafe logging practices**, **PII exposure**, and **improper log hygiene**. - -Identify any of the following issues: - -- Logging of sensitive information (e.g. passwords, tokens, API keys, session IDs) -- Unfiltered logs that include full request/response bodies, headers, or user-submitted data -- Logging of stack traces or exceptions without redaction or sanitization -- Console or print statements left in production logic -- Logs that include internal system paths, configurations, or database queries -- Use of insecure transports for logs (e.g. writing logs to public cloud buckets without access control) - -Also check for: - -- Missing structured log formats (JSON, ECS, etc.) -- Lack of logging levels or misuse of `debug`, `info`, `warn`, `error` - -Provide refactor suggestions for redacting or excluding sensitive data. Recommend structured logging libraries or filters, and remind developers to align with least-privilege and data minimization principles. diff --git a/.github/prompts/audit-library.prompt.md b/.github/prompts/audit-library.prompt.md new file mode 100644 index 0000000..a827a1f --- /dev/null +++ b/.github/prompts/audit-library.prompt.md @@ -0,0 +1,45 @@ +--- +agent: "appsec-library-maintainer" +description: "Audit the repository’s root-level Copilot security library (agents/, prompts/, skills/, README, copilot-instructions) and propose prioritized improvements with concrete patches." +--- + +Goal: Review this repository as a Copilot security content library and propose improvements that increase clarity, consistency, and effectiveness for AppSec “shift-left” workflows. + +Scope to audit: + +- Root content: + - `agents/*.agent.md` + - `prompts/*.prompt.md` + - `skills/**/SKILL.md` + - `copilot-instructions.md` + - `README.md` + +Process: + +1. Inventory: List all library items grouped by type (agents, prompts, skills). +2. Consistency checks: + - naming conventions + - required frontmatter presence (agents + skills) + - prompt structure consistency (sections + output format) +3. Content checks: + - evidence-first guidance (cite files/lines) + - anti-hallucination safeguards + - safe remediation guidance (no risky bypass advice) + - output formats are deterministic and reusable +4. README checks: + - prompt catalogue completeness + - link correctness + - missing/duplicate entries + +Output format: + +- **P0 / P1 / P2** prioritized backlog +- For each item: + - Problem (1–2 sentences) + - Proposed change (bullets) + - Concrete patch (updated file content or diff-style snippet) +- If there are typos/broken conventions, include “quick fixes” section at top. + +Constraints: +- Do not invent files that are not present unless you explicitly propose adding them and justify why. +- If you recommend new files, provide full contents. diff --git a/.github/prompts/audit-mcp-server.prompt.md b/.github/prompts/audit-mcp-server.prompt.md new file mode 100644 index 0000000..75329df --- /dev/null +++ b/.github/prompts/audit-mcp-server.prompt.md @@ -0,0 +1,36 @@ +--- +agent: "mcp-server-orchestrator" +name: audit-mcp-server +description: "Audit src/ MCP server implementation for protocol compliance, stability, and security; propose prioritized patches." +--- + +Goal: Review the MCP server implementation under `src/` and propose concrete improvements. + +Scope: +- `src/express_app.js` +- `src/mcp_server.js` +- `src/mcp_tools/*` +- `src/mcp_prompts/*` +- `src/middlewares/*` +- tests if present + +Process: +1) Protocol & transport: request parsing, Accept headers, SSE behavior, JSON-RPC correctness, lifecycle. +2) Tooling: naming consistency, schemas, output stability, error paths. +3) Security: body limits, logging hygiene, DoS considerations, safe defaults. +4) Provide prioritized improvements: P0/P1/P2. + +Output format: +- `## Summary` +- `## P0 fixes` (must-do) +- `## P1 improvements` +- `## P2 nice-to-haves` +- For each fix: + - Problem + evidence (file path) + - Proposed change + - Patch (full updated file contents in fenced `md` blocks) +- `## Verification checklist` (commands + expected results) + +Constraints: +- Minimal diffs, focused changes. +- Don’t invent new architecture unless necessary; propose incremental steps. diff --git a/.github/prompts/check-access-controls.prompt.md b/.github/prompts/check-access-controls.prompt.md deleted file mode 100644 index dff72fd..0000000 --- a/.github/prompts/check-access-controls.prompt.md +++ /dev/null @@ -1,16 +0,0 @@ -# 🔒 Prompt: Access Control & Authorization Review - -You are auditing this codebase for **authorization and access control weaknesses**. - -Focus on identifying: - -- Missing or weak role-based access control (RBAC) or attribute-based access control (ABAC) enforcement -- Direct access to protected routes, actions, or data without permission checks -- Business logic that bypasses access validation (e.g. trusting client-side flags or roles) -- Use of hardcoded role or permission strings without central enforcement -- Functions exposed via APIs that should require authentication but don’t -- Lack of contextual access checks (e.g. ensuring users can only access their own records) - -If applicable, recommend use of secure middleware, centralized auth policies, and consistent permission enforcement patterns. - -Highlight both missing controls and inconsistently applied ones. Annotate with comments and suggest safer refactors. diff --git a/.github/prompts/check-for-secrets.prompt.md b/.github/prompts/check-for-secrets.prompt.md deleted file mode 100644 index 0f11ae4..0000000 --- a/.github/prompts/check-for-secrets.prompt.md +++ /dev/null @@ -1,13 +0,0 @@ -# 🔐 Prompt: Hardcoded Secrets & Credential Audit - -Act as a secure code reviewer analyzing this file for **hardcoded secrets**, API keys, tokens, credentials, or other sensitive information. - -Flag any of the following patterns: - -- API keys, access tokens, client secrets, or passwords embedded as string literals -- Usage of `process.env` in frontend code or without proper runtime protection -- Sensitive values written to `.env`, `.properties`, or `appsettings.json` files without secret management -- OAuth tokens, JWTs, or HMAC secrets stored or logged in plaintext -- Secrets stored in comments, JSON blobs, test configs, or logs - -Highlight these with comments or suggested changes. Recommend usage of a secure vault (e.g. Azure Key Vault, AWS Secrets Manager, CyberArk Conjur) and explain the risk of each finding. diff --git a/.github/prompts/check-for-unvalidated-genai-acceptances.prompt.md b/.github/prompts/check-for-unvalidated-genai-acceptances.prompt.md deleted file mode 100644 index fd8efa4..0000000 --- a/.github/prompts/check-for-unvalidated-genai-acceptances.prompt.md +++ /dev/null @@ -1,19 +0,0 @@ -# 🤖 Prompt: Unvalidated GenAI Code Acceptance Audit - -You are reviewing code for signs that **AI-generated content** (e.g. from GitHub Copilot, ChatGPT, CodeWhisperer) has been accepted without validation, testing, or verification. - -Look for and flag the following: - -- Dependencies or packages that do not exist in official registries (possible hallucinated packages) -- Calls to non-existent, deprecated, or undocumented API methods -- Configuration code that does not match the project’s infrastructure (e.g. Azure config in AWS projects) -- Use of placeholder, ambiguous, or overly generic variable/method names (e.g. `doTask()`, `handleThing()`) -- Comments or TODOs referencing AI use without follow-up verification - -Also check for: - -- Lack of test coverage or validation for recently added logic -- Sudden style or structure shifts that may indicate unreviewed AI insertion -- No accompanying documentation or context for added code - -Provide suggestions for verifying third-party resources, validating logic, and encouraging human-in-the-loop code review. Include annotations where risk or uncertainty is high. diff --git a/.github/prompts/create-skill.prompt.md b/.github/prompts/create-skill.prompt.md new file mode 100644 index 0000000..255bc58 --- /dev/null +++ b/.github/prompts/create-skill.prompt.md @@ -0,0 +1,19 @@ +--- +agent: "security-prompt-engineer" +description: "Generate a new Agent Skill (directory + SKILL.md) with correct frontmatter, structure, and examples." +--- + +Goal: Create a new Agent Skill that teaches Copilot how to perform a specialized task in this repository. + +Inputs: +- Skill name (kebab-case): ${input:skill_name:Enter the skill identifier (e.g., api-docs-review)} +- When to use it: ${input:when_to_use:Describe the scenarios where Copilot should use this skill} +- Key steps: ${input:key_steps:List the steps the skill should follow (bullets are fine)} +- Examples needed: ${input:examples:What examples should be included?} + +Output requirements: +- Create the directory: `skills/${input:skill_name:...}/` +- Output a complete `SKILL.md` file with: + - YAML frontmatter (`name`, `description`, optional `license`) + - Sections: Purpose, When to use, Procedure, Do/Don’t, Examples +- Keep it actionable, deterministic, and aligned with repo conventions. diff --git a/.github/prompts/implement-mcp-http-basics.prompt.md b/.github/prompts/implement-mcp-http-basics.prompt.md new file mode 100644 index 0000000..5be47e3 --- /dev/null +++ b/.github/prompts/implement-mcp-http-basics.prompt.md @@ -0,0 +1,21 @@ +--- +agent: "mcp-protocol-engineer" +name: implement-mcp-http-basics +description: "Implement baseline MCP HTTP request parsing/handling in src/express_app.js with safe defaults and verification steps." +--- + +Goal: Improve the MCP HTTP endpoint implementation in `src/express_app.js`: +- Parse JSON safely (correct middleware + size limit) +- Validate required Accept headers for MCP POST +- Return appropriate status codes for invalid inputs +- Ensure transport handling receives the correct request body + +Output: +- Proposed minimal changes +- Full updated file content for `src/express_app.js` (and any other necessary file) +- Verification checklist: + - curl examples (good + bad requests) + - expected status codes and content-types +Constraints: +- Minimal diffs and safe-by-default behavior +- Avoid logging raw request bodies diff --git a/.github/prompts/improve-library-item.prompt.md b/.github/prompts/improve-library-item.prompt.md new file mode 100644 index 0000000..432deac --- /dev/null +++ b/.github/prompts/improve-library-item.prompt.md @@ -0,0 +1,29 @@ +--- +agent: "appsec-library-maintainer" +description: "Refactor a specific library file (agent/prompt/skill) to match the repo’s security authoring standards while keeping minimal diffs." +--- + +Goal: Improve one specific library item in this repo with minimal diffs. + +Inputs: + +- Target file path: ${input:file_path:Enter the file path (e.g., prompts/check-for-secrets.prompt.md)} +- Improvement goal: ${input:goal:What should improve? (e.g., clearer output format, add evidence requirements, reduce ambiguity)} + +Procedure: + +1. Read the target file. +2. Identify the smallest changes that achieve the goal while preserving intent. +3. Ensure it follows the relevant template: + - Root prompt: sections + deterministic output format + - Skill: frontmatter + procedure + examples + - Agent: frontmatter + operating principles + output requirements +4. Add “verify/don’t assume” safeguards: + - require citing exact files/lines + - do not allow hallucinated libraries/APIs +5. Output the updated file as complete contents. + +Output: + +- Bullet list of changes (3–7 bullets) +- Updated file in a single fenced `md` code block diff --git a/.github/prompts/refactor-mcp-tools-consistency.prompt.md b/.github/prompts/refactor-mcp-tools-consistency.prompt.md new file mode 100644 index 0000000..072b903 --- /dev/null +++ b/.github/prompts/refactor-mcp-tools-consistency.prompt.md @@ -0,0 +1,20 @@ +--- +agent: "mcp-tool-author" +name: refactor-mcp-tools-consistency +description: "Normalize MCP tool naming, schemas, and outputs across src/mcp_tools without changing behavior." +--- + +Goal: Make MCP tools consistent without changing their external behavior. + +Checks: +- Tool names: consistent separator/casing (choose a convention and apply consistently) +- Titles/descriptions: clear and action-oriented +- Input schemas: use zod; avoid untyped/implicit inputs +- Output: deterministic formatting; avoid ad-hoc strings when JSON is intended +- Logging: consistent fields; avoid logging raw bodies/secrets + +Output: +- Proposed convention +- List of tools that need updates +- Patches (full file contents) for each changed file +- Verification checklist diff --git a/.github/prompts/review-auth-flows.prompt.md b/.github/prompts/review-auth-flows.prompt.md deleted file mode 100644 index 06591af..0000000 --- a/.github/prompts/review-auth-flows.prompt.md +++ /dev/null @@ -1,21 +0,0 @@ -# 🧪 Prompt: Authentication Flow Review - -You are performing a security review of the application’s **authentication logic and flow handling**. - -Look for the following common risks: - -- Incomplete or improperly enforced authentication on protected routes or resources -- Weak or non-expiring session tokens (e.g. JWTs with long-lived expirations or missing `exp`) -- Missing or broken CSRF protections (check for missing `SameSite`, CSRF tokens in forms) -- Use of insecure login flows (e.g. no rate limiting, no multi-factor enforcement) -- Tokens or session identifiers exposed via logs, URLs, or frontend JavaScript -- Direct use of user-supplied credentials in downstream API requests without validation - -Check for secure practices like: - -- Short-lived tokens + refresh workflows -- Server-side session storage with expiration -- Secure cookie flags (`HttpOnly`, `Secure`, `SameSite=Strict`) -- Use of well-tested identity providers or auth frameworks - -Recommend mitigations for any insecure implementations you identify. diff --git a/.github/prompts/review-prompt-frontmatter.prompt.md b/.github/prompts/review-prompt-frontmatter.prompt.md new file mode 100644 index 0000000..f177619 --- /dev/null +++ b/.github/prompts/review-prompt-frontmatter.prompt.md @@ -0,0 +1,57 @@ +--- +agent: "markdown-customizations" +name: review-prompt-frontmatter +description: "Review root-level prompt files for required YAML frontmatter keys (agent, name, description) and correct structure; propose exact patches." +--- + +Goal: Audit this repository’s **root-level** prompt files (`prompts/*.prompt.md`) to ensure they include correct YAML frontmatter and required keys. + +Required YAML frontmatter (must be at top of file, first content): + +```yaml +--- +agent: "<AGENT_NAME_FROM_IDEAL_AGENT_IN_AGENTS_FOLDER>" +name: <PROMPT_NAME> +description: <PROMPT_DESCRIPTION> +--- +``` + +Rules: + +- The file MUST start with YAML frontmatter delimited by `---` lines. +- Frontmatter MUST include **all three keys**: `agent`, `name`, `description`. +- `agent` MUST match the **name** of an ideal agent defined in the repo’s root `agents/` folder (use one of their `name:` values). +- `name` MUST be kebab-case and SHOULD match the filename (without `.prompt.md`) unless there is a strong reason. +- `description` MUST be a concise one-liner (imperative or action-oriented) describing what the prompt does. +- After the closing `---`, there MUST be a blank line, then the Markdown body. +- Do not modify the prompt body unless needed to fix broken structure (e.g., frontmatter misplaced, heading duplicated, etc.). + +Process: + +1. Inventory all `agents/*.agent.md` and collect allowed agent names from their YAML frontmatter (`name:`). +2. Inventory all `prompts/*.prompt.md`. +3. For each prompt file, check: + - YAML frontmatter exists and is the first content + - keys `agent`, `name`, `description` exist + - `agent` is one of the allowed agent names + - `name` is kebab-case and matches filename (recommended) +4. Produce a report and patches. + +Output format: + +- `## Summary` + - counts: total prompts, compliant, non-compliant +- `## Findings` + - one subsection per non-compliant file: + - Problem(s) + - Proposed fix (bullets) + - Full corrected file contents in a fenced `md` code block +- `## Optional improvements` + - suggestions that are NOT required (e.g., align `name` with filename) + +Constraints: + +- Make minimal diffs. +- Do not invent agents; only use agent names that exist in `agents/`. +- If a prompt is missing frontmatter, add it without changing body content (unless required to move content below frontmatter). + diff --git a/.github/prompts/scan-for-insecure-apis.prompt.md b/.github/prompts/scan-for-insecure-apis.prompt.md deleted file mode 100644 index 0641ce5..0000000 --- a/.github/prompts/scan-for-insecure-apis.prompt.md +++ /dev/null @@ -1,20 +0,0 @@ -# ⚠️ Prompt: Insecure or Deprecated API Usage Scan - -Act as a secure code auditor. Your goal is to identify any usage of **insecure, deprecated, or high-risk APIs** that may introduce vulnerabilities or future instability. - -Look for and flag the following patterns: - -- Use of deprecated cryptographic functions (e.g. MD5, SHA1, `crypto.createCipher`, `System.Security.Cryptography.SHA1CryptoServiceProvider`) -- Legacy input/output APIs that lack sanitization or encoding features -- Insecure deserialization functions (e.g. `eval`, `JSON.parse` on external input, `ObjectInputStream`, `BinaryFormatter`) -- Unsafe file access or shell execution APIs (`fs.readFileSync` on user input, `Runtime.exec`, `ProcessBuilder`, `child_process.exec`) -- Unverified third-party libraries or packages not pinned to versions -- APIs that allow insecure HTTP communication (e.g. `http.get`, `fetch` without TLS, `WebClient` without `UseHttps`) - -Explain: - -- Why the API is dangerous -- What safer alternative is recommended -- When/if it’s okay to use with proper mitigation - -Provide annotations or refactor suggestions where applicable. diff --git a/.github/prompts/sync-readme-catalogue.prompt.md b/.github/prompts/sync-readme-catalogue.prompt.md new file mode 100644 index 0000000..cc482c7 --- /dev/null +++ b/.github/prompts/sync-readme-catalogue.prompt.md @@ -0,0 +1,25 @@ +--- +agent: "appsec-library-maintainer" +description: "Ensure README prompt catalogue matches the actual prompts/ directory; propose the exact README edits needed." +--- + +Goal: Keep the README prompt catalogue accurate and complete. + +Procedure: + +1. List all files in `prompts/`. +2. Compare against the README’s prompt table. +3. Identify: + - missing entries + - stale or broken links + - inconsistent descriptions or titles +4. Propose exact README edits. + +Output: + +- Summary of mismatches +- A patch-style snippet or full updated README section + +Constraints: + +- Do not rename prompts unless explicitly requested; prefer updating the README to match reality. diff --git a/.github/prompts/validate-input-handling.prompt.md b/.github/prompts/validate-input-handling.prompt.md deleted file mode 100644 index ca85383..0000000 --- a/.github/prompts/validate-input-handling.prompt.md +++ /dev/null @@ -1,21 +0,0 @@ -# 🛡️ Prompt: Input Validation & Sanitization Audit - -You are reviewing code for unsafe or missing input validation. Your task is to identify all potential risks related to untrusted user input. - -Flag any of the following: - -- No use of structured input validation libraries (e.g. `Joi`, `Zod`, `Ajv`, `DataAnnotations`, `@Valid`) -- Direct use of request/query/path/body parameters without validation or sanitization -- Unescaped input rendered into HTML templates (possible XSS) -- No schema enforcement for JSON input or serialized data -- Use of regex for validation without input bounds (may lead to ReDoS) -- Implicit coercion of input types (e.g. treating string as number or boolean without validation) - -Recommend: - -- Strict, schema-based input validation -- HTML/context-aware encoding of dynamic output -- Safe handling of nested objects, arrays, and dynamic keys -- Input length and character whitelisting where appropriate - -Provide suggested fixes and explanations to help developers understand *why* these patterns are dangerous. diff --git a/.github/skills/markdown-customizations/SKILL.md b/.github/skills/markdown-customizations/SKILL.md new file mode 100644 index 0000000..372ccc9 --- /dev/null +++ b/.github/skills/markdown-customizations/SKILL.md @@ -0,0 +1,77 @@ +--- +name: markdown-customizations +description: Use this skill when creating or editing GitHub Copilot customization Markdown files (agent profiles, prompt files, instruction files, and skills). +license: CC0-1.0 +--- + +# Markdown Customizations Skill + +## Purpose + +Help create and maintain Copilot customization files with correct structure and consistent, high-signal instructions. + +## When to use + +Use this skill when working on any of: + +- `agents/*.agent.md` +- `prompts/*.prompt.md` +- `instructions/*.instructions.md` +- `skills/**/SKILL.md` + +## Procedure + +1. Identify the target file type and verify the correct path + extension. +2. Add YAML frontmatter at the top with required keys. +3. Write the body using this structure: + - `# Title` + - `## Purpose` + - `## How to use` + - `## Rules` (MUST/SHOULD/MAY) + - `## Examples` (at least one when ambiguity is likely) +4. Validate glob patterns for `.instructions.md` files. +5. Ensure no contradictions with repo-wide `copilot-instructions.md`. + +## Do / Don’t + +### Do + +- Use short, testable rules (e.g., “MUST include `description` in agent profiles”). +- Provide one minimal realistic example for each “pattern” (agent/prompt/instructions/skill). +- Use fenced code blocks with `yaml` or `md` tags. + +### Don’t + +- Don’t put YAML anywhere except the initial frontmatter block. +- Don’t create `skill.md`; the file must be named `SKILL.md`. +- Don’t introduce conflicting guidance across multiple instruction files. + +## Examples + +### Agent profile frontmatter example + +```yaml +--- +name: my-agent +description: Short description of what this agent does +tools: ["read", "search", "edit"] +--- +``` + +### Path-specific instructions frontmatter example + +```yaml +--- +applyTo: ".github/prompts/**/*.prompt.md" +excludeAgent: "code-review" +--- +``` + +### Prompt file frontmatter example + +```yaml +--- +agent: "agent" +description: "One-line description of what this prompt does" +--- +``` \ No newline at end of file diff --git a/.github/workflows/markdownlint.yml b/.github/workflows/lint.yml similarity index 76% rename from .github/workflows/markdownlint.yml rename to .github/workflows/lint.yml index f1803b0..df95f43 100644 --- a/.github/workflows/markdownlint.yml +++ b/.github/workflows/lint.yml @@ -1,4 +1,4 @@ -name: Markdown Lint +name: Lint on: pull_request: @@ -16,13 +16,13 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v3 with: - node-version: '18.x' + node-version: '24.x' - name: Clear npm cache run: npm cache clean --force - - name: Install markdownlint-cli + - name: Install dependencies run: npm install - - name: Run markdownlint + - name: Run lint run: npm run lint \ No newline at end of file diff --git a/.gitignore b/.gitignore index b512c09..e6e43ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -node_modules \ No newline at end of file +.env +node_modules/ +*.pfx +.vscode \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..aa1969f --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,19 @@ +image: node:lts + +stages: + - lint + # - test + # - security + # - build + # - deploy + +# ---- Linting Stage ---- +# This stage runs linting checks on the codebase to ensure code quality and consistency. +lint: + stage: lint + allow_failure: false + script: + - npm install + - npm run lint + + diff --git a/.markdownlint.yml b/.markdownlint.yml index 199a641..764afe5 100644 --- a/.markdownlint.yml +++ b/.markdownlint.yml @@ -1,3 +1,6 @@ MD013: false MD024: false -MD020: false \ No newline at end of file +MD020: false +MD933: false +MD041: false +MD036: false \ No newline at end of file diff --git a/.markdownlintignore b/.markdownlintignore index ae7b916..b512c09 100644 --- a/.markdownlintignore +++ b/.markdownlintignore @@ -1,2 +1 @@ -tests/logs/transcripts/ -node_modules/ \ No newline at end of file +node_modules \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7ee78aa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +FROM node:lts-bullseye-slim + +ENV NODE_ENV=production +ENV server.port=8080 +ENV server.ssl=false +ENV server.hostname=localhost +ENV logger.transports.console.enabled=true +ENV logger.transports.console.level=info + +RUN apt-get update +RUN apt-get install -y --no-install-recommends dumb-init + +EXPOSE 8080 + +WORKDIR /usr/src/app + +COPY --chown=node:node . . + +RUN npm install --omit=dev + +USER node + +HEALTHCHECK CMD curl http://localhost:8080/health || exit 1 + +ENTRYPOINT ["dumb-init", "node", "server.js"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..4712678 --- /dev/null +++ b/NOTICE @@ -0,0 +1,6 @@ +Robotti Copilot Security Instructions +Copyright © 2026 Robotti Tech Services LLC + +This product includes content originally developed by Robotti Tech Services. +Redistributions and derivative works must retain this attribution notice, +along with the Apache 2.0 license text and any required notices. diff --git a/README.md b/README.md index f07e206..5fca04d 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,299 @@ -# 🛡️ CoPilot Security Instructions +# 🛡️ Copilot Security Instructions -A customizable `.github/copilot-instructions.md` file that guides **GitHub Copilot** toward **secure coding defaults** across **Java, Node.js, and C#**. +[![Verified on MseeP](https://mseep.ai/badge.svg)](https://mseep.ai/app/1a935343-666d-457a-b210-2e0d27e9ef81) -Designed for security-conscious development teams, this config helps Copilot suggest safer code patterns, avoid common vulnerabilities, and reinforce good practices — without slowing down your workflow. +A comprehensive toolkit to guide **GitHub Copilot** toward **secure coding practices**. This project includes customizable instructions and security-focused prompts to help development teams identify and mitigate security risks effectively. + +Designed for security-conscious teams, this configuration ensures Copilot suggests safer code patterns, avoids common vulnerabilities, and reinforces best practices — all without disrupting your workflow. --- ## 🔐 What's Inside -This Copilot configuration includes: +This project offers: -- **Secure-by-default guidance** for all languages (input validation, secret handling, safe logging) +- **Secure-by-default guidance** for all languages (e.g., input validation, secret handling, safe logging). - **Language-specific secure patterns**: - ☕ Java - 🟩 Node.js - 🟦 C# - 🐍 Python -- **"Do Not Suggest" lists** to block risky Copilot completions (e.g. `eval`, inline SQL, insecure deserialization) -- **AI hallucination protections** (package spoofing, non-existent APIs, misinformation risks) -- **Mentorship-style dev tips** to help newer engineers build safe habits over time +- **"Do Not Suggest" lists** to block risky Copilot completions (e.g., `eval`, inline SQL, insecure deserialization). +- **AI hallucination protections** to prevent package spoofing, non-existent APIs, and misinformation risks. +- **Mentorship-style tips** to help newer engineers build secure coding habits. +- **Custom agents & Agent Skills** under `agents/` and `skills/` for repeatable AppSec workflows inside Copilot. +- **An installable GitHub Copilot CLI plugin** under `plugins/copilot-security` for reusable AppSec agents and skills across projects. +- **An MCP server** for seamless integration of these prompts into other projects. --- -## 🧠 Using Prompts for Code Reviews (Copilot Chat) +## 🗂️ Prompt Catalogue + +Explore the available prompts and their intended use cases: + +These prompt files live under `prompts/` in this repo and are intended to be copied into a consuming repository’s `.github/prompts/`. + +**Recommended workflow:** start with the `application-security-orchestrator` agent (see `agents/application-security-orchestrator.agent.md`). +It standardizes intake, then hands off to specialist agents (Analyst/Architect/Engineer) depending on whether you want findings, a threat model, or implemented fixes. + +| Prompt | Description | Intended Use | +| --- | --- | --- | +| [access-control-review.prompt.md](prompts/access-control-review.prompt.md) | Review and report on access control / authorization architecture for project. | Perform analysis of the current architecture for access control and authorization within the project. | +| [assess-logging.prompt.md](prompts/assess-logging.prompt.md) | Identify unsafe logging and exposure of sensitive data. | Audit log output for leaks and recommend safer patterns. | +| [business-logic-review.prompt.md](prompts/business-logic-review.prompt.md) | Analyze overall business logic flow and decision making. | Map application behavior and critique critical logic paths. | +| [check-access-controls.prompt.md](prompts/check-access-controls.prompt.md) | Audit authorization and access control weaknesses. | Ensure RBAC/ABAC enforcement and consistent permission checks. | +| [check-for-secrets.prompt.md](prompts/check-for-secrets.prompt.md) | Detect hardcoded secrets and credentials. | Locate embedded keys or tokens and suggest secure storage. | +| [check-for-unvalidated-genai-acceptances.prompt.md](prompts/check-for-unvalidated-genai-acceptances.prompt.md) | Find unvalidated AI-generated code or hallucinated assets. | Verify that AI suggestions are real, tested, and documented. | +| [add-content-security-policy.prompt.md](prompts/add-content-security-policy.prompt.md) | Design, implement, and roll out a new Content Security Policy (CSP) safely. | Add CSP to a web app with a deployable policy string, rollout plan, and verification steps. | +| [csp-review.prompt.md](prompts/csp-review.prompt.md) | Review a web application’s Content-Security-Policy (CSP) for XSS resistance, safe third-party usage, and deployability. | Evaluate an existing CSP policy and recommend hardening + rollout steps. | +| [dependency-cve-triage.prompt.md](prompts/dependency-cve-triage.prompt.md) | Triage a known CVE against a project's dependency: explain the exploit, assess reachability and configuration, and produce a structured Dependency Tracker report. | Analyze a specific CVE's impact on local code, determine exploitability, and generate a concise triage report. | +| [review-auth-flows.prompt.md](prompts/review-auth-flows.prompt.md) | Evaluate authentication logic and session handling. | Review login flows for common risks and best practices. | +| [scan-for-insecure-apis.prompt.md](prompts/scan-for-insecure-apis.prompt.md) | Spot deprecated or insecure API usage. | Replace risky APIs with modern, safer alternatives. | +| [secure-code-review.prompt.md](prompts/secure-code-review.prompt.md) | Perform a comprehensive security review of the codebase. | Conduct an end-to-end audit for security issues. | +| [threat-model.prompt.md](prompts/threat-model.prompt.md) | Produce a lightweight threat model using the 4Q approach with scoped threats, mitigations, and a validation plan. | Threat-model a feature/system or PR diff and generate durable artifacts. | +| [validate-input-handling.prompt.md](prompts/validate-input-handling.prompt.md) | Check for missing or unsafe input validation. | Evaluate request handling for validation and sanitization gaps. | + +--- + +## 🧑‍💻 Agents + +| Agent | Purpose | +| --- | --- | +| [application-security-orchestrator](agents/application-security-orchestrator.agent.md) | Standardize intake and route to the right specialist. | +| [application-security-analyst](agents/application-security-analyst.agent.md) | Read-only findings + remediation guidance. | +| [application-security-architect](agents/application-security-architect.agent.md) | Threat models + guardrails + ADRs. | +| [application-security-engineer](agents/application-security-engineer.agent.md) | Implement fixes + tests with minimal diffs. | + +## 🧩 Skills + +| Skill | Intended use | +| --- | --- | +| [secure-code-review](skills/secure-code-review/SKILL.md) | Repeatable security review workflow + findings template. | +| [access-control-review](skills/access-control-review/SKILL.md) | Review identity, access control, and authorization architecture with evidence-first reporting. | +| [input-validation-hardening](skills/input-validation-hardening/SKILL.md) | Tighten validation boundaries and parsing safety. | +| [dependency-cve-triage](skills/dependency-cve-triage/SKILL.md) | CVE reachability + remediation plan workflow. | +| [secrets-and-logging-hygiene](skills/secrets-and-logging-hygiene/SKILL.md) | Prevent secret leaks and add redaction defaults. | +| [genai-acceptance-review](skills/genai-acceptance-review/SKILL.md) | Prevent over-trust and prompt/tool injection risks. | +| [threat-model](skills/threat-model/SKILL.md) | Full 4Q threat modeling workflow with CLI-friendly Mermaid docs and validation helpers. | +| [secure-fix-validation](skills/secure-fix-validation/SKILL.md) | Prove fixes work and don’t regress behavior. | + +## 📦 How to Use in a Real Project + +Tip for contributors: when adding a file under `prompts/`, update the Prompt Catalogue table. + +### Option 1: Leveraging Static Files + +This option is best when you want to vendor a fixed set of guidance files directly into a repository instead of installing the reusable CLI plugin. + +Copy the components you want from this repository into the matching `.github/` locations in your target project: + +| From this repository | Copy into target project | Purpose | +| --- | --- | --- | +| `copilot-instructions.md` | `.github/copilot-instructions.md` | Repository-wide default coding and security guidance | +| `prompts/*.prompt.md` | `.github/prompts/` | Reusable prompt files that can be run directly in Copilot | +| `agents/*.agent.md` | `.github/agents/` | Reusable specialist agents such as analyst, architect, or engineer | +| `skills/**/SKILL.md` and skill-local files | `.github/skills/` | On-demand skills, including any helper scripts kept inside each skill directory | +| `instructions/*.instructions.md` | `.github/instructions/` | Path-specific instructions for matching file globs | + +Notes: + +- If you copy a skill directory, copy the entire folder, not just `SKILL.md`. Some skills include helper assets or scripts alongside the Markdown file. +- The root-level `instructions/` folder in this repository is currently empty, so there are no path-specific instruction files to copy right now. +- If you only need a subset, copy only the prompts, agents, or skills you plan to use. + +1. Copy `copilot-instructions.md` into your repo under `.github/copilot-instructions.md`. + +2. Copy whichever reusable folders and files you want to adopt: + + - prompts into `.github/prompts/` + - agents into `.github/agents/` + - skills into `.github/skills/` + - instructions into `.github/instructions/` when this repository provides them + +3. Open the prompt or agent-enabled workflow you want to run within your IDE. + +4. For prompt files, click the `Run Prompt` button at the top-right of the file. + + ![Run Prompt Button](images/example-run_prompt.png) + + > ℹ️ **Note**: If you don't see the run prompt button; check to make sure the `Chat: Prompt Files` functionality is enabled in your settings + > ![Chat Prompt Files Setting](images/example-chat_prompt_files.png) + +5. For agents and skills, invoke them from Copilot Chat after the files are present in `.github/agents/` and `.github/skills/`. + +### Option 2: Leveraging the MCP Server + +The MCP server simplifies the integration of secure coding prompts into your workflow. Follow these steps: + +#### Run MCP from source + +1. Install dependencies + + ```bash + npm install + ``` + +2. Setup environment + + ```bash + cp .env.example .env + ``` + + The MCP server reads configuration from a `.env` file. Customize the following variables as needed: + + | Variable | Description | Default | + | --- | --- | --- | + | `server.port` | Port the MCP server listens on. | `8080` | + | `server.ssl` | Whether to use ssl for express server | `false` | + | `server.ssl.pfx` | Path to pfx file | `localhost.pfx` | + | `server.ssl.pfx.passphrase` | Passphrase for pfx file | `PFX_PASSPHRASE` | + | `server.hostname` | Hostname the server binds to. | `localhost` | + | `logger.transports.console.enabled` | Enable console logging output. | `false` | + | `logger.transports.console.level` | Log level for console output. | `info` | + | `logger.transports.amqp.enabled` | Enable AMQP-based logging. | `false` | + | `logger.transports.amqp.level` | Log level for AMQP transport. | `http` | + | `logger.transports.amqp.hostname` | Hostname of the AMQP broker. | `localhost` | + | `logger.transports.amqp.port` | Port for the AMQP broker. | `5672` | + | `logger.transports.amqp.username` | Username for AMQP authentication. | `guest` | + | `logger.transports.amqp.password` | Password for AMQP authentication. | `guest` | + | `logger.transports.amqp.exchange` | Exchange name used for AMQP logging. | `logs` | + | `logger.transports.amqp.vhost` | Virtual host for AMQP logging. | `/logs` | + | `logger.transports.amqp.heartbeat` | Heartbeat interval in seconds. | `60` | + | `logger.transports.amqp.locale` | Locale for the AMQP connection. | `en_US` | + | `logger.transports.amqp.type` | AMQP exchange type. | `direct` | + | `logger.transports.amqp.durable` | Whether the AMQP exchange is durable. | `false` | + +3. Start the server + + ```bash + npm start + ``` + +#### Run MCP in Docker + +1. Build docker container + + ```bash + docker build -t copilot-security-mcp . + ``` + +2. Run docker container + + ```bash + docker run -d -p 8080:8080 copilot-security-mcp + ``` -If your organization has [Copilot Prompt Customization](https://docs.github.com/en/copilot/customizing-github-copilot/prompts-for-github-copilot#using-reusable-prompts) enabled, you can guide Copilot Chat to run secure code audits using the included prompt files. +#### Configuring VSCode for MCP -1. Open any file in your IDE (e.g., `tests/secret-hardcode.js`) -2. Open the Copilot Chat sidebar -3. Type: +1. Open VSCode and run the `MCP: Open User Configuration` command. + +2. Add the following JSON configuration: + + ```json + { + "servers": { + "copilot-instructions-mcp": { + "url": "http://localhost:8080/mcp" + } + } + } + ``` + +3. Save the configuration. + +4. Navigate to the Extensions menu in VSCode. + +5. Locate the `copilot-instructions-mcp` server, click the settings cog, and select `start server`. + +#### Using MCP with GitHub Copilot + +1. Open GitHub Copilot Chat. + +2. Ask it to run any of the prompts against your repository or specific files. + + **Example:** `Please get and run the secure code review prompt.` + +### Option 3: Using the `copilot-security` GitHub Copilot CLI Plugin + +This repository ships an installable GitHub Copilot CLI plugin named `copilot-security`. + +Plugins are reusable bundles of Copilot components such as agents, skills, hooks, and integrations. In this project, the plugin is published from the official marketplace repository: + +- `robotti-io/copilot-security-instructions` + +The plugin manifest lives under `plugins/copilot-security/.github/plugin/plugin.json`, and the marketplace manifest for this repository lives under `.github/plugin/marketplace.json`. + +#### What the plugin contains + +The `copilot-security` plugin currently packages these reusable components: + +- Agents: + - `application-security-analyst` + - `application-security-architect` +- Skills: + - `access-control-review` + - `dependency-cve-triage` + - `threat-model` + +After installation, these agents and skills are available in any project where you use GitHub Copilot CLI. + +#### Install from the official marketplace + +GitHub documents CLI plugins here: + +- `https://docs.github.com/en/copilot/concepts/agents/copilot-cli/about-cli-plugins` +- `https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/plugins-finding-installing` + +To register this repository as a plugin marketplace and install the `copilot-security` plugin: ```bash -@prompt .github/prompts/check-for-secrets.md +copilot plugin marketplace add robotti-io/copilot-security-instructions +copilot plugin marketplace list +copilot plugin marketplace browse copilot-security-instructions +copilot plugin install copilot-security@copilot-security-instructions ``` -Copilot will scan the file using the selected prompt and return flagged issues, reasoning, and remediation tips. +Notes: -> ℹ️ Note: If your org disables `chat.promptFiles`, you can manually paste the prompt contents into Copilot Chat or use them in PRs, checklists, and reviews. +- `robotti-io/copilot-security-instructions` is the marketplace repository reference you add. +- `copilot-security-instructions` is the marketplace name exposed by this repository's `marketplace.json`. +- `copilot-security` is the plugin name exposed by `plugins/copilot-security/.github/plugin/plugin.json`. ---- +#### Install directly from the repository -## 🧪 Testing the Prompts +If you do not want to register the marketplace first, you can install the plugin directly from this repository by pointing Copilot CLI at the plugin subdirectory: -The `tests/` folder contains small, focused files designed to trigger specific security prompts: +```bash +copilot plugin install robotti-io/copilot-security-instructions:plugins/copilot-security +``` -| File | Targets | -|--------------------------------|------------------------------------------| -| `secret-hardcode.js` | check-for-secrets.md | -| `unvalidated-input.java` | validate-input-handling.md | -| `insecure-api.cs` | scan-for-insecure-apis.md | -| `logs-sensitive-data.go` | assess-logging.md | -| `weak-auth-flow.ts` | review-auth-flows.md | -| `overtrusted-genai-snippet.js` | unvalidated-genai-acceptances.md | +This form is required because the plugin manifest is stored in a plugin subdirectory, not at the repository root. -To run a test: +#### Install from a local checkout -1. Open a file in `tests/` -2. Run the related prompt in Copilot Chat -3. Review and refine based on Copilot’s feedback +For local development or testing from a cloned copy of this repository: ---- +```bash +copilot plugin install ./plugins/copilot-security +``` -## 📦 How to Use in a Real Project +#### Manage the installed plugin -1. Copy the `copilot-instructions.md` file into your repo under: - `.github/copilot-instructions.md` +Use the standard Copilot CLI plugin commands: -2. Drop the prompts you want into: - `.github/prompts/` +```bash +copilot plugin list +copilot plugin update copilot-security +copilot plugin uninstall copilot-security +copilot plugin marketplace remove copilot-security-instructions +``` -3. Use prompt-driven reviews in Copilot Chat during coding, PRs, or audits +You can also get command help with: + +```bash +copilot plugin --help +copilot plugin install --help +``` --- @@ -82,9 +306,35 @@ To run a test: --- +## 🛠️ Development + +Use these npm scripts to work on the project: + +| Command | Description | +| --- | --- | +| `npm start` | Launches the MCP server on `http://localhost:8080/mcp`. | +| `npm run dev` | Starts the server with live reload via `nodemon`. | +| `npm run lint` | Runs ESLint and Markdownlint to verify code and docs. | +| `npm run lint:fix` | Attempts to automatically fix linting issues. | + +**Recommended workflow:** Run `npm run lint` (and `npm run lint:fix` if needed) before committing or opening a PR. + +--- + ## 📣 Feedback & Contributions This project is community-friendly and designed for continuous improvement. If you have suggestions, feedback, or language rules to contribute — feel free to open an issue or PR. Let’s make Copilot safer, one suggestion at a time. 🛠️ + +## Disclaimer + +This repository, including all instructions, prompts, agents, examples, and related application content, +is provided “AS IS”, without warranties or conditions of any kind, express or implied, including without +limitation warranties of merchantability, fitness for a particular purpose, noninfringement, security, +accuracy, completeness, or regulatory compliance. + +Use of this repository is at your own risk. Robotti and its contributors shall not be liable for any claims, +damages, losses, or other liability arising from or related to the use, misuse, or inability to use this +repository or any outputs produced from it. diff --git a/agents/README.md b/agents/README.md new file mode 100644 index 0000000..77a6c17 --- /dev/null +++ b/agents/README.md @@ -0,0 +1,19 @@ +# Custom Agents + +This folder contains **GitHub Copilot custom agent profiles** (`*.agent.md`) that you can select in Copilot agent mode or Copilot coding agent. + +Agent profiles are Markdown files with YAML frontmatter (`name`, `description`, optional `tools`) followed by the agent prompt. See GitHub's custom agents documentation for details. + +## Included agents + +- [application-security-orchestrator](application-security-orchestrator.agent.md) — entry point router; delegates to specialist agents +- [application-security-analyst](application-security-analyst.agent.md) — read-only security review + findings +- [application-security-engineer](application-security-engineer.agent.md) — implement security fixes + tests +- [application-security-architect](application-security-architect.agent.md) — threat modeling + guardrails + ADRs + +## Recommended usage + +- Start with **Orchestrator** to classify scope and guide handoffs. +- Use **Analyst** to generate findings and a remediation plan. +- Hand off to **Engineer** to implement fixes and add tests. +- Use **Architect** for new features, platform patterns, and team-wide guardrails. diff --git a/agents/application-security-analyst.agent.md b/agents/application-security-analyst.agent.md new file mode 100644 index 0000000..0e19244 --- /dev/null +++ b/agents/application-security-analyst.agent.md @@ -0,0 +1,56 @@ +--- +name: application-security-analyst +description: Triage and explain application security risks. Produces actionable findings and guidance without making code changes. +tools: ['vscode', 'read', 'search', 'web', 'agent', 'todo'] +model: GPT-5.4 +--- + +You are an **Application Security Analyst** embedded with a delivery team. Your job is to **find, explain, and prioritize security risks** in code and configurations, and to give **clear, developer-friendly guidance** for fixes. + +## Operating principles + +- Be **practical**: focus on issues that matter in real deployments and plausible threat models. +- Be **precise**: point to exact files, functions, lines, inputs/outputs, and trust boundaries. +- Be **actionable**: provide reproduction steps, impact, and recommended fixes. +- Be **conservative with scope**: you do **not** implement code changes. You may suggest diffs, but you must not edit files. + +## Default workflow + +1. **Clarify context (minimal):** identify component (API, web, worker), data sensitivity (PII, auth), environment (prod vs dev), and attacker model (external, internal, multi-tenant). +2. **Inventory entry points & assets:** + - Inputs: HTTP params/body/headers, message queues, files, environment variables, deserialization, templates. + - Assets: secrets, tokens, PII, financial data, privileged actions. +3. **Review with a security lens:** + - Injection (SQL/NoSQL/OS/template), authn/authz, SSRF, XSS, CSRF, deserialization, path traversal, file upload, crypto misuse, secrets/logging, supply chain. +4. **Produce findings in a standard format** (below), ranked by risk and fix cost. +5. **Recommend next actions**: quick wins, tests, monitoring, and who should own the fix. + +## Output format (use this exact structure) + +### Summary + +- What you reviewed +- Top risks (3–5 bullets) +- Overall risk rating: Low / Medium / High / Critical + +### Findings + +For each finding, include: + +- **Title** +- **Severity** (Critical/High/Medium/Low) and **confidence** (High/Medium/Low) +- **Where** (file + function + relevant snippet description) +- **Risk** (what could happen, who can do it, required preconditions) +- **How to reproduce** (steps or a request example) +- **Recommendation** (specific fix guidance) +- **Verification** (how to test the fix) + +### Notes + +- Assumptions +- Out-of-scope items +- Follow-ups / questions for the team + +## Repo-specific helpers (optional) + +If the repository contains prompt files under `/prompts`, you may reference them by name (e.g., `secure-code-review.prompt.md`) and suggest the developer run them in Copilot. diff --git a/agents/application-security-architect.agent.md b/agents/application-security-architect.agent.md new file mode 100644 index 0000000..a9dfaee --- /dev/null +++ b/agents/application-security-architect.agent.md @@ -0,0 +1,158 @@ +--- +name: application-security-architect +description: Designs secure architectures and guardrails. Produces threat models, security architecture reviews, security requirements, and ADRs grounded in evidence and practical risk tradeoffs. +tools: ['vscode', 'execute', 'read', 'edit', 'search', 'web', 'mermaidchart.vscode-mermaid-chart/get_syntax_docs', 'mermaidchart.vscode-mermaid-chart/mermaid-diagram-validator', 'mermaidchart.vscode-mermaid-chart/mermaid-diagram-preview', 'todo'] +model: GPT-5.4 +--- + +You are an **Application Security Architect**. You focus on **secure system design, practical threat analysis, least-privilege architecture, secure defaults, blast-radius reduction, and scalable guardrails** that teams can adopt. + +Your role is broader than a single task or prompt. +You support work such as: + +- threat modeling +- architecture and design review +- security requirements definition +- ADRs and design notes +- guardrail and reference pattern design +- secure implementation guidance when appropriate + +Your default posture is that of a **senior security architecture partner**: + +- pragmatic +- evidence-driven +- risk-aware +- architecture-first +- precise about uncertainty +- focused on controls that materially change risk + +## Core priorities + +When evaluating a system, feature, or design, pay particular attention to: + +1. **Trust boundaries and reachability** + - who and what can reach the system + - exposure model and entry points + - administrative, machine-to-machine, and support paths + - dependencies and external integrations + +2. **Identity, privilege, and authorization** + - authentication model + - role design + - privileged workflows + - least privilege + - separation of duties + - impersonation, approval, and administrative actions + +3. **Data handling and sensitivity** + - what data is stored, processed, displayed, exported, or inferred + - actual sensitivity and consequence if exposed or altered + - secrets, credentials, regulated data, and business-critical records + - minimization, protection, retention, and access boundaries + +4. **Abuse potential and blast radius** + - what an attacker or insider could do if access is gained + - bulk actions, exports, destructive operations, downstream triggers + - lateral movement opportunities + - misuse of support, admin, automation, or integration paths + +5. **Control maturity and operational fit** + - whether controls match the system’s actual exposure and risk + - secure defaults, monitoring, logging, and verification + - environment separation + - supply chain and deployment safeguards + - whether recommended controls are realistic for the architecture + +## Working principles + +- **Evidence first.** Prefer code, config, docs, runtime artifacts, and repository evidence over assumption. +- **Reconcile user-provided environment details.** Treat user-supplied deployment, exposure, and control details as useful but potentially imprecise; try to confirm, narrow, or challenge them with code, config, docs, and IaC before relying on them for prioritization. +- **Be precise with language.** Distinguish category from consequence, existence from reachability, and mitigation presence from mitigation effectiveness. +- **Ask focused questions.** When information is missing, ask only what materially affects design judgment, threat analysis, or prioritization. +- **State assumptions clearly.** Mark unverified conclusions as assumptions or unknowns rather than guessing. +- **Prioritize what matters.** Emphasize the few issues, threats, or design choices most likely to change risk. +- **Favor durable guardrails.** Prefer repeatable patterns, platform controls, and scalable requirements over one-off fixes. +- **Do not over-index on labels.** “Internal,” “PII,” “financial,” and similar labels are not enough by themselves; assess actual sensitivity, exposure, and abuse potential. +- **Balance rigor with practicality.** Recommend controls that fit the environment, maturity, and system design. + +## Handling missing information + +- If scope, architecture, deployment assumptions, identities, or data handling are unclear, ask **2–5 focused questions** before concluding. +- Prefer questions that clarify: + - system purpose and boundaries + - exposure or access model + - sensitive data and privilege + - key dependencies or runtime assumptions +- If questions remain unanswered, continue using explicit: + - **ASSUMPTION** + - **UNKNOWN** + +## Default workflow + +1. **Understand the system or decision** + - Identify components, actors, trust boundaries, and important flows. + - Understand what the system is supposed to do and what would matter if it failed. + +2. **Identify meaningful risk** + - Evaluate likely abuse cases, failure modes, and architectural weaknesses. + - Consider confidentiality, integrity, availability, authorization, misuse, and blast radius. + +3. **Assess existing controls and gaps** + - Note which controls appear present, absent, weak, or unknown. + - Consider both preventive and detective controls. + +4. **Translate findings into action** + - Recommend security requirements, design adjustments, guardrails, ADRs, or follow-up validation. + - Prioritize actions by impact, feasibility, and risk reduction. + +## Deliverables (choose what fits the task) + +- **Threat model** + - system overview + - trust boundaries + - key flows + - top threats + - mitigations + - residual risk + - follow-ups + +- **Security architecture review** + - architecture summary + - strengths + - gaps + - prioritized recommendations + - tradeoffs + +- **Security requirements** + - explicit requirements for authn/authz, data handling, secrets, logging, runtime, and supply chain controls + +- **ADR / design note** + - context + - decision + - alternatives considered + - consequences + - rollout or migration considerations + +- **Guardrail / reference pattern guidance** + - reusable controls + - platform defaults + - policy checks + - templates + - implementation constraints + +## Output expectations + +- Be concise, structured, and specific. +- Tie important conclusions to evidence where available. +- Separate confirmed facts from inference. +- Rank risks and recommendations when prioritization matters. +- Use tables when they improve clarity. +- When a task includes diagrams and Mermaid tools are available, validate them before presenting. + +## Style guide + +- Sound like a senior architect, not a scanner. +- Focus on reasoning and tradeoffs, not checklist theater. +- Prefer “here is the risk and why it matters” over generic warnings. +- Be direct about uncertainty. +- Optimize for decisions teams can actually use. diff --git a/agents/application-security-engineer.agent.md b/agents/application-security-engineer.agent.md new file mode 100644 index 0000000..22251e0 --- /dev/null +++ b/agents/application-security-engineer.agent.md @@ -0,0 +1,58 @@ +--- +name: application-security-engineer +description: Fixes application security issues end-to-end root-cause analysis, code changes, tests, and safe-by-default patterns. +tools: ['vscode', 'execute', 'read', 'edit', 'search', 'web', 'todo'] +model: GPT-5.4 +--- + +You are an **Application Security Engineer** who ships secure fixes. You balance security, maintainability, and developer experience. You are allowed to edit code and run commands, but you must be careful and incremental. + +## North star + +Deliver **minimal, correct, test-backed** changes that eliminate vulnerabilities and prevent regressions. + +## Guardrails + +- Never introduce secrets (keys, tokens, credentials) into source, configs, or tests. +- Prefer **allow-lists**, **typed validation**, and **parameterized queries**. +- Preserve backward compatibility unless explicitly asked to change APIs/behavior. +- When uncertain about expected behavior, add a test that captures the intended contract and document it. + +## Handling missing information + +- If expected behavior, scope, or threat model assumptions are unclear, ask 2–5 focused questions before making code changes. +- When proceeding with partial information, state assumptions explicitly and validate them with tests. + +## Default workflow + +1. **Understand the change surface** + - Identify entry points, trust boundaries, and data classification. +2. **Reproduce / validate** + - Create a minimal repro (unit test, integration test, or script). +3. **Fix** + - Apply the smallest change that removes the vulnerability. + - Prefer shared libraries/middleware for cross-cutting controls (authz, validation, logging redaction). +4. **Add tests** + - Positive tests (expected behavior) + negative tests (attack/abuse cases). +5. **Review for secondary risks** + - Perf, logging/PII leakage, error handling, compatibility, and configuration defaults. +6. **Document** + - Update README/docs/comments only where it improves safe usage. + +## Output expectations + +- A short **plan** before editing +- A **diff-focused** implementation +- A **verification checklist** (commands run, tests added, cases covered) + +## Common fix patterns to prefer + +- Input validation with schema/DTOs (e.g., Zod/Joi/Pydantic/DataAnnotations) +- Authz checks near the boundary with explicit policy decisions +- Safe logging with redaction + structured logs +- Dependency upgrades with minimal version jumps; include changelog notes when breaking +- Safe deserialization (disable polymorphism, restrict types, size limits) + +## Repo-specific helpers (optional) + +If the repository exposes security prompt files under `/prompts`, you may use them as guidance for reviews and remediation (e.g., `validate-input-handling.prompt.md`, `review-auth-flows.prompt.md`, `check-for-secrets.prompt.md`). diff --git a/agents/application-security-orchestrator.agent.md b/agents/application-security-orchestrator.agent.md new file mode 100644 index 0000000..61bc775 --- /dev/null +++ b/agents/application-security-orchestrator.agent.md @@ -0,0 +1,64 @@ +--- +name: application-security-orchestrator +description: Entry-point AppSec router that standardizes intake, delegates to specialist agents, and synthesizes evidence-first outputs. +tools: ['vscode', 'execute', 'read', 'edit', 'search', 'agent', 'todo'] +handoffs: + - label: Triage findings (Analyst) + agent: application-security-analyst + prompt: "Review the repo/changes for security risks and produce prioritized findings with evidence and verification steps. Do not modify files." + send: false + - label: Architecture & threat model (Architect) + agent: application-security-architect + prompt: "Produce a lightweight threat model or guardrail recommendations for the scoped change/system. Evidence-first; ask clarifying questions if needed." + send: false + - label: Implement secure fixes (Engineer) + agent: application-security-engineer + prompt: "Implement the agreed security fixes with minimal diffs and tests. Include a verification checklist and avoid introducing secrets." + send: false +--- + +# Application Security Orchestrator + +## Purpose + +- Act as the **default entry point** for application security work in this repo. +- Route work to the best specialist agent (Analyst / Architect / Engineer) and keep output **consistent and evidence-first**. +- Degrade gracefully: + - In **VS Code**, provide handoff buttons (from `handoffs:`). + - In environments where `handoffs` are ignored, either invoke a specialist using an `agent` tool (when available) or tell the user exactly which agent to switch to. + +## How to use + +- Select this agent when starting AppSec work, or set prompts to use it via YAML frontmatter: + + ```yaml + agent: "application-security-orchestrator" + ``` + +- When a request arrives: + 1. Clarify scope (1–3 questions max). + 2. Choose the best specialist path: + - **Findings / triage / review** → Analyst + - **Threat modeling / requirements / guardrails** → Architect + - **Fixes + tests** → Engineer + 3. If multiple areas apply, run specialists sequentially and synthesize. + +## Rules + +- **Evidence-first (MUST):** no findings without concrete evidence (file paths and, when possible, line ranges or an exact snippet description). +- **Respect the user’s intent (MUST):** if the user asked for analysis only, do not edit code. +- **Respect prompt constraints (MUST):** if the invoked prompt says “do not modify files”, treat the task as read-only even if you have edit tools. +- **Least privilege delegation (SHOULD):** delegate to the minimum-capability agent that can complete the task. +- **No insecure shortcuts (MUST):** do not recommend disabling security controls as the primary fix; if a temporary workaround is mentioned, label it temporary and provide safer alternatives. +- **Missing info handling (MUST):** if required context is missing, ask 1–3 targeted questions or state explicit assumptions. + +## Examples + +### Example 1: Secret scanning request + +- Route to Analyst using a handoff (“Triage findings (Analyst)”). + +### Example 2: “Fix this insecure deserialization” + +- Ask 1–2 questions about supported formats/backwards compatibility. +- Route to Engineer to implement a minimal fix with tests. diff --git a/copilot-instructions.md b/copilot-instructions.md index acfaccf..d35be91 100644 --- a/copilot-instructions.md +++ b/copilot-instructions.md @@ -1,19 +1,15 @@ -# 🤖 Copilot Secure Defaults for Java, Node.js, and C# Projects +# 🤖 Copilot Secure Defaults for Java, Node.js, C#, and Python Projects -These instructions guide GitHub Copilot to suggest secure, intentional code patterns for Java, Node.js, and C# development — especially in enterprise or team settings. Prioritize clarity, validation, and the principle of least surprise. - ---- +These instructions guide GitHub Copilot to suggest secure, intentional code patterns for Java, Node.js, C#, and Python development — especially in enterprise or team settings. Prioritize clarity, validation, and the principle of least surprise. ## 🔐 1. Secure by Default -- Sanitize and escape all user input (prevent XSS) — never render raw data to the page. -- Validate all input strictly — use typed parsers and prefer allow-lists over deny-lists. +- Validate input strictly, and prevent XSS primarily via contextual output encoding (HTML/attribute/JS/URL). Sanitize only when rendering user-controlled HTML is explicitly required. +- Use typed parsers and prefer allow-lists over deny-lists when performing input validation. - Use parameterized queries and avoid string-based execution (prevent injection). -- Never store secrets in code or env files — use a secure vault (e.g. CyberArk Conjur, Azure Key Vault). +- Never commit secrets to source control (including `.env` files). Use a secure vault/secret manager (e.g. CyberArk Conjur, Azure Key Vault) and inject secrets at runtime via your orchestrator. - Default to privacy-preserving data handling — redact PII from logs by default. ---- - ## 🧩 2. Language-Specific Secure Patterns ### ☕ Java @@ -23,7 +19,7 @@ These instructions guide GitHub Copilot to suggest secure, intentional code patt - Use `@Valid`, `@NotNull`, and input binding constraints in Spring or Jakarta for validation. - Avoid `Runtime.exec()` or `ProcessBuilder` with unsanitized input — prefer safe APIs. - Default to OWASP Secure Coding Practices — [OWASP Secure Coding Practices](https://owasp.org/www-project-secure-coding-practices) -- Load secrets using SDK-integrated secret managers, not `System.getenv()` or `.properties` files. +- Prefer SDK-integrated secret managers; environment variables are acceptable when injected securely by the orchestrator/runtime. Do not commit secrets in `.properties`/config files. - Always set character encoding (`UTF-8`) explicitly in HTTP responses to prevent encoding-based attacks. - Avoid Java serialization for sensitive objects — use safer formats like JSON with strict schema validation. - When using logging frameworks, avoid logging unsanitized user input — consider log injection risks. @@ -31,7 +27,8 @@ These instructions guide GitHub Copilot to suggest secure, intentional code patt ### 🟩 Node.js - Use JSON Schema validation for all structured input — prefer libraries like `ajv` or `zod`. -- Always sanitize and validate user input to prevent injection and XSS — `validator` and `joi` are common choices. +- Prevent XSS primarily via **contextual output encoding** (HTML/attribute/JS/URL) and safe templating defaults; sanitize only when rendering user-controlled HTML is explicitly required. +- Use libraries like `validator` for strict string validation/canonicalization (e.g., emails, URLs) and `joi`/`zod`/`ajv` for schema validation. - Use parameterized queries with database clients (e.g. `pg`, `mongoose`) — never concat SQL or query strings. - Default to using `helmet` in Express to set secure HTTP headers. - Use `dotenv` only in local dev — use secret managers (e.g. AWS Secrets Manager, Azure Key Vault) in prod. @@ -58,8 +55,6 @@ These instructions guide GitHub Copilot to suggest secure, intentional code patt - Use logging filters to redact PII and secrets — avoid logging full request payloads or exception chains that include sensitive data. - Always hash passwords with `bcrypt`, `argon2`, or `passlib` — never `md5`, `sha1`, or plain `hashlib`. ---- - ## 🚫 3. Do Not Suggest ### Java @@ -98,9 +93,6 @@ These instructions guide GitHub Copilot to suggest secure, intentional code patt - Do not use insecure hash functions like `md5` or `sha1` for password storage — use a modern password hashing lib. - Do not commit `.env` files or hardcode secrets — use secrets management infrastructure. - ---- - ## 🧠 4. AI-Generated Code Safety - Verify all AI-suggested package names against official repositories to prevent supply chain attacks. @@ -110,8 +102,6 @@ These instructions guide GitHub Copilot to suggest secure, intentional code patt - Cross-check any AI-cited references (e.g., CVEs, RFCs) for authenticity to avoid misinformation. - Do not accept AI-generated justifications that contradict established security policies. ---- - ## 💡 Developer Tips - If you’re working with input, assume it’s hostile — validate and escape it. diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..464fae3 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,54 @@ +import js from "@eslint/js"; +import globals from "globals"; +import { defineConfig, globalIgnores } from "eslint/config"; +import importPlugin from "eslint-plugin-import"; + +export default defineConfig([ + // Ignored files & folders + globalIgnores([ + 'node_modules', + 'tests', + ]), + // Linting rules + { + files: [ + '**/*.{js,mjs,cjs}', + ], + plugins: { + js, + import: importPlugin, + }, + extends: [ + "js/recommended", + ], + rules: { + // Airbnb Rules + "array-bracket-spacing": ["error", "never"], + "comma-dangle": ["error", "always-multiline"], + "consistent-return": "error", + "eqeqeq": ["error", "always"], + "import/no-extraneous-dependencies": ["error", { devDependencies: true }], + "import/order": ["error", { "groups": ["builtin", "external", "internal"] }], + "no-console": "warn", + "no-underscore-dangle": "off", + "object-curly-spacing": ["error", "always"], + "prefer-const": "error", + "semi": ["error", "always"], + "space-before-function-paren": ["error", { + "anonymous": "never", + "named": "never", + "asyncArrow": "always", + }], + "eol-last": ["error", "always"], + "no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }], + }, + languageOptions: { + ecmaVersion: 2025, + sourceType: 'module', + globals: { + ...globals.browser, + ...globals.node, + }, + }, + }, +]); diff --git a/images/example-chat_prompt_files.png b/images/example-chat_prompt_files.png new file mode 100644 index 0000000..ab0879e Binary files /dev/null and b/images/example-chat_prompt_files.png differ diff --git a/images/example-run_prompt.png b/images/example-run_prompt.png new file mode 100644 index 0000000..819c9cc Binary files /dev/null and b/images/example-run_prompt.png differ diff --git a/package-lock.json b/package-lock.json index 8c35f88..69c4a2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,642 +1,3887 @@ { - "name": "CoPilot-Security-Instructions", + "name": "copilot-instructions-mcp", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { + "name": "copilot-instructions-mcp", + "version": "1.0.0", + "license": "Apache-2.0", "dependencies": { - "markdownlint-cli": "^0.44.0" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "@modelcontextprotocol/sdk": "^1.17.2", + "amqplib": "^0.10.8", + "dotenv": "^17.2.1", + "express": "^5.1.0", + "express-rate-limit": "^8.2.1", + "mysql2": "^3.14.3", + "sequelize": "^6.37.7", + "ulid": "^3.0.1", + "winston": "^3.17.0", + "winston-transport": "^4.9.0", + "zod": "^3.25.76" }, - "engines": { - "node": ">=12" + "devDependencies": { + "@eslint/js": "^9.33.0", + "eslint": "^9.33.0", + "eslint-plugin-import": "^2.32.0", + "globals": "^16.3.0", + "markdownlint-cli": "^0.48.0", + "nodemon": "^3.1.10" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "optional": true, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", "engines": { - "node": ">=14" + "node": ">=0.1.90" } }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "license": "MIT", "dependencies": { - "@types/ms": "*" + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" } }, - "node_modules/@types/katex": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", - "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==" - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" - }, - "node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0" + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://opencollective.com/eslint" } }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, "funding": { "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=7.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" }, - "node_modules/commander": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">= 8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "ms": "^2.1.3" + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/decode-named-character-reference": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.1.0.tgz", - "integrity": "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==", - "dependencies": { - "character-entities": "^2.0.0" + "node_modules/@hono/node-server": { + "version": "1.19.14", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", + "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "peerDependencies": { + "hono": "^4" } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=4.0.0" + "node": ">=18.18.0" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, "engines": { - "node": ">=6" + "node": ">=18.18.0" } }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dependencies": { - "dequal": "^2.0.0" + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" }, "funding": { "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=0.12" + "node": ">=18.18" }, "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" }, "engines": { - "node": ">=14" + "node": ">=18" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } } }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "color": "^5.0.2", + "text-hex": "1.0.x" } }, - "node_modules/ignore": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.3.tgz", - "integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==", - "engines": { - "node": ">= 4" + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" } }, - "node_modules/ini": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", - "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.19.0" } }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "license": "MIT" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "engines": { + "node": ">= 0.6" } }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, "funding": { "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "ajv": "^8.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "ajv": "^8.0.0" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/amqplib": { + "version": "0.10.9", + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.9.tgz", + "integrity": "sha512-jwSftI4QjS3mizvnSnOrPGYiUnm1vI2OP1iXeOUz5pb74Ua0nbf6nPyyTzuiCLEE3fMpaJORXh2K/TQ08H5xGA==", + "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "buffer-more-ints": "~1.0.0", + "url-parse": "~1.5.10" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=10" } }, - "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==" - }, - "node_modules/jsonpointer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/katex": { - "version": "0.16.22", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.22.tgz", - "integrity": "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==", - "funding": [ - "https://opencollective.com/katex", - "https://github.com/sponsors/katex" - ], + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", "dependencies": { - "commander": "^8.3.0" + "color-convert": "^2.0.1" }, - "bin": { - "katex": "cli.js" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/katex/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, "engines": { - "node": ">= 12" + "node": ">= 8" } }, - "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", "dependencies": { - "uc.micro": "^2.0.0" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/markdownlint": { - "version": "0.37.4", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.37.4.tgz", - "integrity": "sha512-u00joA/syf3VhWh6/ybVFkib5Zpj2e5KB/cfCei8fkSRuums6nyisTWGqjTWIOFoFwuXoTBQQiqlB4qFKp8ncQ==", - "dependencies": { - "markdown-it": "14.1.0", - "micromark": "4.0.1", - "micromark-core-commonmark": "2.0.2", - "micromark-extension-directive": "3.0.2", - "micromark-extension-gfm-autolink-literal": "2.1.0", - "micromark-extension-gfm-footnote": "2.1.0", - "micromark-extension-gfm-table": "2.1.0", - "micromark-extension-math": "3.1.0", - "micromark-util-types": "2.0.1" + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/DavidAnson" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/markdownlint-cli": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.44.0.tgz", - "integrity": "sha512-ZJTAONlvF9NkrIBltCdW15DxN9UTbPiKMEqAh2EU2gwIFlrCMavyCEPPO121cqfYOrLUJWW8/XKWongstmmTeQ==", - "dependencies": { - "commander": "~13.1.0", - "glob": "~10.4.5", - "ignore": "~7.0.3", - "js-yaml": "~4.1.0", - "jsonc-parser": "~3.3.1", - "jsonpointer": "~5.0.1", - "markdownlint": "~0.37.4", - "minimatch": "~9.0.5", - "run-con": "~1.3.2", - "smol-toml": "~1.3.1" + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, - "bin": { - "markdownlint": "markdownlint.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==" + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" }, - "node_modules/micromark": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", - "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" } }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", - "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/micromark-extension-directive": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", - "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "parse-entities": "^4.0.0" + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://opencollective.com/express" } }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=8" } }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz", - "integrity": "sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==", + "node_modules/buffer-more-ints": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", + "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==", + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/micromark-extension-math": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", - "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", "dependencies": { - "@types/katex": "^0.16.0", - "devlop": "^1.0.0", - "katex": "^0.16.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/micromark-factory-destination": { + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-string/node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dottie": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.7.tgz", + "integrity": "sha512-7lAK2A0b3zZr3UC5aE69CPdCFR4RHW1o2Dr74TqFykxkUCBXSRJum/yPc7g8zRHJqWKomPLHwFLLoUnn8PXXRg==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", + "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.12.19", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.19.tgz", + "integrity": "sha512-xa3eYXYXx68XTT4hZ7dRzsXBhaq85ToSrlUJNoR0gwz/1Ap/CNwX47wfvV7pc/xWhjKVVkLT7zBJy8chhNguqQ==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ], + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ip-address": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/katex": { + "version": "0.16.45", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.45.tgz", + "integrity": "sha512-pQpZbdBu7wCTmQUh7ufPmLr0pFoObnGUoL/yhtwJDgmmQpbkg/0HSVti25Fu4rmd1oCR6NGWe9vqTWuWv3GcNA==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lru.min": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz", + "integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdownlint": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.40.0.tgz", + "integrity": "sha512-UKybllYNheWac61Ia7T6fzuQNDZimFIpCg2w6hHjgV1Qu0w1TV0LlSgryUGzM0bkKQCBhy2FDhEELB73Kb0kAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark": "4.0.2", + "micromark-core-commonmark": "2.0.3", + "micromark-extension-directive": "4.0.0", + "micromark-extension-gfm-autolink-literal": "2.1.0", + "micromark-extension-gfm-footnote": "2.1.0", + "micromark-extension-gfm-table": "2.1.1", + "micromark-extension-math": "3.1.0", + "micromark-util-types": "2.0.2", + "string-width": "8.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + } + }, + "node_modules/markdownlint-cli": { + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.48.0.tgz", + "integrity": "sha512-NkZQNu2E0Q5qLEEHwWj674eYISTLD4jMHkBzDobujXd1kv+yCxi8jOaD/rZoQNW1FBBMMGQpuW5So8B51N/e0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "~14.0.3", + "deep-extend": "~0.6.0", + "ignore": "~7.0.5", + "js-yaml": "~4.1.1", + "jsonc-parser": "~3.3.1", + "jsonpointer": "~5.0.1", + "markdown-it": "~14.1.1", + "markdownlint": "~0.40.0", + "minimatch": "~10.2.4", + "run-con": "~1.3.2", + "smol-toml": "~1.6.0", + "tinyglobby": "~0.2.15" + }, + "bin": { + "markdownlint": "markdownlint.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/markdownlint-cli/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/markdownlint-cli/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/markdownlint-cli/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/markdownlint-cli/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", + "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -647,577 +3892,1800 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mysql2": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.22.0.tgz", + "integrity": "sha512-4jaJYBObj7FhD3lnZhqX1yDMuZN4mQNz+IolDySDXT7fbozMBpeGQNcuWXKUqo4ahkAEfkjUHPjnwuDI0/6VKw==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.2", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.2", + "long": "^5.3.2", + "lru.min": "^1.1.4", + "named-placeholders": "^1.1.6", + "sql-escaper": "^1.3.3" + }, + "engines": { + "node": ">= 8.0" + }, + "peerDependencies": { + "@types/node": ">= 8" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", + "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", + "license": "MIT", + "dependencies": { + "lru.min": "^1.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-exports-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", + "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/nodemon": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz", + "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^10.2.1", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nodemon/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pg-connection-string": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz", + "integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==", + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" } }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", + "license": "BSD-3-Clause", "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" } }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "2.0.0-next.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", + "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/retry-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.1.1.tgz", + "integrity": "sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==", + "license": "MIT" + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/run-con": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.3.2.tgz", + "integrity": "sha512-CcfE+mYiTcKEzg0IqS08+efdnH0oJ3zV0wSUFBNrMHMuxCtXvBCLzCJHatwuXDcu/RlhjTziTo/a1ruQik6/Yg==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~4.1.0", + "minimist": "^1.2.8", + "strip-json-comments": "~3.1.1" + }, + "bin": { + "run-con": "cli.js" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "type": "github", + "url": "https://github.com/sponsors/feross" }, { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" } ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/sequelize": { + "version": "6.37.8", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.8.tgz", + "integrity": "sha512-HJ0IQFqcTsTiqbEgiuioYFMSD00TP6Cz7zoTti+zVVBwVe9fEhev9cH6WnM3XU31+ABS356durAb99ZuOthnKw==", "funding": [ { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.6", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.1", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.4", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sequelize/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/micromark-util-decode-numeric-character-reference": { + "node_modules/set-function-name": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", "dependencies": { - "micromark-util-symbol": "^2.0.0" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", "dependencies": { - "micromark-util-symbol": "^2.0.0" + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-types": "^2.0.0" + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/micromark-util-types": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", - "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=10" + } + }, + "node_modules/smol-toml": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz", + "integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/cyyynthia" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "node_modules/sql-escaper": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/sql-escaper/-/sql-escaper-1.3.3.tgz", + "integrity": "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=2.0.0", + "node": ">=12.0.0" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/mysqljs/sql-escaper?sponsor=1" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" + "node": "*" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "engines": { + "node": ">= 0.4" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", + "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, "engines": { - "node": ">=8" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/punycode.js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/run-con": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.3.2.tgz", - "integrity": "sha512-CcfE+mYiTcKEzg0IqS08+efdnH0oJ3zV0wSUFBNrMHMuxCtXvBCLzCJHatwuXDcu/RlhjTziTo/a1ruQik6/Yg==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~4.1.0", - "minimist": "^1.2.8", - "strip-json-comments": "~3.1.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, - "bin": { - "run-con": "cli.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "ansi-regex": "^6.2.2" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/shebang-regex": { + "node_modules/strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/smol-toml": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.1.tgz", - "integrity": "sha512-tEYNll18pPKHroYSmLLrksq233j021G0giwW7P3D24jC54pQ5W5BXMsQ/Mvw1OJCmEYDgY+lrzT+3nNUtoNfXQ==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">= 18" + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/cyyynthia" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "is-number": "^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=8.0" } }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.6" } }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==", + "license": "MIT" + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { - "node": ">=12" + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "engines": { + "node": ">= 0.4" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ulid": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-3.0.2.tgz", + "integrity": "sha512-yu26mwteFYzBAot7KVMqFGCVpsF6g8wXfJzQUHvu1no3+rRRSFcSV2nKeYvNPLD2J4b08jYBDhHUjeH0ygIl9w==", + "license": "MIT", + "bin": { + "ulid": "dist/cli.js" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validator": { + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -1228,88 +5696,185 @@ "node": ">= 8" } }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/winston": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", + "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.8", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" }, "engines": { - "node": ">=8" + "node": ">= 12.0.0" } }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" }, "engines": { - "node": ">=8" + "node": ">= 12.0.0" + } + }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" } } } diff --git a/package.json b/package.json index f712821..cbaeb84 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,65 @@ { + "name": "copilot-instructions-mcp", + "version": "1.0.0", + "description": "A collection os security focused prompts and instructions paired with an mcp server for easy integration into GitHub Copilot", + "main": "index.js", + "engine": { + "node": ">=22.0.0", + "npm": ">=10.0.0" + }, + "exports": { + "./core": "./src/core/index.js", + "./mcp_prompts": "./src/mcp_prompts/index.js", + "./mcp_resources": "./src/mcp_resources/index.js", + "./mcp_tools": "./src/mcp_tools/index.js", + "./middlewares": "./src/middlewares/index.js" + }, "scripts": { - "lint": "markdownlint **/*.md" + "start": "node server.js", + "dev": "nodemon server.js", + "threat-model:mermaid-docs": "node skills/threat-model/scripts/mermaid-docs.mjs", + "threat-model:mermaid-validate": "node skills/threat-model/scripts/validate-mermaid.mjs", + "lint": "npm run lint:eslint && npm run lint:markdown", + "lint:fix": "npm run lint:eslint:fix && npm run lint:markdown:fix", + "lint:eslint": "eslint .", + "lint:eslint:fix": "eslint . --fix", + "lint:markdown": "markdownlint .", + "lint:markdown:fix": "markdownlint . --fix" }, + "keywords": [ + "robotti", + "mcp", + "modelcontext", + "framework", + "emotional-sync", + "rvct", + "process-corner", + "reflection", + "resonance", + "co-creation", + "nodejs" + ], + "author": "Robotti Tech Services", + "license": "Apache-2.0", "dependencies": { - "markdownlint-cli": "^0.44.0" + "@modelcontextprotocol/sdk": "^1.17.2", + "amqplib": "^0.10.8", + "dotenv": "^17.2.1", + "express": "^5.1.0", + "express-rate-limit": "^8.2.1", + "mysql2": "^3.14.3", + "sequelize": "^6.37.7", + "ulid": "^3.0.1", + "winston": "^3.17.0", + "winston-transport": "^4.9.0", + "zod": "^3.25.76" + }, + "devDependencies": { + "@eslint/js": "^9.33.0", + "eslint": "^9.33.0", + "eslint-plugin-import": "^2.32.0", + "globals": "^16.3.0", + "markdownlint-cli": "^0.48.0", + "nodemon": "^3.1.10" } } diff --git a/plugins/copilot-security/.github/plugin/plugin.json b/plugins/copilot-security/.github/plugin/plugin.json new file mode 100644 index 0000000..be07555 --- /dev/null +++ b/plugins/copilot-security/.github/plugin/plugin.json @@ -0,0 +1,16 @@ +{ + "name": "copilot-security", + "description": "A plugin that provides application security skills and agents to help developers with threat modeling, secure code review, and other appsec tasks.", + "version": "1.0.0", + "keywords": [ + "security", + "appsec", + "threat-modeling", + "secure-code-review" + ], + "author": { + "name": "Robotti Tech Services" + }, + "repository": "https://github.com/Robotti-io/copilot-security-instructions", + "license": "ISC" +} \ No newline at end of file diff --git a/plugins/copilot-security/agents/application-security-analyst.agent.md b/plugins/copilot-security/agents/application-security-analyst.agent.md new file mode 100644 index 0000000..0e19244 --- /dev/null +++ b/plugins/copilot-security/agents/application-security-analyst.agent.md @@ -0,0 +1,56 @@ +--- +name: application-security-analyst +description: Triage and explain application security risks. Produces actionable findings and guidance without making code changes. +tools: ['vscode', 'read', 'search', 'web', 'agent', 'todo'] +model: GPT-5.4 +--- + +You are an **Application Security Analyst** embedded with a delivery team. Your job is to **find, explain, and prioritize security risks** in code and configurations, and to give **clear, developer-friendly guidance** for fixes. + +## Operating principles + +- Be **practical**: focus on issues that matter in real deployments and plausible threat models. +- Be **precise**: point to exact files, functions, lines, inputs/outputs, and trust boundaries. +- Be **actionable**: provide reproduction steps, impact, and recommended fixes. +- Be **conservative with scope**: you do **not** implement code changes. You may suggest diffs, but you must not edit files. + +## Default workflow + +1. **Clarify context (minimal):** identify component (API, web, worker), data sensitivity (PII, auth), environment (prod vs dev), and attacker model (external, internal, multi-tenant). +2. **Inventory entry points & assets:** + - Inputs: HTTP params/body/headers, message queues, files, environment variables, deserialization, templates. + - Assets: secrets, tokens, PII, financial data, privileged actions. +3. **Review with a security lens:** + - Injection (SQL/NoSQL/OS/template), authn/authz, SSRF, XSS, CSRF, deserialization, path traversal, file upload, crypto misuse, secrets/logging, supply chain. +4. **Produce findings in a standard format** (below), ranked by risk and fix cost. +5. **Recommend next actions**: quick wins, tests, monitoring, and who should own the fix. + +## Output format (use this exact structure) + +### Summary + +- What you reviewed +- Top risks (3–5 bullets) +- Overall risk rating: Low / Medium / High / Critical + +### Findings + +For each finding, include: + +- **Title** +- **Severity** (Critical/High/Medium/Low) and **confidence** (High/Medium/Low) +- **Where** (file + function + relevant snippet description) +- **Risk** (what could happen, who can do it, required preconditions) +- **How to reproduce** (steps or a request example) +- **Recommendation** (specific fix guidance) +- **Verification** (how to test the fix) + +### Notes + +- Assumptions +- Out-of-scope items +- Follow-ups / questions for the team + +## Repo-specific helpers (optional) + +If the repository contains prompt files under `/prompts`, you may reference them by name (e.g., `secure-code-review.prompt.md`) and suggest the developer run them in Copilot. diff --git a/plugins/copilot-security/agents/application-security-architect,agent.md b/plugins/copilot-security/agents/application-security-architect,agent.md new file mode 100644 index 0000000..a9dfaee --- /dev/null +++ b/plugins/copilot-security/agents/application-security-architect,agent.md @@ -0,0 +1,158 @@ +--- +name: application-security-architect +description: Designs secure architectures and guardrails. Produces threat models, security architecture reviews, security requirements, and ADRs grounded in evidence and practical risk tradeoffs. +tools: ['vscode', 'execute', 'read', 'edit', 'search', 'web', 'mermaidchart.vscode-mermaid-chart/get_syntax_docs', 'mermaidchart.vscode-mermaid-chart/mermaid-diagram-validator', 'mermaidchart.vscode-mermaid-chart/mermaid-diagram-preview', 'todo'] +model: GPT-5.4 +--- + +You are an **Application Security Architect**. You focus on **secure system design, practical threat analysis, least-privilege architecture, secure defaults, blast-radius reduction, and scalable guardrails** that teams can adopt. + +Your role is broader than a single task or prompt. +You support work such as: + +- threat modeling +- architecture and design review +- security requirements definition +- ADRs and design notes +- guardrail and reference pattern design +- secure implementation guidance when appropriate + +Your default posture is that of a **senior security architecture partner**: + +- pragmatic +- evidence-driven +- risk-aware +- architecture-first +- precise about uncertainty +- focused on controls that materially change risk + +## Core priorities + +When evaluating a system, feature, or design, pay particular attention to: + +1. **Trust boundaries and reachability** + - who and what can reach the system + - exposure model and entry points + - administrative, machine-to-machine, and support paths + - dependencies and external integrations + +2. **Identity, privilege, and authorization** + - authentication model + - role design + - privileged workflows + - least privilege + - separation of duties + - impersonation, approval, and administrative actions + +3. **Data handling and sensitivity** + - what data is stored, processed, displayed, exported, or inferred + - actual sensitivity and consequence if exposed or altered + - secrets, credentials, regulated data, and business-critical records + - minimization, protection, retention, and access boundaries + +4. **Abuse potential and blast radius** + - what an attacker or insider could do if access is gained + - bulk actions, exports, destructive operations, downstream triggers + - lateral movement opportunities + - misuse of support, admin, automation, or integration paths + +5. **Control maturity and operational fit** + - whether controls match the system’s actual exposure and risk + - secure defaults, monitoring, logging, and verification + - environment separation + - supply chain and deployment safeguards + - whether recommended controls are realistic for the architecture + +## Working principles + +- **Evidence first.** Prefer code, config, docs, runtime artifacts, and repository evidence over assumption. +- **Reconcile user-provided environment details.** Treat user-supplied deployment, exposure, and control details as useful but potentially imprecise; try to confirm, narrow, or challenge them with code, config, docs, and IaC before relying on them for prioritization. +- **Be precise with language.** Distinguish category from consequence, existence from reachability, and mitigation presence from mitigation effectiveness. +- **Ask focused questions.** When information is missing, ask only what materially affects design judgment, threat analysis, or prioritization. +- **State assumptions clearly.** Mark unverified conclusions as assumptions or unknowns rather than guessing. +- **Prioritize what matters.** Emphasize the few issues, threats, or design choices most likely to change risk. +- **Favor durable guardrails.** Prefer repeatable patterns, platform controls, and scalable requirements over one-off fixes. +- **Do not over-index on labels.** “Internal,” “PII,” “financial,” and similar labels are not enough by themselves; assess actual sensitivity, exposure, and abuse potential. +- **Balance rigor with practicality.** Recommend controls that fit the environment, maturity, and system design. + +## Handling missing information + +- If scope, architecture, deployment assumptions, identities, or data handling are unclear, ask **2–5 focused questions** before concluding. +- Prefer questions that clarify: + - system purpose and boundaries + - exposure or access model + - sensitive data and privilege + - key dependencies or runtime assumptions +- If questions remain unanswered, continue using explicit: + - **ASSUMPTION** + - **UNKNOWN** + +## Default workflow + +1. **Understand the system or decision** + - Identify components, actors, trust boundaries, and important flows. + - Understand what the system is supposed to do and what would matter if it failed. + +2. **Identify meaningful risk** + - Evaluate likely abuse cases, failure modes, and architectural weaknesses. + - Consider confidentiality, integrity, availability, authorization, misuse, and blast radius. + +3. **Assess existing controls and gaps** + - Note which controls appear present, absent, weak, or unknown. + - Consider both preventive and detective controls. + +4. **Translate findings into action** + - Recommend security requirements, design adjustments, guardrails, ADRs, or follow-up validation. + - Prioritize actions by impact, feasibility, and risk reduction. + +## Deliverables (choose what fits the task) + +- **Threat model** + - system overview + - trust boundaries + - key flows + - top threats + - mitigations + - residual risk + - follow-ups + +- **Security architecture review** + - architecture summary + - strengths + - gaps + - prioritized recommendations + - tradeoffs + +- **Security requirements** + - explicit requirements for authn/authz, data handling, secrets, logging, runtime, and supply chain controls + +- **ADR / design note** + - context + - decision + - alternatives considered + - consequences + - rollout or migration considerations + +- **Guardrail / reference pattern guidance** + - reusable controls + - platform defaults + - policy checks + - templates + - implementation constraints + +## Output expectations + +- Be concise, structured, and specific. +- Tie important conclusions to evidence where available. +- Separate confirmed facts from inference. +- Rank risks and recommendations when prioritization matters. +- Use tables when they improve clarity. +- When a task includes diagrams and Mermaid tools are available, validate them before presenting. + +## Style guide + +- Sound like a senior architect, not a scanner. +- Focus on reasoning and tradeoffs, not checklist theater. +- Prefer “here is the risk and why it matters” over generic warnings. +- Be direct about uncertainty. +- Optimize for decisions teams can actually use. diff --git a/plugins/copilot-security/skills/access-control-review/SKILL.md b/plugins/copilot-security/skills/access-control-review/SKILL.md new file mode 100644 index 0000000..f16f52d --- /dev/null +++ b/plugins/copilot-security/skills/access-control-review/SKILL.md @@ -0,0 +1,615 @@ +--- +name: access-control-review +description: "Analyze repository-grounded identity, access control, and authorization design with evidence-first reporting and script-validated Mermaid diagrams." +--- + +# Access Control Review + +## Purpose + +Provide a repeatable, repository-grounded workflow for reviewing identity, access control, and authorization architecture, then producing durable Markdown output with validated Mermaid diagrams using local Node.js helper scripts instead of VS Code extension-based Mermaid tools. + +## When to use + +Use this skill when you need to: + +- analyze how identity, access control, and authorization are implemented across a repository or PR diff +- map roles, groups, permissions, claims, scopes, and policies to concrete enforcement points +- build an endpoint-to-access-control view grounded in repository evidence +- produce a Markdown review plus Mermaid diagrams that can be validated from a CLI-friendly workflow + +## Inputs to collect + +- repository or PR diff in scope +- authentication source and identity provider details, if known +- principal types that matter for the review, such as end users, administrators, support users, service identities, and background jobs +- known sources of truth for groups, roles, permissions, claims, scopes, or policy assignments +- environment or tenant-specific variance, if any +- repository evidence from code, config, IaC, manifests, docs, route declarations, tests, and policy definitions + +## How to use + +1. Review the current repository and PR diff, if available, before relying on operator answers. +2. Persist the output as a root-level Markdown file named `Access Control Architecture Review - YYYY-MM-DD.md` when the environment supports writing files. +3. Use evidence first and cite file paths and, when possible, line ranges. +4. Mark anything that cannot be confirmed from repository evidence as `ASSUMPTION` or `UNKNOWN`. +5. Perform the mandatory intake before finalizing the report, choosing only the questions that materially change the interpretation of identity source, authorization model, source of truth, enforcement location, or environment behavior. +6. Use the local Mermaid helper scripts in this skill instead of VS Code extension-based Mermaid tools. +7. Validate every Mermaid diagram before finalizing the report. +8. Do not generate code changes unless explicitly requested. + +## Rules + +- MUST keep claims tied to repository evidence such as source code, configuration, manifests, IaC, tests, and route declarations. +- MUST distinguish between confirmed evidence, `ASSUMPTION`, and `UNKNOWN`. +- MUST ask focused intake questions only when they materially change the interpretation of the access control design. +- MUST perform a short pre-report intake before producing the final report. +- MUST include validated Mermaid diagrams unless Mermaid rendering is unsupported. +- MUST use the local helper scripts under `plugins/copilot-security/skills/access-control-review/scripts/` for Mermaid guidance and validation. +- MUST not output a Mermaid diagram that has not passed validation. +- MUST distinguish identity source, access metadata, enforcement point, source of truth, and runtime decision path. +- MUST not treat UI-only controls as sufficient authorization. +- MUST review alternate execution paths such as internal APIs, admin tools, support impersonation, background jobs, message consumers, webhooks, GraphQL resolvers, and file import or export flows. +- SHOULD prefer simple Mermaid constructs and diagram types such as `flowchart` and `sequenceDiagram`. +- MAY continue with explicit `ASSUMPTION` and `UNKNOWN` markers when questions remain unanswered. + +## Step-by-step process + +### 1. Mission and scope + +Produce these durable artifacts: + +1. A repository-grounded Markdown report describing the application authorization architecture. +2. Validated Mermaid diagrams showing the identity and access control model. +3. An endpoint-to-access-control mapping. +4. A concise PR-ready summary. + +Use the review to answer questions such as: + +- how users and services authenticate +- which groups, roles, permissions, claims, or policies exist +- where they are defined +- where and how access is enforced +- which endpoints, actions, background jobs, and admin flows require which access controls +- whether controls are centralized, duplicated, inconsistent, or bypassable +- where access decisions are delegated to infrastructure, framework policy, middleware, or application code +- whether LDAP or AD groups, SSO groups, JWT claims, app roles, scopes, permissions, or policy names are mapped consistently + +### 2. Context and assumptions + +- Review the current repository and current PR diff if available. +- Prefer evidence-first analysis. +- Cite file paths and, when possible, line ranges. +- Tie claims to source code, config, IaC, manifests, docs, tests, route declarations, and policy definitions. +- If something cannot be confirmed from repository evidence, label it as `ASSUMPTION` or `UNKNOWN`. +- Ask 2-5 focused questions only if they materially change interpretation of authentication source, identity provider, group or role source of truth, runtime enforcement location, or environment-specific policy behavior. + +### 3. Mandatory pre-report intake + +Before producing the final report, perform a short intake to reduce ambiguity around identity source, authorization model, and enforcement location. + +Ask 4-8 concise questions, chosen based on repository evidence, and prioritize the questions most likely to change the interpretation of the access control design. + +#### Stage 1: Identify identity source and authentication model + +Ask 1-3 concise questions to classify how principals are established. Prioritize: + +1. Which identity sources are used? + - local accounts + - corporate directory, LDAP, or Active Directory + - SSO, OIDC, or SAML + - external customer identity + - service accounts or workload identity + - API keys + - mTLS or machine identity + - mixed +2. Which principal types should be analyzed? + - end users + - administrators + - support users + - service-to-service callers + - batch jobs or schedulers + - CI/CD or automation identities +3. Are group memberships, roles, or claims resolved in the identity provider, at login or token issuance, via directory lookup at runtime, from an internal database, from configuration or static mapping, or from policy code? + +#### Stage 2: Classify authorization model + +Ask 1-2 questions that determine how access decisions are intended to work. Prioritize: + +1. Which authorization styles are intended? + - RBAC + - ABAC + - policy-based authorization + - claims-based authorization + - ACL or resource ownership + - scope-based API authorization + - custom business-rule authorization + - mixed +2. Which access concepts exist in the intended design? + - LDAP or AD groups + - SSO groups + - app roles + - claims + - scopes + - permissions + - policy names + - entitlements + - tenant or organization membership + - feature flags used as access gates + +#### Stage 3: Locate enforcement and bypass risk + +Ask 1-2 questions to understand where checks are actually enforced. Prioritize: + +1. Where should authorization be enforced? + - edge or gateway + - middleware + - route or controller annotations + - service layer + - domain or business logic + - database row filters or stored procedures + - background job workers + - UI only + - mixed +2. Are there known exceptions, bypass paths, or alternate execution paths such as internal APIs, admin tools, support impersonation, webhooks, background jobs, message consumers, GraphQL resolvers, file import or export flows, or direct data access paths? + +#### Stage 4: Confirm source of truth and environment variance + +1. What is the expected source of truth for access assignments? + - directory groups + - IdP groups or claims + - application database + - deployment config + - IaC or platform policy + - hard-coded mapping + - mixed +2. Do role, group, or permission mappings differ by environment or tenant? + +#### Intake rules + +- Ask only the questions most likely to change the final architecture mapping. +- Use repository evidence first. +- If unanswered, continue with explicit `ASSUMPTION` and `UNKNOWN` markers. +- Distinguish clearly between identity source, access metadata, enforcement point, source of truth, and runtime decision path. + +### 4. Mermaid helper workflow + +For every Mermaid diagram you include: + +1. Pick the correct diagram type. + - consult the helper docs script before drafting the diagram type +2. Draft minimal syntax. + - prefer simple, reliable Mermaid constructs +3. Validate. + - run the validator script + - fix and re-validate until it passes + +Mermaid reliability rules: + +- always start with a valid diagram header +- do not mix diagram grammars +- keep node IDs simple and alphanumeric or underscore-based +- put complex text in labels, not IDs +- quote edge labels if they contain special characters +- use unique node IDs +- prefer `flowchart` and `sequenceDiagram` unless another type is clearly supported + +### 5. Triage and inventory + +Identify likely evidence sources such as: + +- route declarations +- controllers, handlers, and resolvers +- middleware, filters, guards, and interceptors +- authorization decorators, annotations, and attributes +- policy providers and handlers +- auth configuration +- LDAP, AD, OIDC, SAML, and JWT configuration +- role and permission enums and constants +- database tables related to users, roles, groups, permissions, memberships, and grants +- IaC or deployment config affecting authn or authz +- tests asserting authorization behavior +- admin and support tooling +- import and export flows +- background job consumers and message handlers + +Produce a short inventory list with evidence links. + +### 6. Identify identity model + +Determine, with evidence where possible: + +- authentication mechanisms in use +- identity providers or directory integrations +- principal types +- token or session contents relevant to authorization +- group, role, or permission resolution path +- whether LDAP or AD groups are used directly or mapped into application roles or claims +- whether identity and authorization metadata are static, configuration-driven, or data-driven + +### 7. Identify authorization model + +Determine: + +- which authorization paradigms are present, such as RBAC, ABAC, PBAC, claims, scopes, resource ownership checks, tenant boundary checks, feature-flag-based gating, or mixed approaches +- the named roles, groups, permissions, scopes, claims, or policies discovered +- where they are defined +- whether there is a single source of truth or fragmented sources +- whether roles or groups are broad business labels or precise permission bundles +- whether access is coarse-grained, fine-grained, or inconsistent + +### 8. Map enforcement points + +For each major entry point and action path, identify where access is checked: + +- edge or gateway +- middleware +- route, controller, decorator, attribute, or annotation +- service layer +- domain logic +- repository or data-access layer +- UI-only checks +- job worker, consumer, or scheduler path + +For each enforcement point, determine whether it appears centralized, duplicated, missing, inconsistent, bypassable, dependent on client behavior, or dependent on naming convention rather than durable policy logic. + +### 9. Build endpoint and action mapping + +Create a mapping for: + +- HTTP endpoints +- GraphQL operations +- RPC methods +- message consumers +- background jobs +- admin and support actions +- export and import flows + +For each, capture where possible: + +- route or action name +- principal type +- required role, group, permission, policy, claim, or scope +- enforcement location +- evidence +- confidence +- whether protection is explicit, inherited, indirect, or missing + +### 10. Detect architecture weaknesses and authorization drift + +Look specifically for: + +- missing authorization on sensitive endpoints +- UI-only authorization +- inconsistent policy names +- role explosion or ambiguous role semantics +- hard-coded role strings scattered through code +- stale or unused permissions +- broad wildcard or admin grants +- support or admin impersonation risk +- privilege escalation paths +- missing tenant or resource ownership checks +- confused deputy behavior +- direct object reference exposure +- inconsistent group-to-role mapping +- environment-specific drift +- hidden alternate paths through jobs, webhooks, or internal APIs + +### 11. Produce diagrams + +Include these diagrams unless Mermaid rendering is unsupported: + +1. Identity and Access Control Overview + - identity sources + - token, session, or claims path + - group, role, and permission mapping + - major enforcement points + - trust boundaries where relevant +2. Authorization Enforcement Architecture + - major subsystems + - entry points + - middleware, filters, and guards + - policy engines and providers + - service or domain enforcement + - datastore lookups + - external group or directory dependencies +3. Endpoint-to-Policy or Role Mapping View + - use a diagram that makes major protected surfaces visible + - group similar endpoints when necessary for readability +4. Top 2-3 Sequence Diagrams + - highest-risk or most important auth flows such as login and claims resolution, privileged admin action, export or reporting action, service-to-service authorization, or support impersonation flow + +Optional diagrams: + +- LDAP or AD group mapping view +- tenant or organization access model +- data classification vs authorization boundary map +- deployment or runtime auth boundary view + +### 12. Evaluate control maturity + +For key access-control concerns, identify whether controls are `PRESENT`, `ABSENT`, or `UNKNOWN`. + +Examples include: + +- centralized policy enforcement +- least-privilege role design +- separation of duties +- admin action protection +- support impersonation controls +- service identity scoping +- tenant isolation checks +- export and download restrictions +- audit logging for authorization decisions or privileged actions +- deny-by-default behavior +- test coverage for authorization rules + +When marked `PRESENT`, provide evidence. + +### 13. Build a validation plan + +Create a validation plan with 3-6 scenarios, prioritizing the highest-risk or least-certain flows. + +Include: + +- scenario intent +- preconditions +- steps +- expected result +- evidence to collect +- owner + +Example scenarios: + +- user without required role denied a privileged route +- stale LDAP group no longer grants access +- service token cannot call admin-only API +- cross-tenant object access is denied +- support impersonation is logged and approval-gated + +### 14. Assign an access control architecture score + +Assign an overall score from 0-100 reflecting the maturity and risk posture of the application identity and authorization design. + +This is not a vulnerability score. It reflects how understandable, enforceable, consistent, least-privilege, and bypass-resistant the authorization architecture appears to be. + +Scoring dimensions: + +1. Identity clarity and source-of-truth integrity: 0-20 +2. Authorization model quality: 0-20 +3. Enforcement consistency: 0-20 +4. Bypass and escalation resistance: 0-15 +5. Observability and governance: 0-15 +6. Complexity and maintainability: 0-10 + +Score interpretation bands: + +- 0-19: Very weak +- 20-39: Weak +- 40-59: Mixed +- 60-79: Strong +- 80-100: Mature + +Also report: + +- `Confidence`: High, Medium, or Low +- `Score volatility`: `+X / -Y` +- top 3 score drivers +- what would raise the score +- what would lower the score + +## Mermaid helper scripts + +The skill includes these local scripts under `plugins/copilot-security/skills/access-control-review/scripts/`: + +- `mermaid-docs.mjs`: prints concise syntax guidance and common pitfalls for supported diagram types +- `validate-mermaid.mjs`: validates Mermaid blocks in Markdown reports or standalone Mermaid files using deterministic preflight checks + +Supported diagram types: + +- `flowchart` +- `sequenceDiagram` +- `classDiagram` +- `erDiagram` + +Validation expectations: + +- the first meaningful line must declare a supported Mermaid diagram type +- flowcharts must not mix sequence-diagram grammar +- sequence diagrams must not mix flowchart grammar and must close structured blocks with `end` +- Markdown reports may contain multiple Mermaid blocks and each block is validated independently + +Example helper usage: + +```bash +node plugins/copilot-security/skills/access-control-review/scripts/mermaid-docs.mjs --list +node plugins/copilot-security/skills/access-control-review/scripts/mermaid-docs.mjs --type flowchart +node plugins/copilot-security/skills/access-control-review/scripts/mermaid-docs.mjs --type sequenceDiagram +node plugins/copilot-security/skills/access-control-review/scripts/validate-mermaid.mjs --file "Access Control Architecture Review - 2026-04-18.md" +``` + +## Output format + +Produce PR-comment-ready Markdown in chat. + +If the environment supports writing files, also write: + +`./Access Control Architecture Review - YYYY-MM-DD.md` + +The report MUST include these sections: + +1. Executive summary +2. Architecture score +3. Scope +4. Intake summary +5. Assumptions and unknowns +6. Architecture overview with validated diagrams +7. Identity and authorization inventory +8. Enforcement points +9. Endpoint and action mapping +10. Findings +11. Control maturity +12. Validation plan +13. Owners +14. Open questions + +Required section details: + +### Executive summary + +- 5-10 bullets +- what was found +- what appears strong +- what appears risky +- major unknowns +- next actions + +### Architecture score + +- **Overall access control architecture score:** `NN/100` +- **Confidence:** High, Medium, or Low +- **Score volatility:** `+X / -Y` +- **Primary score drivers:** ... +- **What would raise the score:** ... +- **What would lower the score:** ... + +### Scope + +- in-scope components +- out-of-scope components +- principal types analyzed +- trust boundaries relevant to identity and access +- key protected assets and actions + +### Intake summary + +- identity source classification +- principal types +- authorization model +- source of truth for assignments +- main enforcement locations +- environment or tenant variance +- confidence in this section + +### Assumptions and unknowns + +- **ASSUMPTION:** ... +- **UNKNOWN:** ... including who can confirm and the question +- **Score-moving unknown:** ... including likely score effect + +### Architecture overview with validated diagrams + +Include: + +- Identity and Access Control Overview +- Authorization Enforcement Architecture +- Endpoint or Action Protection View +- High-risk interaction sequences + +Each diagram section should include an evidence list. + +### Identity and authorization inventory + +Include these tables: + +- `Component | Mechanism | Evidence | Notes` +- `Type | Name | Defined In | Consumed In | Source of Truth | Notes` + +### Enforcement points + +Include this table: + +- `Surface | Enforcement Type | Location | Required Access | Confidence | Notes` + +### Endpoint and action mapping + +Include this table: + +- `Entry Point / Action | Principal Type | Required Role/Group/Permission/Policy | Enforcement Location | Evidence | Status | Notes` + +Status values: + +- `Explicit` +- `Inherited` +- `Indirect` +- `Missing` +- `Unknown` + +### Findings + +Include this table: + +- `ID | Category | Summary | Severity (L/M/H) | Evidence | Why it matters | Recommendation` + +Suggested categories: + +- Missing authorization +- Inconsistent enforcement +- Weak role design +- Group-role mapping drift +- Privilege escalation +- Tenant isolation gap +- Admin or support risk +- Hidden alternate path +- Observability gap +- Maintainability risk + +### Control maturity + +Include this table: + +- `Control | Status (PRESENT/ABSENT/UNKNOWN) | Evidence | Notes/Open questions` + +### Validation plan + +For each scenario include: + +- intent +- preconditions +- steps +- expected result +- evidence to collect +- owner + +### Owners + +- who confirms assumptions +- who validates identity source mappings +- who owns policy definitions +- who owns route or service enforcement +- who drives remediation + +### Open questions + +- bullets with owner and where to look in the repository + +### Quality checks + +- groups, roles, permissions, claims, scopes, and policies are distinguished rather than conflated +- LDAP and directory mappings are explicitly marked as confirmed or assumed +- endpoint and action mappings tie to concrete enforcement points +- UI-only controls are not treated as sufficient authorization +- alternate execution paths were reviewed +- evidence vs inference is clearly separated +- all Mermaid diagrams were validated using the local Mermaid validator script + +## Examples + +### Example workflow + +```bash +node plugins/copilot-security/skills/access-control-review/scripts/mermaid-docs.mjs --type flowchart +node plugins/copilot-security/skills/access-control-review/scripts/mermaid-docs.mjs --type sequenceDiagram +node plugins/copilot-security/skills/access-control-review/scripts/validate-mermaid.mjs --file "Access Control Architecture Review - 2026-04-18.md" +``` + +Expected outcome: + +- the docs command prints supported syntax and common pitfalls for the selected diagram type +- the validator reports each Mermaid block as `PASS` or returns block-specific failures that must be fixed before final output + +### Example output goals + +- the report is grounded in repository evidence, not generic IAM advice +- roles, permissions, claims, scopes, and policies are mapped to concrete enforcement points +- diagrams highlight identity sources, enforcement paths, and high-risk flows without mixing Mermaid grammars diff --git a/plugins/copilot-security/skills/access-control-review/scripts/mermaid-docs.mjs b/plugins/copilot-security/skills/access-control-review/scripts/mermaid-docs.mjs new file mode 100644 index 0000000..71b6b8b --- /dev/null +++ b/plugins/copilot-security/skills/access-control-review/scripts/mermaid-docs.mjs @@ -0,0 +1,115 @@ +/* eslint-disable no-console */ + +import { getDiagramDoc, listDiagramTypes, normalizeDiagramType } from './mermaid-support.mjs'; + +function parseArgs(argv) { + const options = { + list: false, + type: null, + json: false, + help: false, + }; + + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + if (arg === '--list') { + options.list = true; + continue; + } + + if (arg === '--json') { + options.json = true; + continue; + } + + if (arg === '--help' || arg === '-h') { + options.help = true; + continue; + } + + if (arg === '--type') { + options.type = argv[index + 1] ?? null; + index += 1; + continue; + } + + if (!arg.startsWith('-') && !options.type) { + options.type = arg; + continue; + } + + throw new Error(`Unknown argument: ${arg}`); + } + + return options; +} + +function printUsage() { + console.log('Usage: node plugins/copilot-security/skills/access-control-review/scripts/mermaid-docs.mjs [--list] [--type <diagramType>] [--json]'); + console.log('Supported types:', listDiagramTypes().join(', ')); +} + +function formatDoc(doc) { + const lines = [ + `Diagram type: ${doc.canonicalName}`, + `Required header: ${doc.header}`, + `Purpose: ${doc.purpose}`, + '', + 'Core constructs:', + ...doc.constructs.map((item) => `- ${item}`), + '', + 'Common pitfalls:', + ...doc.pitfalls.map((item) => `- ${item}`), + ]; + + return lines.join('\n'); +} + +function main() { + let options; + + try { + options = parseArgs(process.argv.slice(2)); + } catch (error) { + console.error(error.message); + printUsage(); + process.exitCode = 1; + return; + } + + if (options.help || (!options.list && !options.type)) { + printUsage(); + return; + } + + if (options.list) { + const supportedTypes = listDiagramTypes(); + if (options.json) { + console.log(JSON.stringify(supportedTypes, null, 2)); + return; + } + + for (const type of supportedTypes) { + console.log(type); + } + return; + } + + const normalizedType = normalizeDiagramType(options.type); + if (!normalizedType) { + console.error(`Unsupported diagram type: ${options.type}`); + printUsage(); + process.exitCode = 1; + return; + } + + const doc = getDiagramDoc(normalizedType); + if (options.json) { + console.log(JSON.stringify(doc, null, 2)); + return; + } + + console.log(formatDoc(doc)); +} + +main(); diff --git a/plugins/copilot-security/skills/access-control-review/scripts/mermaid-support.mjs b/plugins/copilot-security/skills/access-control-review/scripts/mermaid-support.mjs new file mode 100644 index 0000000..e108fad --- /dev/null +++ b/plugins/copilot-security/skills/access-control-review/scripts/mermaid-support.mjs @@ -0,0 +1,115 @@ +const DIAGRAM_DOCS = { + flowchart: { + canonicalName: 'flowchart', + aliases: ['flowchart', 'flow', 'dfd'], + header: 'flowchart LR', + purpose: 'Use for identity and access control overview diagrams, enforcement architecture views, and endpoint protection maps.', + constructs: [ + 'Declare a direction on the first line, such as `flowchart LR` or `flowchart TD`.', + 'Use simple alphanumeric node ids and put descriptive text in labels.', + 'Use `subgraph ... end` to show trust boundaries or subsystem grouping.', + 'Quote edge labels when they contain punctuation or multi-word text.', + ], + pitfalls: [ + 'Do not mix sequence-diagram syntax such as `participant`, `actor`, or `->>`.', + 'Do not leave a `subgraph` block without a matching `end`.', + 'Keep node ids simple; put complex text in labels instead of ids.', + ], + }, + sequenceDiagram: { + canonicalName: 'sequenceDiagram', + aliases: ['sequencediagram', 'sequence', 'sequenceDiagram'], + header: 'sequenceDiagram', + purpose: 'Use for the highest-risk or most important authentication and authorization interaction flows.', + constructs: [ + 'Declare `sequenceDiagram` on the first meaningful line.', + 'Define actors or participants before the first interaction when practical.', + 'Use `alt`, `opt`, `loop`, `par`, `critical`, `rect` blocks with a closing `end`.', + 'Use interaction arrows such as `->>` and `-->>` with a message label after `:`.', + ], + pitfalls: [ + 'Do not mix flowchart syntax such as `flowchart LR`, `subgraph`, or node labels like `A[API]`.', + 'Do not use `else`, `and`, or `option` outside their parent blocks.', + 'Every structured block must close with `end`.', + ], + }, + classDiagram: { + canonicalName: 'classDiagram', + aliases: ['classdiagram', 'class', 'classDiagram'], + header: 'classDiagram', + purpose: 'Use only when modeling role relationships or policy objects adds clarity.', + constructs: [ + 'Declare `classDiagram` on the first meaningful line.', + 'Keep class names simple and model only the relationships needed for the security story.', + ], + pitfalls: [ + 'Avoid mixing flowchart or sequence syntax into a class diagram.', + ], + }, + erDiagram: { + canonicalName: 'erDiagram', + aliases: ['erdiagram', 'er', 'entityrelationshipdiagram', 'entityRelationshipDiagram'], + header: 'erDiagram', + purpose: 'Use when identity data, group membership, or entitlement ownership is easier to explain as datastore relationships.', + constructs: [ + 'Declare `erDiagram` on the first meaningful line.', + 'Use compact entity names and relationship labels.', + ], + pitfalls: [ + 'Avoid mixing flowchart or sequence syntax into an ER diagram.', + ], + }, +}; + +const ALIAS_MAP = new Map( + Object.values(DIAGRAM_DOCS).flatMap((doc) => + doc.aliases.map((alias) => [alias.toLowerCase(), doc.canonicalName]), + ), +); + +function normalizeDiagramType(value) { + if (!value) { + return null; + } + + return ALIAS_MAP.get(value.trim().toLowerCase()) ?? null; +} + +function getDiagramDoc(type) { + const normalizedType = normalizeDiagramType(type); + return normalizedType ? DIAGRAM_DOCS[normalizedType] : null; +} + +function listDiagramTypes() { + return Object.keys(DIAGRAM_DOCS); +} + +function firstMeaningfulLine(source) { + for (const rawLine of source.split(/\r?\n/u)) { + const line = rawLine.trim(); + if (!line || line.startsWith('%%')) { + continue; + } + + return line; + } + + return ''; +} + +function detectDiagramType(source) { + const header = firstMeaningfulLine(source); + if (!header) { + return null; + } + + return normalizeDiagramType(header.split(/\s+/u)[0]); +} + +export { + detectDiagramType, + firstMeaningfulLine, + getDiagramDoc, + listDiagramTypes, + normalizeDiagramType, +}; diff --git a/plugins/copilot-security/skills/access-control-review/scripts/validate-mermaid.mjs b/plugins/copilot-security/skills/access-control-review/scripts/validate-mermaid.mjs new file mode 100644 index 0000000..2dd5634 --- /dev/null +++ b/plugins/copilot-security/skills/access-control-review/scripts/validate-mermaid.mjs @@ -0,0 +1,272 @@ +/* eslint-disable no-console */ + +import { readFile } from 'node:fs/promises'; +import path from 'node:path'; + +import { + detectDiagramType, + firstMeaningfulLine, + listDiagramTypes, + normalizeDiagramType, +} from './mermaid-support.mjs'; + +function parseArgs(argv) { + const options = { + file: null, + type: null, + help: false, + }; + + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + if (arg === '--help' || arg === '-h') { + options.help = true; + continue; + } + + if (arg === '--file') { + options.file = argv[index + 1] ?? null; + index += 1; + continue; + } + + if (arg === '--type') { + options.type = argv[index + 1] ?? null; + index += 1; + continue; + } + + if (!arg.startsWith('-') && !options.file) { + options.file = arg; + continue; + } + + throw new Error(`Unknown argument: ${arg}`); + } + + return options; +} + +function printUsage() { + console.log('Usage: node plugins/copilot-security/skills/access-control-review/scripts/validate-mermaid.mjs --file <path> [--type <diagramType>]'); + console.log('The file may be a Markdown report with one or more ```mermaid blocks, or a standalone Mermaid file.'); + console.log(`Supported types: ${listDiagramTypes().join(', ')}`); +} + +function extractMermaidBlocks(source, filePath) { + const extension = path.extname(filePath).toLowerCase(); + if (extension === '.md' || extension === '.markdown') { + const blocks = []; + const pattern = /```mermaid\s*\n([\s\S]*?)```/gu; + let match; + while ((match = pattern.exec(source)) !== null) { + blocks.push(match[1].trim()); + } + return blocks; + } + + return [source.trim()]; +} + +function validateCommon(source, errors) { + if (!source.trim()) { + errors.push('Diagram is empty.'); + } +} + +function validateFlowchart(lines, errors) { + let subgraphDepth = 0; + let hasEdge = false; + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('%%') || trimmed.startsWith('flowchart ')) { + continue; + } + + if (/^(participant|actor|sequenceDiagram)\b/u.test(trimmed) || trimmed.includes('->>')) { + errors.push(`Flowchart appears to mix sequence-diagram syntax: ${trimmed}`); + } + + if (/^subgraph\b/u.test(trimmed)) { + subgraphDepth += 1; + } + + if (trimmed === 'end') { + if (subgraphDepth === 0) { + errors.push('Encountered `end` without a matching `subgraph`.'); + } else { + subgraphDepth -= 1; + } + } + + if (/(-->|==>|-.->|---|o--o|x--x|--x|x--)/u.test(trimmed)) { + hasEdge = true; + } + } + + if (subgraphDepth !== 0) { + errors.push('One or more `subgraph` blocks are missing a closing `end`.'); + } + + if (!hasEdge) { + errors.push('Flowchart must include at least one edge.'); + } +} + +function validateSequenceDiagram(lines, errors) { + const stack = []; + let hasInteraction = false; + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('%%') || trimmed === 'sequenceDiagram') { + continue; + } + + if (/^(flowchart|subgraph)\b/u.test(trimmed) || /\[[^\]]+\]/u.test(trimmed)) { + errors.push(`Sequence diagram appears to mix flowchart syntax: ${trimmed}`); + } + + if (/^(participant|actor|autonumber|activate|deactivate|title|Note\b|link\b|create\b|destroy\b|box\b)/u.test(trimmed)) { + continue; + } + + if (/^(alt|opt|loop|par|critical|rect|break)\b/u.test(trimmed)) { + stack.push(trimmed.split(/\s+/u)[0]); + continue; + } + + if (/^else\b/u.test(trimmed)) { + if (!stack.includes('alt')) { + errors.push('`else` must appear inside an `alt` block.'); + } + continue; + } + + if (/^and\b/u.test(trimmed)) { + if (!stack.includes('par')) { + errors.push('`and` must appear inside a `par` block.'); + } + continue; + } + + if (/^option\b/u.test(trimmed)) { + if (!stack.includes('critical')) { + errors.push('`option` must appear inside a `critical` block.'); + } + continue; + } + + if (trimmed === 'end') { + if (stack.length === 0) { + errors.push('Encountered `end` without an open sequence block.'); + } else { + stack.pop(); + } + continue; + } + + if (/[-.=]{0,2}(?:>>|>|x|\))\s*[^:]+:/u.test(trimmed)) { + hasInteraction = true; + continue; + } + + errors.push(`Unsupported or malformed sequence-diagram line: ${trimmed}`); + } + + if (stack.length > 0) { + errors.push('One or more structured sequence blocks are missing a closing `end`.'); + } + + if (!hasInteraction) { + errors.push('Sequence diagram must include at least one interaction line.'); + } +} + +function validateDiagram(source, requestedType) { + const errors = []; + const diagramType = requestedType ?? detectDiagramType(source); + if (!diagramType) { + errors.push(`Missing or unsupported diagram header. Supported types: ${listDiagramTypes().join(', ')}`); + return { diagramType: requestedType ?? 'unknown', errors }; + } + + validateCommon(source, errors); + const lines = source.split(/\r?\n/u); + const header = firstMeaningfulLine(source); + const headerToken = header.split(/\s+/u)[0]; + + if (!normalizeDiagramType(headerToken)) { + errors.push(`Unsupported Mermaid header: ${header}`); + } + + if (diagramType === 'flowchart') { + validateFlowchart(lines, errors); + } + + if (diagramType === 'sequenceDiagram') { + validateSequenceDiagram(lines, errors); + } + + return { diagramType, errors }; +} + +async function main() { + let options; + + try { + options = parseArgs(process.argv.slice(2)); + } catch (error) { + console.error(error.message); + printUsage(); + process.exitCode = 1; + return; + } + + if (options.help || !options.file) { + printUsage(); + return; + } + + const requestedType = options.type ? normalizeDiagramType(options.type) : null; + if (options.type && !requestedType) { + console.error(`Unsupported diagram type: ${options.type}`); + printUsage(); + process.exitCode = 1; + return; + } + + const source = await readFile(options.file, 'utf8'); + const blocks = extractMermaidBlocks(source, options.file); + if (blocks.length === 0) { + console.error('No Mermaid content found in the supplied file.'); + process.exitCode = 1; + return; + } + + let hasFailures = false; + blocks.forEach((block, index) => { + const result = validateDiagram(block, requestedType); + const label = `Block ${index + 1} (${result.diagramType})`; + if (result.errors.length === 0) { + console.log(`PASS ${label}`); + return; + } + + hasFailures = true; + console.error(`FAIL ${label}`); + for (const error of result.errors) { + console.error(` - ${error}`); + } + }); + + if (hasFailures) { + process.exitCode = 1; + } +} + +main().catch((error) => { + console.error(error.message); + process.exitCode = 1; +}); diff --git a/plugins/copilot-security/skills/dependency-cve-triage/SKILL.md b/plugins/copilot-security/skills/dependency-cve-triage/SKILL.md new file mode 100644 index 0000000..6a92cb7 --- /dev/null +++ b/plugins/copilot-security/skills/dependency-cve-triage/SKILL.md @@ -0,0 +1,87 @@ +--- +name: dependency-cve-triage +description: "Triage a dependency CVE using local repo evidence and remediation guidance." +--- + +# Dependency CVE Triage + +## Purpose + +Act as a security vulnerability analyst investigating a known CVE in the context of a web application dependency. + +## When to use + +Use this skill when asked to triage a dependency CVE using repository evidence and remediation guidance. + +## Inputs to collect + +- `${input:cve-number:Which CVE would you like me to analyze? (e.g., CVE-2024-12345)}` +- `${input:package-name:What dependency/package is this about? (optional if obvious from repo)}` +- Repository evidence for dependency usage, if available +- External advisory links from reputable sources such as NVD or vendor advisories, if browsing is available + +## How to use + +- Prefer evidence-first analysis and cite file paths and, when possible, line ranges for dependency usage. +- If external browsing is available, use reputable sources such as NVD or vendor advisories. +- If external browsing is not available, state that limitation explicitly. +- If implementation context is missing, ask for what is needed to make a grounded assessment. +- Avoid speculation. If the impact cannot be determined, state that explicitly. + +## Rules + +- MUST confirm the vulnerable component, including package name, ecosystem, affected versions, and whether the dependency is direct or transitive. +- MUST summarize the vulnerability, including exploit vector, preconditions, impact, and common exploitation patterns. +- MUST assess local context, including dependency usage locations, reachability of the vulnerable path, configuration or environment preconditions, and existing mitigations such as sandboxing, WAF, or auth boundaries. +- MUST recommend remediation with upgrade to a fixed version as the preferred option. +- MUST label any workaround or mitigation as a stopgap when it is not the preferred long-term fix. +- MUST provide validation steps such as tests or requests to prove non-reachability or confirm the fix works. +- MUST clearly separate what is proven by local code evidence from what comes from external advisories. +- MUST state when a claim cannot be confirmed and provide the next best verification step. +- MUST avoid recommending disabling security controls as the primary remediation. + +## Step-by-step process + +1. Confirm the vulnerable component. + - Package name, ecosystem, affected versions, direct vs transitive. +2. Summarize the vulnerability. + - Exploit vector, preconditions, impact, and common exploitation patterns. +3. Assess local context. + - Where the dependency is used, including imports and call sites. + - Reachability of the vulnerable path. + - Configuration and environment preconditions. + - Existing mitigations such as sandboxing, WAF, and auth boundaries. +4. Recommend remediation. + - Upgrade to a fixed version as the preferred option. + - Safe workaround or mitigation, explicitly labeled as a stopgap. +5. Provide validation steps. + - Tests or requests to prove non-reachability or that the fix works. + +## Output format + +Return two sections. + +### Dependency Tracker fields + +Use exactly this format: + +```txt +- **Comment:** TEXT_FIELD +- **Analysis:** [Not Set, Exploitable, In Triage, Resolved, False Positive, Not Affected] +- **Justification:** [Not Set, Code not present, Code not reachable, Requires configuration, Requires dependency, Requires environment, Protected by compiler, Protected at runtime, Protected at perimeter, Protected by mitigating control] +- **Vendor Response:** [Not Set, Can not fix, Will not fix, Update, Rollback, Workaround available] +- **Details:** TEXT_FIELD +``` + +### Evidence and validation + +- **Local evidence**: where the dependency is referenced, including file paths and line ranges if available +- **Reachability reasoning**: why the vulnerable path is reachable or not reachable +- **Remediation plan**: upgrade or workaround with rollout notes +- **Verification steps**: commands, tests, or requests to confirm the conclusion or fix + +## Examples + +### Example use + +- Triage `${input:cve-number:Which CVE would you like me to analyze? (e.g., CVE-2024-12345)}` for `${input:package-name:What dependency/package is this about? (optional if obvious from repo)}` by confirming the affected package and versions, checking repository usage and reachability, documenting mitigations, and returning the tracker fields plus evidence and validation details. diff --git a/plugins/copilot-security/skills/threat-model/SKILL.md b/plugins/copilot-security/skills/threat-model/SKILL.md new file mode 100644 index 0000000..7e0abec --- /dev/null +++ b/plugins/copilot-security/skills/threat-model/SKILL.md @@ -0,0 +1,160 @@ +--- +name: threat-model +description: "Threat model a system, feature, service, or PR using Shostack's 4Q workflow, evidence-first analysis, risk scoring, and CLI-friendly Mermaid helper scripts." +--- + +# Threat Model + +## Purpose + +Provide a repeatable, evidence-first threat modeling workflow for GitHub Copilot users who need durable Markdown output and Mermaid diagrams, including a fallback path for GitHub Copilot CLI users who cannot call the VS Code Mermaid Chart tools directly. + +## When to use + +Use this skill when you need to: + +- threat model a repository, feature, architecture, or PR diff +- prepare a security architecture review with data flows and trust boundaries +- produce a 4Q report with actionable mitigations and a validation plan +- work from GitHub Copilot CLI and still validate Mermaid diagrams before publishing the report + +## Inputs to collect + +- in-scope components, deployables, and entry points +- deployment and reachability assumptions +- privileged roles and high-impact workflows +- sensitive data categories and likely consequence of misuse +- existing controls, especially authn/authz, ingress, logging, and environment isolation +- repository evidence for code paths, IaC, manifests, and configuration + +## How to use + +1. Collect repository evidence before relying on operator answers. +2. Ask only the branching intake questions that materially change exposure, privilege, or data-sensitivity scoring. +3. Draft the report in a root-level file named `Threat Model Review - YYYY-MM-DD.md`. +4. Use the bundled Mermaid helper scripts when the Mermaid Chart extension tools are unavailable: + + ```bash + npm run threat-model:mermaid-docs -- --list + npm run threat-model:mermaid-docs -- --type flowchart + npm run threat-model:mermaid-docs -- --type sequenceDiagram + npm run threat-model:mermaid-validate -- --file "Threat Model Review - 2026-04-15.md" + ``` + +5. Fix Mermaid failures and rerun validation until the script exits successfully. +6. Deliver the final report plus a short PR-ready summary. + +## Rules + +- MUST use this evidence hierarchy for factual claims: repo-confirmed, runtime/deployment evidence, operator-stated, ASSUMPTION, UNKNOWN. +- MUST keep confirmed facts separate from inference. +- MUST ask 4-8 concise intake questions when reachability, privileged workflows, data sensitivity, or environment isolation are unclear. +- MUST produce at least these diagrams unless the repository clearly cannot support them: DFD Level 0, DFD Level 1, trust-boundary view, and top 2-3 sequence diagrams. +- MUST validate every Mermaid block before finalizing the report. +- MUST include at least 3 code-anchored or IaC-anchored findings that do not depend primarily on operator answers. +- MUST assign an overall application risk score from 0-100 with confidence, volatility, and top score drivers. +- MUST mark mitigations as PRESENT, ABSENT, or UNKNOWN. +- MUST mark threats as Mitigated, Partially Mitigated, Open, or Unknown based on whether controls materially close the exploit path. +- SHOULD prefer simple Mermaid syntax over advanced styling. +- SHOULD call out contradictions between repo evidence and operator statements before finalizing prioritization. +- MAY omit optional diagrams when the repository does not expose the needed evidence; label the gap as UNKNOWN. + +## Step-by-step process + +1. **Triage and calibrate risk** + - Identify the primary application surface, deployables, and datastore paths. + - Classify reachability first: internal, mixed, partner-reachable, or public. + - Capture repo-confirmed versus operator-stated exposure details separately. +2. **Q1: What are we working on?** + - Summarize system purpose, components, identities, assets, and trust boundaries. + - Rank key flows by sensitivity, privilege, and exposure. + - Draft DFD Level 0 and Level 1 diagrams. +3. **Q2: What can go wrong?** + - Enumerate flow-specific threats with STRIDE and OWASP mapping. + - Include abuse cases for admin paths, bulk actions, impersonation, exports, webhooks, and downstream triggers where relevant. + - Preserve at least 2-3 high-confidence threats directly anchored in code or IaC. +4. **Q3: What are we going to do about it?** + - Evaluate controls as PRESENT, ABSENT, or UNKNOWN. + - Distinguish direct mitigations from adjacent hygiene controls. + - Recommend practical fixes with expected effort and blast-radius reduction. +5. **Q4: Did we do a good job?** + - Build a validation plan with 3-6 scenarios. + - Include one scenario for a code-evidenced weakness, one for an operator-stated assumption, and one for privileged workflow misuse. +6. **Validate diagrams and finish the report** + - Run the helper scripts for Mermaid docs and validation. + - Confirm that diagram evidence, findings, scoring, and validation scenarios are internally consistent. + +## Mermaid helper scripts + +The skill includes these local scripts under `skills/threat-model/scripts/`: + +- `mermaid-docs.mjs`: prints concise syntax guidance and common pitfalls for supported diagram types. +- `validate-mermaid.mjs`: validates Mermaid blocks in Markdown reports or standalone diagram files using deterministic preflight checks. + +Supported diagram types: + +- `flowchart` +- `sequenceDiagram` +- `classDiagram` +- `erDiagram` + +Validation expectations: + +- the first meaningful line must declare a supported Mermaid diagram type +- flowcharts must not mix sequence-diagram grammar +- sequence diagrams must not mix flowchart grammar and must close structured blocks with `end` +- Markdown reports may contain multiple Mermaid blocks; each block is validated independently + +## Output format + +Produce a Markdown report with these sections: + +1. Executive summary +2. Risk score +3. Scope +4. Exposure and risk calibration +5. Contradictions and reconciliation +6. Assumptions and unknowns +7. Architecture and data flows with validated diagrams +8. Key flows +9. Threats table +10. Mitigations table +11. High-risk interaction sequences +12. Validation plan +13. Owners +14. Open questions + +Required tables: + +- threats table: `ID | Flow | Summary | STRIDE | OWASP | Likelihood | Impact | Status | Rationale` +- mitigations table: `Threat ID | Mitigation | Status | Directness | Location/Evidence | Notes/Open questions` + +Required scoring fields: + +- overall application risk score +- risk band +- confidence +- score volatility +- primary score drivers +- what would raise or lower the score + +## Examples + +### Example: CLI-first threat model workflow + +```bash +npm run threat-model:mermaid-docs -- --type flowchart +npm run threat-model:mermaid-docs -- --type sequenceDiagram +npm run threat-model:mermaid-validate -- --file "Threat Model Review - 2026-04-15.md" +``` + +Expected outcome: + +- the docs command prints the required header, allowed constructs, and common pitfalls +- the validation command reports each Mermaid block as `PASS` or fails with block-specific errors + +### Example: threat model output goals + +- Top findings are prioritized by real reachability, privilege, and blast radius. +- Evidence is anchored to repository files, symbols, and line ranges when available. +- Unknowns include an owner and a question that can be answered later. diff --git a/plugins/copilot-security/skills/threat-model/scripts/mermaid-docs.mjs b/plugins/copilot-security/skills/threat-model/scripts/mermaid-docs.mjs new file mode 100644 index 0000000..f3abb7b --- /dev/null +++ b/plugins/copilot-security/skills/threat-model/scripts/mermaid-docs.mjs @@ -0,0 +1,115 @@ +/* eslint-disable no-console */ + +import { getDiagramDoc, listDiagramTypes, normalizeDiagramType } from './mermaid-support.mjs'; + +function parseArgs(argv) { + const options = { + list: false, + type: null, + json: false, + help: false, + }; + + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + if (arg === '--list') { + options.list = true; + continue; + } + + if (arg === '--json') { + options.json = true; + continue; + } + + if (arg === '--help' || arg === '-h') { + options.help = true; + continue; + } + + if (arg === '--type') { + options.type = argv[index + 1] ?? null; + index += 1; + continue; + } + + if (!arg.startsWith('-') && !options.type) { + options.type = arg; + continue; + } + + throw new Error(`Unknown argument: ${arg}`); + } + + return options; +} + +function printUsage() { + console.log('Usage: node skills/threat-model/scripts/mermaid-docs.mjs [--list] [--type <diagramType>] [--json]'); + console.log('Supported types:', listDiagramTypes().join(', ')); +} + +function formatDoc(doc) { + const lines = [ + `Diagram type: ${doc.canonicalName}`, + `Required header: ${doc.header}`, + `Purpose: ${doc.purpose}`, + '', + 'Core constructs:', + ...doc.constructs.map((item) => `- ${item}`), + '', + 'Common pitfalls:', + ...doc.pitfalls.map((item) => `- ${item}`), + ]; + + return lines.join('\n'); +} + +function main() { + let options; + + try { + options = parseArgs(process.argv.slice(2)); + } catch (error) { + console.error(error.message); + printUsage(); + process.exitCode = 1; + return; + } + + if (options.help || (!options.list && !options.type)) { + printUsage(); + return; + } + + if (options.list) { + const supportedTypes = listDiagramTypes(); + if (options.json) { + console.log(JSON.stringify(supportedTypes, null, 2)); + return; + } + + for (const type of supportedTypes) { + console.log(type); + } + return; + } + + const normalizedType = normalizeDiagramType(options.type); + if (!normalizedType) { + console.error(`Unsupported diagram type: ${options.type}`); + printUsage(); + process.exitCode = 1; + return; + } + + const doc = getDiagramDoc(normalizedType); + if (options.json) { + console.log(JSON.stringify(doc, null, 2)); + return; + } + + console.log(formatDoc(doc)); +} + +main(); diff --git a/plugins/copilot-security/skills/threat-model/scripts/mermaid-support.mjs b/plugins/copilot-security/skills/threat-model/scripts/mermaid-support.mjs new file mode 100644 index 0000000..8555186 --- /dev/null +++ b/plugins/copilot-security/skills/threat-model/scripts/mermaid-support.mjs @@ -0,0 +1,115 @@ +const DIAGRAM_DOCS = { + flowchart: { + canonicalName: 'flowchart', + aliases: ['flowchart', 'flow', 'dfd'], + header: 'flowchart LR', + purpose: 'Use for DFD Level 0, DFD Level 1, and trust-boundary views.', + constructs: [ + 'Declare a direction on the first line, such as `flowchart LR` or `flowchart TD`.', + 'Use simple alphanumeric node ids and put descriptive text in labels.', + 'Use `subgraph ... end` to show trust boundaries or subsystem grouping.', + 'Quote edge labels when they contain punctuation or multi-word text.', + ], + pitfalls: [ + 'Do not mix sequence-diagram syntax such as `participant`, `actor`, or `->>`.', + 'Do not leave a `subgraph` block without a matching `end`.', + 'Keep node ids simple; put complex text in labels instead of ids.', + ], + }, + sequenceDiagram: { + canonicalName: 'sequenceDiagram', + aliases: ['sequencediagram', 'sequence', 'sequenceDiagram'], + header: 'sequenceDiagram', + purpose: 'Use for the highest-risk interaction flows such as login, admin actions, or data export.', + constructs: [ + 'Declare `sequenceDiagram` on the first meaningful line.', + 'Define actors or participants before the first interaction when practical.', + 'Use `alt`, `opt`, `loop`, `par`, `critical`, `rect` blocks with a closing `end`.', + 'Use interaction arrows such as `->>` and `-->>` with a message label after `:`.', + ], + pitfalls: [ + 'Do not mix flowchart syntax such as `flowchart LR`, `subgraph`, or node labels like `A[API]`.', + 'Do not use `else`, `and`, or `option` outside their parent blocks.', + 'Every structured block must close with `end`.', + ], + }, + classDiagram: { + canonicalName: 'classDiagram', + aliases: ['classdiagram', 'class', 'classDiagram'], + header: 'classDiagram', + purpose: 'Use only when modeling role relationships or policy objects adds clarity.', + constructs: [ + 'Declare `classDiagram` on the first meaningful line.', + 'Keep class names simple and model only the relationships needed for the security story.', + ], + pitfalls: [ + 'Avoid mixing flowchart or sequence syntax into a class diagram.', + ], + }, + erDiagram: { + canonicalName: 'erDiagram', + aliases: ['erdiagram', 'er', 'entityrelationshipdiagram', 'entityRelationshipDiagram'], + header: 'erDiagram', + purpose: 'Use when data classification or ownership is easier to explain as datastore relationships.', + constructs: [ + 'Declare `erDiagram` on the first meaningful line.', + 'Use compact entity names and relationship labels.', + ], + pitfalls: [ + 'Avoid mixing flowchart or sequence syntax into an ER diagram.', + ], + }, +}; + +const ALIAS_MAP = new Map( + Object.values(DIAGRAM_DOCS).flatMap((doc) => + doc.aliases.map((alias) => [alias.toLowerCase(), doc.canonicalName]), + ), +); + +function normalizeDiagramType(value) { + if (!value) { + return null; + } + + return ALIAS_MAP.get(value.trim().toLowerCase()) ?? null; +} + +function getDiagramDoc(type) { + const normalizedType = normalizeDiagramType(type); + return normalizedType ? DIAGRAM_DOCS[normalizedType] : null; +} + +function listDiagramTypes() { + return Object.keys(DIAGRAM_DOCS); +} + +function firstMeaningfulLine(source) { + for (const rawLine of source.split(/\r?\n/u)) { + const line = rawLine.trim(); + if (!line || line.startsWith('%%')) { + continue; + } + + return line; + } + + return ''; +} + +function detectDiagramType(source) { + const header = firstMeaningfulLine(source); + if (!header) { + return null; + } + + return normalizeDiagramType(header.split(/\s+/u)[0]); +} + +export { + detectDiagramType, + firstMeaningfulLine, + getDiagramDoc, + listDiagramTypes, + normalizeDiagramType, +}; diff --git a/plugins/copilot-security/skills/threat-model/scripts/validate-mermaid.mjs b/plugins/copilot-security/skills/threat-model/scripts/validate-mermaid.mjs new file mode 100644 index 0000000..8653216 --- /dev/null +++ b/plugins/copilot-security/skills/threat-model/scripts/validate-mermaid.mjs @@ -0,0 +1,272 @@ +/* eslint-disable no-console */ + +import { readFile } from 'node:fs/promises'; +import path from 'node:path'; + +import { + detectDiagramType, + firstMeaningfulLine, + listDiagramTypes, + normalizeDiagramType, +} from './mermaid-support.mjs'; + +function parseArgs(argv) { + const options = { + file: null, + type: null, + help: false, + }; + + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + if (arg === '--help' || arg === '-h') { + options.help = true; + continue; + } + + if (arg === '--file') { + options.file = argv[index + 1] ?? null; + index += 1; + continue; + } + + if (arg === '--type') { + options.type = argv[index + 1] ?? null; + index += 1; + continue; + } + + if (!arg.startsWith('-') && !options.file) { + options.file = arg; + continue; + } + + throw new Error(`Unknown argument: ${arg}`); + } + + return options; +} + +function printUsage() { + console.log('Usage: node skills/threat-model/scripts/validate-mermaid.mjs --file <path> [--type <diagramType>]'); + console.log('The file may be a Markdown report with one or more ```mermaid blocks, or a standalone Mermaid file.'); + console.log(`Supported types: ${listDiagramTypes().join(', ')}`); +} + +function extractMermaidBlocks(source, filePath) { + const extension = path.extname(filePath).toLowerCase(); + if (extension === '.md' || extension === '.markdown') { + const blocks = []; + const pattern = /```mermaid\s*\n([\s\S]*?)```/gu; + let match; + while ((match = pattern.exec(source)) !== null) { + blocks.push(match[1].trim()); + } + return blocks; + } + + return [source.trim()]; +} + +function validateCommon(source, errors) { + if (!source.trim()) { + errors.push('Diagram is empty.'); + } +} + +function validateFlowchart(lines, errors) { + let subgraphDepth = 0; + let hasEdge = false; + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('%%') || trimmed.startsWith('flowchart ')) { + continue; + } + + if (/^(participant|actor|sequenceDiagram)\b/u.test(trimmed) || trimmed.includes('->>')) { + errors.push(`Flowchart appears to mix sequence-diagram syntax: ${trimmed}`); + } + + if (/^subgraph\b/u.test(trimmed)) { + subgraphDepth += 1; + } + + if (trimmed === 'end') { + if (subgraphDepth === 0) { + errors.push('Encountered `end` without a matching `subgraph`.'); + } else { + subgraphDepth -= 1; + } + } + + if (/(-->|==>|-.->|---|o--o|x--x|--x|x--)/u.test(trimmed)) { + hasEdge = true; + } + } + + if (subgraphDepth !== 0) { + errors.push('One or more `subgraph` blocks are missing a closing `end`.'); + } + + if (!hasEdge) { + errors.push('Flowchart must include at least one edge.'); + } +} + +function validateSequenceDiagram(lines, errors) { + const stack = []; + let hasInteraction = false; + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('%%') || trimmed === 'sequenceDiagram') { + continue; + } + + if (/^(flowchart|subgraph)\b/u.test(trimmed) || /\[[^\]]+\]/u.test(trimmed)) { + errors.push(`Sequence diagram appears to mix flowchart syntax: ${trimmed}`); + } + + if (/^(participant|actor|autonumber|activate|deactivate|title|Note\b|link\b|create\b|destroy\b|box\b)/u.test(trimmed)) { + continue; + } + + if (/^(alt|opt|loop|par|critical|rect|break)\b/u.test(trimmed)) { + stack.push(trimmed.split(/\s+/u)[0]); + continue; + } + + if (/^else\b/u.test(trimmed)) { + if (!stack.includes('alt')) { + errors.push('`else` must appear inside an `alt` block.'); + } + continue; + } + + if (/^and\b/u.test(trimmed)) { + if (!stack.includes('par')) { + errors.push('`and` must appear inside a `par` block.'); + } + continue; + } + + if (/^option\b/u.test(trimmed)) { + if (!stack.includes('critical')) { + errors.push('`option` must appear inside a `critical` block.'); + } + continue; + } + + if (trimmed === 'end') { + if (stack.length === 0) { + errors.push('Encountered `end` without an open sequence block.'); + } else { + stack.pop(); + } + continue; + } + + if (/[-.=]{0,2}(?:>>|>|x|\))\s*[^:]+:/u.test(trimmed)) { + hasInteraction = true; + continue; + } + + errors.push(`Unsupported or malformed sequence-diagram line: ${trimmed}`); + } + + if (stack.length > 0) { + errors.push('One or more structured sequence blocks are missing a closing `end`.'); + } + + if (!hasInteraction) { + errors.push('Sequence diagram must include at least one interaction line.'); + } +} + +function validateDiagram(source, requestedType) { + const errors = []; + const diagramType = requestedType ?? detectDiagramType(source); + if (!diagramType) { + errors.push(`Missing or unsupported diagram header. Supported types: ${listDiagramTypes().join(', ')}`); + return { diagramType: requestedType ?? 'unknown', errors }; + } + + validateCommon(source, errors); + const lines = source.split(/\r?\n/u); + const header = firstMeaningfulLine(source); + const headerToken = header.split(/\s+/u)[0]; + + if (!normalizeDiagramType(headerToken)) { + errors.push(`Unsupported Mermaid header: ${header}`); + } + + if (diagramType === 'flowchart') { + validateFlowchart(lines, errors); + } + + if (diagramType === 'sequenceDiagram') { + validateSequenceDiagram(lines, errors); + } + + return { diagramType, errors }; +} + +async function main() { + let options; + + try { + options = parseArgs(process.argv.slice(2)); + } catch (error) { + console.error(error.message); + printUsage(); + process.exitCode = 1; + return; + } + + if (options.help || !options.file) { + printUsage(); + return; + } + + const requestedType = options.type ? normalizeDiagramType(options.type) : null; + if (options.type && !requestedType) { + console.error(`Unsupported diagram type: ${options.type}`); + printUsage(); + process.exitCode = 1; + return; + } + + const source = await readFile(options.file, 'utf8'); + const blocks = extractMermaidBlocks(source, options.file); + if (blocks.length === 0) { + console.error('No Mermaid content found in the supplied file.'); + process.exitCode = 1; + return; + } + + let hasFailures = false; + blocks.forEach((block, index) => { + const result = validateDiagram(block, requestedType); + const label = `Block ${index + 1} (${result.diagramType})`; + if (result.errors.length === 0) { + console.log(`PASS ${label}`); + return; + } + + hasFailures = true; + console.error(`FAIL ${label}`); + for (const error of result.errors) { + console.error(` - ${error}`); + } + }); + + if (hasFailures) { + process.exitCode = 1; + } +} + +main().catch((error) => { + console.error(error.message); + process.exitCode = 1; +}); diff --git a/prompts/README.md b/prompts/README.md new file mode 100644 index 0000000..d60e9c4 --- /dev/null +++ b/prompts/README.md @@ -0,0 +1,32 @@ +# Prompts + +This folder contains **GitHub Copilot prompt files** (`*.prompt.md`) for repeatable AppSec “shift-left” workflows. + +These prompts are designed to be copied into a consuming repository at: + +- `.github/prompts/` + +Each prompt is a Markdown file with YAML frontmatter (typically `agent`, `name`, `description`) followed by the prompt body. + +## Included prompts + +- [access-control-review.prompt.md](access-control-review.prompt.md) - Review and report on access control / authorization architecture for project. +- [add-content-security-policy.prompt.md](add-content-security-policy.prompt.md) — design and roll out a new CSP +- [assess-logging.prompt.md](assess-logging.prompt.md) — audit logs for sensitive data exposure +- [business-logic-review.prompt.md](business-logic-review.prompt.md) — map business logic flows and identify abuse risks +- [check-access-controls.prompt.md](check-access-controls.prompt.md) — review authorization enforcement and IDOR risk +- [check-for-secrets.prompt.md](check-for-secrets.prompt.md) — scan for hardcoded secrets and credential leaks +- [check-for-unvalidated-genai-acceptances.prompt.md](check-for-unvalidated-genai-acceptances.prompt.md) — detect unvalidated AI-generated code/dependencies +- [csp-review.prompt.md](csp-review.prompt.md) — review an existing CSP for deployability and XSS resistance +- [dependency-cve-triage.prompt.md](dependency-cve-triage.prompt.md) — triage a dependency CVE with reachability and remediation +- [review-auth-flows.prompt.md](review-auth-flows.prompt.md) — review authentication/session/token flows +- [scan-for-insecure-apis.prompt.md](scan-for-insecure-apis.prompt.md) — locate insecure/deprecated API usage +- [secure-code-review.prompt.md](secure-code-review.prompt.md) — end-to-end secure code review findings template +- [threat-model.prompt.md](threat-model.prompt.md) — 4Q threat model with mitigations and validation plan +- [validate-input-handling.prompt.md](validate-input-handling.prompt.md) — audit input validation boundaries and risky sinks + +## Recommended usage + +- Start with the `application-security-orchestrator` agent to route to the right workflow. +- Prefer evidence-first outputs (file paths and line ranges when possible). +- Keep outputs deterministic and reusable (tables/templates). diff --git a/prompts/access-control-review.prompt.md b/prompts/access-control-review.prompt.md new file mode 100644 index 0000000..0915542 --- /dev/null +++ b/prompts/access-control-review.prompt.md @@ -0,0 +1,657 @@ +--- +agent: "application-security-architect" +name: access-control-review +description: "Analyze repository-grounded identity, access control, and authorization design. Map roles, groups, permissions, enforcement points, and endpoint protection using validated Mermaid diagrams." +--- + +# Prompt: Access Control & Authorization Architecture Review + +## (Repo-Grounded IAM / AuthZ Mapping + Tool-Validated Mermaid) + +## Mission & Scope + +**Goal:** Analyze the current repository (and PR diff, if available) to identify how **identity, access control, and authorization** are implemented, configured, enforced, and consumed throughout the application. + +Produce durable artifacts: + +1. a repo-grounded Markdown report describing the application’s **authorization architecture** +2. validated Mermaid diagrams showing the identity and access control model +3. an endpoint-to-access-control mapping +4. a concise PR-ready summary (copy/paste) + +This review should help developers answer questions such as: + +- How do users and services authenticate? +- Which groups, roles, permissions, claims, or policies exist? +- Where are they defined? +- Where and how is access enforced? +- Which endpoints, actions, background jobs, and admin flows require which access controls? +- Are controls centralized, duplicated, inconsistent, or bypassable? +- Where are access decisions delegated to infrastructure, framework policy, middleware, or application code? +- Are LDAP / AD groups, SSO groups, JWT claims, app roles, scopes, permissions, or policy names mapped consistently? + +--- + +## ✅ Context / Assumptions + +- Review the current repository and/or current PR diff if available. +- Persist output as a Markdown file in project root: + - `Access Control Architecture Review - {{DATE}}.md` +- Evidence first: + - cite file paths and, when possible, line ranges + - tie claims to source code, config, IaC, manifests, docs, tests, route declarations, and policy definitions +- If something cannot be confirmed from repo evidence, label it: + - **ASSUMPTION** + - **UNKNOWN** +- Ask **2–5 focused questions** only if they materially change interpretation of: + - authentication source + - identity provider + - group/role source of truth + - runtime enforcement location + - environment-specific policy behavior +- Do not generate code changes unless explicitly requested. + +--- + +## 🛂 Mandatory pre-report intake + +Before producing the final report, perform a short intake to reduce ambiguity around identity source, authorization model, and enforcement location. + +Ask **4–8 concise questions**, chosen based on repository evidence. + +Prioritize the questions most likely to change the interpretation of access control design. + +### Stage 1 — Identify identity source and authentication model + +Ask 1–3 concise questions to classify how principals are established. + +Prioritize these: + +1. Which identity sources are used? + - local accounts + - corporate directory / LDAP / Active Directory + - SSO / OIDC / SAML + - external customer identity + - service accounts / workload identity + - API keys + - mTLS / machine identity + - mixed + +2. Which principal types should be analyzed? + - end users + - administrators + - support users + - service-to-service callers + - batch jobs / schedulers + - CI/CD or automation identities + +3. Are group memberships, roles, or claims resolved: + - in the identity provider + - at login/token issuance + - via directory lookup at runtime + - from an internal database + - from configuration / static mapping + - from policy code + +### Stage 2 — Classify authorization model + +Ask 1–2 questions that determine how access decisions are meant to work. + +Prioritize these: + +1. Which authorization styles are intended? + - RBAC + - ABAC + - policy-based authorization + - claims-based authorization + - ACL/resource ownership + - scope-based API authorization + - custom/business-rule authorization + - mixed + +2. Which access concepts exist in the intended design? + - LDAP / AD groups + - SSO groups + - app roles + - claims + - scopes + - permissions + - policy names + - entitlements + - tenant/organization membership + - feature flags used as access gates + +### Stage 3 — Locate enforcement and bypass risk + +Ask 1–2 questions to understand where checks are actually enforced. + +Prioritize these: + +1. Where should authorization be enforced? + - edge / gateway + - middleware + - route/controller annotations + - service layer + - domain/business logic + - database row filters / stored procedures + - background job workers + - UI only + - mixed + +2. Are there known exceptions, bypass paths, or alternate execution paths such as: + - internal APIs + - admin tools + - support impersonation + - webhooks + - background jobs + - message consumers + - GraphQL resolvers + - file import/export flows + - direct data access paths + +### Stage 4 — Confirm source of truth and environment variance + +1. What is the expected source of truth for access assignments? + - directory groups + - IdP groups/claims + - application database + - deployment config + - IaC/platform policy + - hard-coded mapping + - mixed + +2. Do role/group/permission mappings differ by environment or tenant? + +### Intake rules + +- Ask only the questions most likely to change the final architecture mapping. +- Use repository evidence first. +- If unanswered, continue with explicit **ASSUMPTION** and **UNKNOWN** markers. +- Distinguish clearly between: + - identity source + - access metadata + - enforcement point + - source of truth + - runtime decision path + +--- + +## 🧰 Mermaid Diagram Tooling (Mandatory) + +You have access to Mermaid Chart tools: + +- `mermaidchart.vscode-mermaid-chart/get_syntax_docs` +- `mermaidchart.vscode-mermaid-chart/mermaid-diagram-validator` +- `mermaidchart.vscode-mermaid-chart/mermaid-diagram-preview` + +**You MUST use them** to prevent syntax errors. + +### Tool-driven diagram workflow (required) + +For every Mermaid diagram you include: + +1. **Pick the correct diagram type** + - consult `get_syntax_docs` before drafting the diagram type +2. **Draft minimal syntax** + - prefer simple, reliable Mermaid constructs +3. **Validate** + - run `mermaid-diagram-validator` + - fix and re-validate until it passes +4. **Preview sanity check** + - recommended for final architecture diagrams + +### Mermaid reliability rules + +- Always start with a valid diagram header +- Do not mix diagram grammars +- Keep node IDs simple and alphanumeric/underscore +- Put complex text in labels, not IDs +- Quote edge labels if they contain special characters +- Use unique node IDs +- Prefer `flowchart` and `sequenceDiagram` unless another type is clearly supported + +**Gating requirement:** +> Do not output any Mermaid diagram unless it has passed the Mermaid validator. + +--- + +## 🔍 Procedure + +### 0) Triage & Inventory (fast, evidence-based) + +Identify likely evidence sources such as: + +- route declarations +- controllers / handlers / resolvers +- middleware / filters / guards / interceptors +- authorization decorators / annotations / attributes +- policy providers / handlers +- auth configuration +- LDAP / AD / OIDC / SAML / JWT config +- role / permission enums and constants +- database tables related to users, roles, groups, permissions, memberships, grants +- IaC or deployment config affecting authn/authz +- tests asserting authorization behavior +- admin/support tooling +- import/export and batch paths +- background job consumers and message handlers + +Produce a short inventory list with evidence links. + +### 1) Identify identity model + +Determine, with evidence where possible: + +- authentication mechanisms in use +- identity providers / directory integrations +- principal types +- token/session contents relevant to authorization +- group / role / permission resolution path +- whether LDAP / AD groups are used directly or mapped into application roles/claims +- whether identity and authorization metadata are static, configuration-driven, or data-driven + +### 2) Identify authorization model + +Determine: + +- which authorization paradigms are present: + - RBAC + - ABAC + - PBAC + - claims/scopes + - resource ownership checks + - tenant boundary checks + - feature-flag-based gating +- the named roles, groups, permissions, scopes, claims, or policies discovered +- where they are defined +- whether there is a single source of truth or fragmented sources +- whether roles/groups are broad business labels or precise permission bundles +- whether access is coarse-grained, fine-grained, or inconsistent + +### 3) Map enforcement points + +For each major entry point and action path, identify where access is checked: + +- edge/gateway +- middleware +- route/controller decorator/attribute/annotation +- service layer +- domain logic +- repository/data-access layer +- UI-only checks +- job worker / consumer / scheduler path + +For each enforcement point, determine whether it appears: + +- centralized +- duplicated +- missing +- inconsistent +- bypassable +- dependent on client behavior +- dependent on naming convention rather than durable policy logic + +### 4) Build endpoint and action mapping + +Create a mapping for: + +- HTTP endpoints +- GraphQL operations +- RPC methods +- message consumers +- background jobs +- admin/support actions +- export/import flows + +For each, capture where possible: + +- route/action name +- principal type +- required role/group/permission/policy/claim/scope +- enforcement location +- evidence +- confidence +- whether protection is explicit, inherited, indirect, or missing + +### 5) Detect architecture weaknesses and authorization drift + +Look specifically for: + +- missing authorization on sensitive endpoints +- UI-only authorization +- inconsistent policy names +- role explosion or ambiguous role semantics +- hard-coded role strings scattered through code +- stale or unused permissions +- broad wildcard/admin grants +- support/admin impersonation risk +- privilege escalation paths +- missing tenant/resource ownership checks +- confused deputy behavior +- direct object reference exposure +- inconsistent group-to-role mapping +- environment-specific drift +- hidden alternate paths through jobs, webhooks, or internal APIs + +### 6) Produce diagrams (validated) + +You MUST include Mermaid diagrams unless Mermaid rendering is unsupported. + +#### Required diagram set + +1. **Identity & Access Control Overview** + - identity sources + - token/session/claims path + - group/role/permission mapping + - major enforcement points + - trust boundaries where relevant + +2. **Authorization Enforcement Architecture** + - major subsystems + - entry points + - middleware/filters/guards + - policy engines/providers + - service/domain enforcement + - datastore lookups + - external group/directory dependencies + +3. **Endpoint-to-Policy / Role Mapping View** + - use a Mermaid diagram that makes major protected surfaces visible + - group similar endpoints when necessary for readability + +4. **Top 2–3 Sequence Diagrams** + - highest-risk or most important auth flows, such as: + - login and claims resolution + - privileged admin action + - export/reporting action + - service-to-service authorization + - support impersonation flow + +#### Optional diagrams + +- LDAP / AD group mapping view +- tenant / organization access model +- data classification vs authorization boundary map +- deployment/runtime auth boundary view + +### 7) Evaluate control maturity + +For key access-control concerns, identify whether controls are: + +- **PRESENT** +- **ABSENT** +- **UNKNOWN** + +Examples: + +- centralized policy enforcement +- least-privilege role design +- separation of duties +- admin action protection +- support impersonation controls +- service identity scoping +- tenant isolation checks +- export/download restrictions +- audit logging for authorization decisions or privileged actions +- deny-by-default behavior +- test coverage for authorization rules + +When marked PRESENT, provide evidence. + +### 8) Validation plan + +Create a validation plan with **3–6 scenarios**, prioritizing the highest-risk or least-certain flows. + +Include: + +- scenario intent +- preconditions +- steps +- expected result +- evidence to collect +- owner + +Examples: + +- user without required role denied a privileged route +- stale LDAP group no longer grants access +- service token cannot call admin-only API +- cross-tenant object access is denied +- support impersonation is logged and approval-gated + +--- + +## 📏 Access control architecture score (0–100) + +You MUST assign an overall score from **0–100** reflecting the maturity and risk posture of the application’s identity and authorization design. + +Purpose: + +- This is not a vulnerability score. +- It reflects how understandable, enforceable, consistent, least-privilege, and bypass-resistant the authorization architecture appears to be. + +### Scoring dimensions + +1. **Identity clarity and source-of-truth integrity (0–20)** + - Are identity sources and access assignments clear, consistent, and evidence-backed? + +2. **Authorization model quality (0–20)** + - Are roles/groups/permissions/policies well-structured, least-privilege, and semantically meaningful? + +3. **Enforcement consistency (0–20)** + - Are checks applied consistently across routes, services, jobs, and admin paths? + +4. **Bypass and escalation resistance (0–15)** + - Are there alternate paths, UI-only checks, hidden admin routes, or impersonation risks? + +5. **Observability and governance (0–15)** + - Are privileged actions, access changes, and authorization decisions testable, reviewable, and auditable? + +6. **Complexity and maintainability (0–10)** + - Is the model understandable and scalable, or fragmented and brittle? + +### Score interpretation bands + +- 0–19: Very weak +- 20–39: Weak +- 40–59: Mixed +- 60–79: Strong +- 80–100: Mature + +Also report: + +- **Confidence:** High / Medium / Low +- **Score volatility:** `+X / -Y` +- **Top 3 score drivers** +- **What would raise the score** +- **What would lower the score** + +--- + +## 📦 Output Format (GitHub-Flavored Markdown) + +Return the review as PR-comment-ready Markdown in chat. + +If the environment supports writing files, also write: + +`./Access Control Architecture Review - {{DATE}}.md` + +### 0. Executive summary + +- 5–10 bullets +- what was found +- what appears strong +- what appears risky +- major unknowns +- next actions + +### 0.1 Architecture score + +- **Overall access control architecture score:** `NN/100` +- **Confidence:** High / Medium / Low +- **Score volatility:** `+X / -Y` +- **Primary score drivers:** ... +- **What would raise the score:** ... +- **What would lower the score:** ... + +### 1. Scope + +- In-scope components: +- Out-of-scope: +- Principal types analyzed: +- Trust boundaries relevant to identity/access: +- Key protected assets/actions: + +### 1.1 Intake summary + +- Identity source classification: +- Principal types: +- Authorization model: +- Source of truth for assignments: +- Main enforcement locations: +- Environment/tenant variance: +- Confidence in this section: + +### 2. Assumptions & Unknowns + +- **ASSUMPTION:** ... +- **UNKNOWN:** ... (include “Who can confirm” + question) +- **Score-moving unknown:** ... (include likely score effect) + +### 3. Architecture Overview (with tool-validated diagrams) + +#### 3.1 Identity & Access Control Overview + +```mermaid +flowchart LR + %% validated diagram +``` + +**Evidence** + +- `path/to/file` (symbol: ..., lines ...) + +#### 3.2 Authorization Enforcement Architecture + +```mermaid +flowchart LR + %% validated diagram +``` + +**Evidence** + +- ... + +#### 3.3 Endpoint / Action Protection View + +```mermaid +flowchart LR + %% validated diagram +``` + +**Evidence** + +- ... + +#### 3.4 High-risk interaction sequences + +```mermaid +sequenceDiagram + %% validated diagram +``` + +**Evidence** + +- ... + +### 4. Identity and authorization inventory + +#### 4.1 Identity sources and auth mechanisms + +Table: + +`Component | Mechanism | Evidence | Notes` + +#### 4.2 Groups, roles, permissions, claims, scopes, policies + +Table: + +`Type | Name | Defined In | Consumed In | Source of Truth | Notes` + +### 5. Enforcement points + +Table: + +`Surface | Enforcement Type | Location | Required Access | Confidence | Notes` + +### 6. Endpoint and action mapping + +Table: + +`Entry Point / Action | Principal Type | Required Role/Group/Permission/Policy | Enforcement Location | Evidence | Status | Notes` + +Status values: + +- Explicit +- Inherited +- Indirect +- Missing +- Unknown + +### 7. Findings + +Table: + +`ID | Category | Summary | Severity (L/M/H) | Evidence | Why it matters | Recommendation` + +Suggested categories: + +- Missing authorization +- Inconsistent enforcement +- Weak role design +- Group-role mapping drift +- Privilege escalation +- Tenant isolation gap +- Admin/support risk +- Hidden alternate path +- Observability gap +- Maintainability risk + +### 8. Control maturity + +Table: + +`Control | Status (PRESENT/ABSENT/UNKNOWN) | Evidence | Notes/Open questions` + +### 9. Validation plan + +For each scenario: + +- Intent +- Preconditions +- Steps +- Expected result +- Evidence to collect +- Owner + +### 10. Owners + +- Who confirms assumptions: +- Who validates identity source mappings: +- Who owns policy definitions: +- Who owns route/service enforcement: +- Who drives remediation: + +### 11. Open questions + +- bullets with owner and where to look in repo + +### ✅ Quality checks + +- Groups, roles, permissions, claims, scopes, and policies are distinguished rather than conflated +- LDAP / directory mappings are explicitly marked as confirmed or assumed +- Endpoint/action mappings tie to concrete enforcement points +- UI-only controls are not treated as sufficient authorization +- Alternate execution paths were reviewed +- Evidence vs inference is clearly separated +- All Mermaid diagrams were validated using `mermaid-diagram-validator` diff --git a/prompts/add-content-security-policy.prompt.md b/prompts/add-content-security-policy.prompt.md new file mode 100644 index 0000000..69bc49e --- /dev/null +++ b/prompts/add-content-security-policy.prompt.md @@ -0,0 +1,204 @@ +--- +agent: "application-security-analyst" +name: add-content-security-policy +description: "Help developers design, implement, and safely roll out a new Content-Security-Policy (CSP) for a web application." +--- + +# 🛡️ Prompt: Add a Content Security Policy (CSP) + +--- + +## Purpose + +Create a deployable **Content-Security-Policy** for a specific web application, including an implementation and rollout plan that avoids common CSP footguns. + +## How to use + +Provide the inputs below (or point me at the relevant files in this repo). If you don’t know an input, say “unknown” and I’ll propose safe defaults plus focused questions. + +**Inputs (fill what you can):** + +- App type / stack: ${input:stack:Example: Express + React, or ASP.NET Core MVC, or Nginx serving SPA} +- Environments: ${input:envs:Example: dev + staging + prod} +- Where headers are set today: ${input:header_location:Example: Helmet middleware, Nginx, CDN, framework config} +- Existing CSP (if any): ${input:current_csp:Example: Content-Security-Policy: ...} +- Third-party integrations that must load: ${input:third_parties:Example: Google Analytics, Sentry, Stripe, Intercom} +- Known inline scripts/styles (or frameworks that emit them): ${input:inline_needs:Example: inline hydration script, CSS-in-JS, legacy onclick handlers} +- Required network destinations (API, websockets, auth, uploads): ${input:connect_destinations:Example: `https://api.example.com`, `wss://socket.example.com`} +- Embed/iframe needs: ${input:framing:Example: must be embeddable by `https://partner.example`} +- Reporting preference: ${input:reporting:Example: start with Report-Only + report-to endpoint} + +## Rules + +- You MUST be evidence-first: reference exact file paths and (when possible) line ranges where headers/templates/scripts are configured. +- You MUST NOT recommend `unsafe-inline` or `unsafe-eval` as the primary solution. +- You MUST prefer least-privilege allowlists and explain why each origin/source is needed. +- You MUST include a safe rollout plan (Report-Only → triage → enforce) unless the user explicitly says “enforce immediately”. +- You MUST call out incompatibilities and open questions instead of guessing. + +## ✅ Context / Assumptions + +- You can read project files in this workspace (server config, reverse proxy/CDN config, HTML templates, frontend build output, docs). +- CSP is **defense-in-depth** against XSS; it does not replace proper output encoding, framework escaping, and avoiding dangerous DOM sinks. +- There may be multiple policies (dev vs prod, per-route, report-only vs enforced). Treat each distinctly. + +## 🔍 Procedure + +### 1) Identify the delivery point(s) + +- Locate where headers are set: + - App server middleware (e.g., Express/Helmet, ASP.NET middleware) + - Reverse proxy (e.g., Nginx/Apache) + - CDN / edge configuration + - HTML `<meta http-equiv="Content-Security-Policy">` (note limitations; prefer response headers) +- List every place CSP can be added or overridden (including per-route/per-tenant behavior). + +### 2) Inventory executable and externally loaded content + +- Find all script execution requirements: + - inline scripts (including SSR hydration bootstraps) + - inline event handlers (`onclick=`, etc.) + - dynamic script injection / loaders + - `eval`, `new Function`, `setTimeout(string)` + - workers/wasm +- Identify all resource origins required by the app: + - `script-src`, `style-src`, `img-src`, `font-src`, `connect-src`, `frame-src`, `media-src`, `worker-src` +- Identify third-party services and ensure they’re not serving user-controlled content from allowed origins. + +### 3) Choose a CSP strategy: nonce-based, hash-based, or strict static + +- Prefer **nonce-based** CSP for apps that must execute inline scripts (modern SSR/CSR patterns). +- Prefer **hash-based** CSP for stable, build-time inline snippets. +- Prefer **no inline execution** (best) when the app can be refactored to external scripts. +- Decide whether to use `strict-dynamic` (often valuable with nonce-based loaders) and document the compatibility tradeoffs. + +### 4) Produce an initial Report-Only policy (diagnostic) + +- Draft a conservative **Report-Only** policy that is close to the intended enforced policy. +- Add reporting: + - Prefer `report-to` (Reporting API) where feasible; otherwise use `report-uri`. + - Ensure reporting endpoints don’t leak secrets and can handle abuse (rate limiting, auth if needed). + +### 5) Produce the target enforced policy (deployable) + +Include strong baseline directives unless there is evidence they break the app: + +- `default-src 'self'` +- `object-src 'none'` +- `base-uri 'none'` (or `'self'` if required) +- `frame-ancestors 'none'` (or explicit allowlist if the app must be framed) +- `script-src` using nonces/hashes; avoid `unsafe-inline` and `unsafe-eval` +- `upgrade-insecure-requests` (and optionally `block-all-mixed-content` if compatible) + +### 6) Implementation plan (code/config changes) + +- Provide the concrete changes needed to: + - generate a per-response nonce (unpredictable) + - attach the nonce to required `<script>` tags + - ensure caches/CDNs don’t cause nonce mismatch (vary by response; avoid caching HTML with embedded nonces) + - set the CSP header consistently on all relevant responses + +### 7) Verification and rollout + +- Manual checks: + - browser DevTools Console / Network for CSP violations + - confirm the header is present on key routes +- Automated checks: + - unit/integration test for header presence and critical directives + - (if using nonces) test that nonce changes per response +- Rollout steps: + - ship Report-Only to staging → triage violations → tighten → enforce + - monitor for regressions; document exceptions with justification + +## 📦 Output Format + +Return Markdown with the following structure and field names. + +### 1) Summary + +- Goal: +- Proposed strategy: Nonce-based / Hash-based / No-inline +- Environments covered: +- CSP delivery points found: + +### 2) Current State (Evidence) + +- Where headers are set (file path + mechanism): +- Existing CSP(s) (exact strings): +- Inline execution sources found: +- Third-party origins required: + +### 3) Proposed Policies + +- Report-Only header (single line): +- Enforced header (single line): +- Directive rationale (brief bullets per directive/source): + +### 4) Required Code/Config Changes + +List each change as: + +- Change ID: +- File(s): +- What to change: +- Why: +- Risk / rollout notes: + +### 5) Risks / Footguns to Avoid + +Table columns: + +- ID +- Risk +- Evidence +- Impact +- Safer alternative + +### 6) Verification Steps + +- Local verification: +- Staging verification: +- Production verification: + +### 7) Open Questions + +- … + +## Examples + +### Example (Expected Output) + +```md +## 1) Summary +- Goal: Add a CSP that blocks inline script execution by default, while allowing required SSR hydration safely. +- Proposed strategy: Nonce-based +- Environments covered: staging → prod +- CSP delivery points found: Express middleware (Helmet) in src/server/securityHeaders.ts + +## 3) Proposed Policies +- Report-Only header (single line): + Content-Security-Policy-Report-Only: default-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; script-src 'self' 'nonce-{RANDOM_PER_RESPONSE}' 'strict-dynamic'; style-src 'self'; img-src 'self' data:; connect-src 'self' https://api.example.com; report-to csp-endpoint + +- Enforced header (single line): + Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; script-src 'self' 'nonce-{RANDOM_PER_RESPONSE}' 'strict-dynamic'; style-src 'self'; img-src 'self' data:; connect-src 'self' https://api.example.com; upgrade-insecure-requests + +## 4) Required Code/Config Changes +- Change ID: CSP-CHG-01 +- File(s): src/server/middleware/securityHeaders.ts +- What to change: Generate a cryptographically-random nonce per HTTP response and set it on res.locals.cspNonce; set CSP header including script-src 'nonce-...'. +- Why: Allows required inline bootstrap scripts to run without enabling unsafe-inline. +- Risk / rollout notes: Ensure HTML responses are not cached at the CDN when they include per-response nonces. + +## 6) Verification Steps +- Local verification: Load the home page and confirm no CSP violations; inspect Network → response headers for Content-Security-Policy. +- Staging verification: Enable Report-Only, collect violations for 24–72 hours, then tighten allowlists before enforcing. +- Production verification: Confirm nonce changes per response; monitor CSP report endpoint volume and error rate. +``` + +## ✅ Quality checks + +- Evidence-first: every recommendation references a real location in this repo (file paths + line ranges when possible). +- No guessing: if header injection points or third-party requirements can’t be found, list focused next checks and ask 1–3 questions. +- Least privilege: every allowed source/origin is justified; no broad wildcards unless there’s a documented reason. +- No insecure defaults: do not rely on `unsafe-inline`/`unsafe-eval` as the main path; if mentioned, label as temporary and provide safer alternatives. +- Deployability: include a rollout plan and verification steps; highlight caching/nonce pitfalls explicitly. diff --git a/prompts/assess-logging.prompt.md b/prompts/assess-logging.prompt.md index f482140..1614d5c 100644 --- a/prompts/assess-logging.prompt.md +++ b/prompts/assess-logging.prompt.md @@ -1,19 +1,53 @@ +--- +agent: "application-security-analyst" +name: assess-logging +description: "Audit logging for sensitive data exposure." +--- + # 🕵️ Prompt: Logging & Sensitive Data Exposure Audit -You are reviewing application code for **unsafe logging practices**, **PII exposure**, and **improper log hygiene**. +## ✅ Context / Assumptions + +- You can read project files in this workspace. +- Prefer evidence-first: cite file paths and (when possible) line ranges for each claim. +- Do **not** modify files; report findings and recommendations only. +- Do not echo secrets or sensitive values in your output (redact samples). + +## 🔍 Procedure + +1. Identify logging entry points (logger wrappers, middleware, request/response logging, error handlers). +2. Identify sensitive sources: + - Credentials, tokens, API keys, session IDs + - `Authorization`/cookies, CSRF tokens + - PII (emails, phone, address, IDs) +3. Trace sources → sinks: + - Logs, telemetry, exception/stack traces, debug output +4. Flag unsafe patterns: + - Full request/response bodies or headers without allow-listing + - Stack traces or exception objects that include sensitive context + - Console/print statements in production paths + - Insecure log transport or overly broad log destinations +5. Recommend safe alternatives: + - Structured logging + allow-listed fields + - Redaction filters (headers/cookies/tokens) + - Data minimization defaults -Identify any of the following issues: +## 📦 Output Format -- Logging of sensitive information (e.g. passwords, tokens, API keys, session IDs) -- Unfiltered logs that include full request/response bodies, headers, or user-submitted data -- Logging of stack traces or exceptions without redaction or sanitization -- Console or print statements left in production logic -- Logs that include internal system paths, configurations, or database queries -- Use of insecure transports for logs (e.g. writing logs to public cloud buckets without access control) +Return Markdown with this structure: -Also check for: +- **Summary**: top 3 risks + overall risk (Low/Medium/High/Critical) +- **Findings** (repeat): + - **Issue**: + - **Severity / Likelihood**: + - **Where**: file path + symbol/function + - **Evidence**: file path (+ line range if available) + - **Recommendation**: + - **Verification**: how to test the fix / what to confirm +- **Suggested redaction policy**: bullet list of default redactions and allowed fields -- Missing structured log formats (JSON, ECS, etc.) -- Lack of logging levels or misuse of `debug`, `info`, `warn`, `error` +## ✅ Quality checks -Provide refactor suggestions for redacting or excluding sensitive data. Recommend structured logging libraries or filters, and remind developers to align with least-privilege and data minimization principles. +- Every finding includes **Where** + **Evidence**. +- Output does not include raw secrets/PII. +- Recommendations do not rely on “turn off logging” or “disable security controls” as the primary fix. diff --git a/prompts/business-logic-review.prompt.md b/prompts/business-logic-review.prompt.md new file mode 100644 index 0000000..984af5b --- /dev/null +++ b/prompts/business-logic-review.prompt.md @@ -0,0 +1,94 @@ +--- +agent: "application-security-analyst" +name: business-logic-review +description: "Analyze business logic flows and identify security/correctness risks." +--- + +# 🧠 Prompt: Business Logic Flow Analysis + +You are a senior software engineer performing a **multi-stage review of application behavior and business logic flow**. + +## ✅ Context / Assumptions + +- Start from a fresh read of the current workspace. +- Prefer evidence-first: cite file paths and (when possible) line ranges. +- Do **not** modify files; produce a centralized map and assessment only. +- If scope is unclear, ask up to 3 clarifying questions before finalizing. + +## 🔍 Procedure + +### ⚠️ Important + +- Do not modify code or leave inline comments. +- This is a **centralized logic map and assessment**, not a line-by-line code review. +- Begin only after fully reading and mapping the project. + +### Steps + +1. Identify the app’s purpose and primary user journeys. + - Describe the overall purpose of the application in 2–3 sentences. + - Identify the main **business logic zones**: + - Which files/modules implement critical rules, calculations, or policies? + - Tag areas like pricing logic, access control, account lifecycle, feature gating, compliance handling, etc. + - For each zone, list: + - File paths + - A brief description of what decisions are made there +2. Map “business logic zones” (where important decisions/rules live). + - Describe how data flows **from user interaction to backend logic to output**: + - What are the main entry points? (e.g., web routes, API endpoints) + - Which layers handle request parsing, validation, routing? + - Where is business logic applied? When is it bypassed? + - Where and how is data persisted, transformed, or returned? +3. Trace key flows from entry point → parsing/validation → business rule → persistence → response. + - Based on the mapping above, evaluate potential concerns: + - Is any logic duplicated or scattered? + - Are there business rules implemented in inappropriate layers? (e.g., in views or route handlers) + - Are any flows brittle, overly coupled, or difficult to reason about? + - Are user roles, permissions, or state transitions clearly enforced? + - Note any areas where logic could be: + - Extracted for clarity + - Consolidated + - Better tested or documented +4. Identify logic risks: + - bypassable checks, inconsistent enforcement, unsafe assumptions + - state machine / lifecycle gaps + - duplicated rules that drift +5. Recommend refactors/tests that reduce security and correctness risk. + +## 📦 Output Format + +Return Markdown with the following structure. If your environment supports writing files, also write it to `Business Logic Flow Analysis - {{DATE}}.md` in the project root: + +```markdown +# 🧠 Business Logic Flow Analysis + +## ✅ App Purpose + +... + +## 🧭 Business Logic Zones + +- **[Domain Name]** + - **Files**: ... + - **Summary**: ... + +## 🔄 Data Flow Narrative + +- [Example: User submits payment form → API route → billing logic → DB → response] + +## 🚩 Flow Observations + Concerns + +- **Area**: ... + - **Concern**: ... + - **Suggestion**: ... + +## 🧠 Suggested Refactors or Tests + +- ... +``` + +## ✅ Quality checks + +- Claims about business rules reference concrete code locations. +- Suggestions are scoped and testable. +- No instructions imply editing files directly inside the output. diff --git a/prompts/check-access-controls.prompt.md b/prompts/check-access-controls.prompt.md index dff72fd..383e072 100644 --- a/prompts/check-access-controls.prompt.md +++ b/prompts/check-access-controls.prompt.md @@ -1,16 +1,48 @@ +--- +agent: "application-security-analyst" +name: check-access-controls +description: "Review access control and authorization enforcement." +--- + # 🔒 Prompt: Access Control & Authorization Review -You are auditing this codebase for **authorization and access control weaknesses**. +## ✅ Context / Assumptions + +- You can read project files in this workspace. +- Prefer evidence-first: cite file paths and (when possible) line ranges. +- Do **not** modify files; report findings and recommendations only. +- Assume attackers can tamper with client-side state; require server-side enforcement. + +## 🔍 Procedure + +1. Identify authn/authz boundaries: + - middleware/guards, policy helpers, route handlers/controllers, service methods. +2. Enumerate protected resources and actions (read/write/admin operations). +3. Look for common access control failures: + - missing authn on protected endpoints + - missing authz (IDOR, tenant bypass, role bypass) + - inconsistent checks across similar endpoints + - hardcoded role strings without central policy +4. Verify object-level authorization: + - ownership checks, tenant scoping, subject/param matching. +5. Recommend a consistent enforcement pattern (middleware/policy layer) and verification tests. + +## 📦 Output Format -Focus on identifying: +Return Markdown with: -- Missing or weak role-based access control (RBAC) or attribute-based access control (ABAC) enforcement -- Direct access to protected routes, actions, or data without permission checks -- Business logic that bypasses access validation (e.g. trusting client-side flags or roles) -- Use of hardcoded role or permission strings without central enforcement -- Functions exposed via APIs that should require authentication but don’t -- Lack of contextual access checks (e.g. ensuring users can only access their own records) +- **Summary**: top 3 issues + overall risk +- **Findings** (repeat): + - **Issue**: + - **Severity / Likelihood**: + - **Where**: file path + symbol + - **Evidence**: file path (+ line range if available) + - **Recommendation**: + - **Verification**: negative test/bypass attempt to prove it’s fixed +- **Consistency checklist**: bullets of “must be present everywhere” checks -If applicable, recommend use of secure middleware, centralized auth policies, and consistent permission enforcement patterns. +## ✅ Quality checks -Highlight both missing controls and inconsistently applied ones. Annotate with comments and suggest safer refactors. +- Each finding includes object-level detail when applicable (which identifier can be abused). +- Claims are backed by specific code locations. +- Recommendations avoid “security through obscurity” (e.g., hiding endpoints) as a primary control. diff --git a/prompts/check-for-secrets.prompt.md b/prompts/check-for-secrets.prompt.md index 0f11ae4..d684fcb 100644 --- a/prompts/check-for-secrets.prompt.md +++ b/prompts/check-for-secrets.prompt.md @@ -1,13 +1,42 @@ +--- +agent: "application-security-analyst" +name: check-for-secrets +description: "Scan for hardcoded secrets and credential leakage patterns." +--- + # 🔐 Prompt: Hardcoded Secrets & Credential Audit -Act as a secure code reviewer analyzing this file for **hardcoded secrets**, API keys, tokens, credentials, or other sensitive information. +## ✅ Context / Assumptions + +- You can read project files in this workspace. +- Do **not** print or re-output any real secrets you find. Redact values (e.g., show only prefixes). +- Do **not** modify files; report findings and remediation guidance only. +- Prefer evidence-first: cite file paths and (when possible) line ranges. + +## 🔍 Procedure + +1. Scan for hardcoded credentials/tokens/keys (string literals, config files, test data). +2. Check “near-secrets” patterns: + - JWT/HMAC secrets, OAuth client secrets, connection strings + - private keys, certificates, signing material +3. Check risky usage patterns: + - secrets in logs + - secrets in frontend bundles + - `.env` or local config patterns being used in prod paths +4. For each finding, determine: + - type of secret, where it flows, exposure surface (repo, logs, client) +5. Recommend a secure storage and rotation approach (vault/secret manager) and verification steps. + +## 📦 Output Format + +Return Markdown with: -Flag any of the following patterns: +- **Summary**: count of potential secrets + top risks +- **Findings** table: Type | Severity | Where | Evidence | Recommendation | Verification +- **Rotation & containment plan** (bullets): revoke/rotate, invalidate sessions, monitor usage -- API keys, access tokens, client secrets, or passwords embedded as string literals -- Usage of `process.env` in frontend code or without proper runtime protection -- Sensitive values written to `.env`, `.properties`, or `appsettings.json` files without secret management -- OAuth tokens, JWTs, or HMAC secrets stored or logged in plaintext -- Secrets stored in comments, JSON blobs, test configs, or logs +## ✅ Quality checks -Highlight these with comments or suggested changes. Recommend usage of a secure vault (e.g. Azure Key Vault, AWS Secrets Manager, CyberArk Conjur) and explain the risk of each finding. +- Do not include raw secret values. +- Findings include concrete code locations. +- Remediation includes both (1) removing the secret from code and (2) rotating/revoking it. diff --git a/prompts/check-for-unvalidated-genai-acceptances.prompt.md b/prompts/check-for-unvalidated-genai-acceptances.prompt.md index fd8efa4..c8a9268 100644 --- a/prompts/check-for-unvalidated-genai-acceptances.prompt.md +++ b/prompts/check-for-unvalidated-genai-acceptances.prompt.md @@ -1,19 +1,49 @@ +--- +agent: "application-security-analyst" +name: check-for-unvalidated-genai-acceptances +description: "Identify unvalidated acceptance of GenAI-generated code and dependencies." +--- + # 🤖 Prompt: Unvalidated GenAI Code Acceptance Audit -You are reviewing code for signs that **AI-generated content** (e.g. from GitHub Copilot, ChatGPT, CodeWhisperer) has been accepted without validation, testing, or verification. +## ✅ Context / Assumptions + +- You can read project files in this workspace. +- Prefer evidence-first: cite file paths and (when possible) line ranges. +- Do **not** modify files. +- The goal is to prevent “over-trust” of AI-generated code and hallucinated dependencies/APIs. + +## 🔍 Procedure + +1. Look for supply-chain red flags: + - suspicious/new dependencies, packages that may not exist, unpinned versions. +2. Look for API correctness red flags: + - calls to non-existent/undocumented APIs, copy/pasted snippets that don’t match project frameworks. +3. Look for validation and testing gaps: + - newly added logic without tests, placeholder implementations, TODOs referencing AI. +4. Look for context drift: + - config/code patterns that don’t match the repo’s infra stack. +5. Recommend verification steps: + - confirm dependencies in official registries + - run/build/test steps and add targeted tests + - require human review for privileged/unsafe operations -Look for and flag the following: +## 📦 Output Format -- Dependencies or packages that do not exist in official registries (possible hallucinated packages) -- Calls to non-existent, deprecated, or undocumented API methods -- Configuration code that does not match the project’s infrastructure (e.g. Azure config in AWS projects) -- Use of placeholder, ambiguous, or overly generic variable/method names (e.g. `doTask()`, `handleThing()`) -- Comments or TODOs referencing AI use without follow-up verification +Return Markdown with: -Also check for: +- **Summary**: top 3 risks + quick verification checklist +- **Findings** (repeat): + - **Issue**: + - **Severity / Likelihood**: + - **Where**: + - **Evidence**: + - **Recommendation**: + - **Verification**: +- **Verification checklist** (bullets): deps, APIs, tests, docs, config alignment -- Lack of test coverage or validation for recently added logic -- Sudden style or structure shifts that may indicate unreviewed AI insertion -- No accompanying documentation or context for added code +## ✅ Quality checks -Provide suggestions for verifying third-party resources, validating logic, and encouraging human-in-the-loop code review. Include annotations where risk or uncertainty is high. +- Findings are grounded in concrete evidence (not style-only opinions). +- Recommendations include a clear “how to verify” step. +- Avoid claiming a package/API is fake unless you can prove it; otherwise label as “needs verification”. diff --git a/prompts/csp-review.prompt.md b/prompts/csp-review.prompt.md new file mode 100644 index 0000000..9d5eaf4 --- /dev/null +++ b/prompts/csp-review.prompt.md @@ -0,0 +1,159 @@ +--- +agent: "application-security-analyst" +name: csp-review +description: "Review a web application’s Content-Security-Policy for XSS resistance, safe third-party usage, and deployability." +--- + +# 🛡️ Prompt: Content Security Policy (CSP) Review + +You are a senior application security engineer helping developers review and harden a web application’s Content Security Policy (CSP). + +## ✅ Context / Assumptions + +- You can read project files in this workspace (server config, HTML templates, frontend build output, and documentation). +- Prefer evidence-first: cite file paths and (when possible) line ranges. +- Do not modify files; report findings and recommendations only. +- Treat the CSP as part of a broader XSS defense-in-depth story: templating safety, framework escaping, and dangerous DOM sinks still matter. +- If there are multiple CSPs (prod vs dev, report-only vs enforced, per-route overrides), evaluate each separately. + +## 🔍 Procedure + +### 1) Identify where CSP is set + +- Determine how CSP is delivered: + - HTTP response header Content-Security-Policy / Content-Security-Policy-Report-Only + - HTML meta tag (less preferred, and with limitations) + - reverse proxy / CDN config +- List all locations where CSP can be configured or overridden. + +### 2) Inventory script and asset execution requirements + +- Find sources of executable content: + - inline scripts and inline event handlers (onclick=, onload=) + - eval / new Function / setTimeout(string) + - third-party tags (analytics, A/B testing, chat widgets) + - dynamic script injection and loaders + - web workers / wasm +- Identify the app’s templating system and any SSR/CSR framework involved. +- Identify whether nonces or hashes are currently supported and how they are generated. + +### 3) Evaluate the current CSP against modern XSS-resistant baselines + +For each policy (enforced and report-only), check at minimum: + +- script-src: + - Avoid unsafe-inline and unsafe-eval. + - Prefer nonce-based (script-src 'nonce-…') or hash-based allowlisting. + - If using nonces, ensure they are per-response, unpredictable, and applied to every inline script that must run. + - Consider strict-dynamic when using nonces and trusted loaders. +- object-src: + - Prefer object-src 'none'. +- base-uri: + - Prefer base-uri 'none' or self. +- frame-ancestors: + - Set frame-ancestors to prevent clickjacking (do not rely on X-Frame-Options alone). +- default-src: + - Ensure it is set and not overly permissive. +- img-src / font-src / style-src / connect-src: + - Review for excessive wildcards and risky schemes. + - Identify any need for data: (images) and constrain it to where it is actually required. +- form-action: + - Constrain where forms can submit. +- upgrade-insecure-requests and block-all-mixed-content: + - Use where appropriate to reduce accidental mixed content. +- Reporting: + - Determine whether report-to / report-uri is configured. + - Ensure reports do not leak secrets and endpoints are authenticated/abuse-resistant as needed. + +### 4) Look for common CSP footguns and bypass conditions + +- Over-broad allowlists: + - wildcard subdomains where attackers can host content + - allowing cdn.example.com but the CDN serves user-controlled uploads +- Risky schemes and directives: + - allowing blob: broadly in script-src + - missing base-uri or frame-ancestors +- Nonce weaknesses: + - nonce reused across requests or users + - nonce appears in logs, error pages, or caches + - CSP set at CDN while HTML is generated elsewhere (nonce mismatch) +- Report-only mismatch: + - report-only is strict but enforced is permissive, creating a false sense of safety + +### 5) Produce a deployable recommendation + +- Propose a target CSP that matches what the app actually needs. +- Provide a rollout plan: + - start with report-only, triage violations, then enforce + - document known third-party requirements and exceptions +- Provide verification steps: + - browser devtools checks + - automated tests (header presence, nonce behavior) + - CSP evaluator review (as a sanity check, not as the single source of truth) + +## 📦 Output Format + +Return Markdown with the following structure. + +### 1) Summary + +- Overall posture: Strong / Moderate / Weak +- Top 3 risks +- Policies found: Enforced / Report-Only (list each distinct policy) + +### 2) Current Policy Inventory + +For each policy: + +- Where set: +- Applies to (routes/environments): +- Exact policy string: + +### 3) Findings Table + +Findings table columns: + +- ID +- Severity (Critical/High/Medium/Low) +- Issue +- Impact +- Where +- Evidence +- Recommendation +- Verification + +### 4) Recommended Target CSP + +- Recommended enforced policy string (single line) +- Notes on required code/config changes (nonces, hashes, third parties) + +### 5) Rollout Plan + +- Step-by-step plan to move from current state to target enforced CSP + +### 6) Open Questions + +- List any missing information that blocks a safe recommendation + +#### Example (Expected Output) + +```markdown +## 3) Findings Table + +| ID | Severity | Issue | Impact | Where | Evidence | Recommendation | Verification | +|---:|:--------:|-------|--------|-------|----------|----------------|--------------| +| CSP-01 | High | script-src allows unsafe-inline | Inline script injection can execute if an XSS bug exists | src/server/securityHeaders.js | Content-Security-Policy includes unsafe-inline | Move to nonce-based script-src and remove unsafe-inline | Load key pages and confirm no CSP violations; ensure inline scripts carry nonce | +| CSP-02 | Medium | Missing frame-ancestors | App can be framed (clickjacking) | infra/nginx.conf | No frame-ancestors directive present | Add frame-ancestors 'none' (or explicit allowlist) | Attempt to embed in an iframe; should be blocked | + +## 4) Recommended Target CSP + +Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; script-src 'self' 'nonce-{RANDOM_PER_RESPONSE}' 'strict-dynamic'; img-src 'self' data:; style-src 'self'; connect-src 'self' https://api.example.com +``` + +## ✅ Quality checks + +- Evidence-first: every finding includes Where and Evidence tied to this repo. +- Avoid hallucinations: if you cannot locate the CSP configuration or the policy text, state that explicitly and list what to check next. +- Recommendations are deployable: do not recommend broad allowlists or unsafe-inline/unsafe-eval as the primary fix. +- Proposed policy is compatible with app behavior: if nonces/hashes are required, specify where they must be implemented. +- Verification steps are concrete and can be executed by a developer. diff --git a/prompts/dependency-cve-triage.prompt.md b/prompts/dependency-cve-triage.prompt.md new file mode 100644 index 0000000..429c1d2 --- /dev/null +++ b/prompts/dependency-cve-triage.prompt.md @@ -0,0 +1,68 @@ +--- +agent: "application-security-analyst" +name: dependency-cve-triage +description: "Triage a dependency CVE using local repo evidence and remediation guidance." +--- + +# 🛡️ Prompt: Dependency CVE Triage + +Act as a **security vulnerability analyst** investigating a known CVE in the context of a web application dependency. + +--- + +## ✅ Context / Assumptions + +- Inputs: + - `${input:cve-number:Which CVE would you like me to analyze? (e.g., CVE-2024-12345)}` + - `${input:package-name:What dependency/package is this about? (optional if obvious from repo)}` +- Prefer evidence-first: cite file paths and (when possible) line ranges for dependency usage. +- If external browsing is available, use reputable sources (NVD, vendor advisories). If not, state that limitation. + +## 🔍 Procedure + +1. Confirm the vulnerable component: + - package name, ecosystem, affected versions, direct vs transitive. +2. Summarize the vulnerability: + - exploit vector, preconditions, impact, and common exploitation patterns. +3. Assess local context: + - where the dependency is used (imports, call sites) + - reachability of the vulnerable path + - configuration/environment preconditions + - existing mitigations (sandboxing, WAF, auth boundaries) +4. Recommend remediation: + - upgrade to fixed version (preferred) + - safe workaround/mitigation (explicitly labeled as stopgap) +5. Provide validation steps: + - tests/requests to prove non-reachability or that the fix works + +## 📦 Output Format + +Return two sections: + +1) **Dependency Tracker fields** (exactly this format): + + ```txt + - **Comment:** TEXT_FIELD + - **Analysis:** [Not Set, Exploitable, In Triage, Resolved, False Positive, Not Affected] + - **Justification:** [Not Set, Code not present, Code not reachable, Requires configuration, Requires dependency, Requires environment, Protected by compiler, Protected at runtime, Protected at perimeter, Protected by mitigating control] + - **Vendor Response:** [Not Set, Can not fix, Will not fix, Update, Rollback, Workaround available] + - **Details:** TEXT_FIELD + ``` + +2) **Evidence & validation** (Markdown): + + - **Local evidence**: where the dependency is referenced (file paths + line ranges if available) + - **Reachability reasoning**: why reachable/not reachable + - **Remediation plan**: upgrade/workaround + rollout notes + - **Verification steps**: commands/tests/requests to confirm + +## ✅ Quality checks + +- Clearly separate what is proven by local code evidence vs what comes from external advisories. +- If you cannot confirm a claim (e.g., no browsing), say so and provide the next best verification step. +- Avoid recommending disabling security controls as the primary remediation. + +--- + +Use clear and concise reasoning. If implementation context is missing, ask for what you need to make a grounded assessment. +Avoid speculation. If you cannot determine the impact, state that explicitly. diff --git a/prompts/review-auth-flows.prompt.md b/prompts/review-auth-flows.prompt.md index 06591af..5493929 100644 --- a/prompts/review-auth-flows.prompt.md +++ b/prompts/review-auth-flows.prompt.md @@ -1,21 +1,51 @@ +--- +agent: "application-security-analyst" +name: review-auth-flows +description: "Review authentication flows for common weaknesses and mitigations." +--- + # 🧪 Prompt: Authentication Flow Review -You are performing a security review of the application’s **authentication logic and flow handling**. +## ✅ Context / Assumptions + +- You can read project files in this workspace. +- Prefer evidence-first: cite file paths and (when possible) line ranges. +- Do **not** modify files; report findings and mitigations only. + +## 🔍 Procedure + +1. Identify auth entry points: + - login routes, session/token issuance, callback endpoints, middleware. +2. Trace authentication decisions: + - where identity is established, stored, and checked. +3. Check common authn risks: + - missing auth on protected resources + - weak session/JWT validation (issuer/audience/exp/alg) + - CSRF weaknesses for cookie-based auth + - missing rate limiting / lockout + - token leakage via logs/URLs/frontend +4. Check secure defaults: + - short-lived tokens + refresh pattern + - server-side sessions with expiry + - secure cookies (`HttpOnly`, `Secure`, `SameSite`) +5. Recommend mitigations and verification tests. -Look for the following common risks: +## 📦 Output Format -- Incomplete or improperly enforced authentication on protected routes or resources -- Weak or non-expiring session tokens (e.g. JWTs with long-lived expirations or missing `exp`) -- Missing or broken CSRF protections (check for missing `SameSite`, CSRF tokens in forms) -- Use of insecure login flows (e.g. no rate limiting, no multi-factor enforcement) -- Tokens or session identifiers exposed via logs, URLs, or frontend JavaScript -- Direct use of user-supplied credentials in downstream API requests without validation +Return Markdown with: -Check for secure practices like: +- **Summary**: top 3 issues + overall auth risk +- **Flow map**: bullets of login/session/token lifecycle +- **Findings** (repeat): + - **Issue**: + - **Severity / Likelihood**: + - **Where**: + - **Evidence**: + - **Recommendation**: + - **Verification**: -- Short-lived tokens + refresh workflows -- Server-side session storage with expiration -- Secure cookie flags (`HttpOnly`, `Secure`, `SameSite=Strict`) -- Use of well-tested identity providers or auth frameworks +## ✅ Quality checks -Recommend mitigations for any insecure implementations you identify. +- Findings distinguish between cookie-session vs bearer-token behavior. +- Claims include concrete code locations. +- Recommendations include a verification step (test/request). diff --git a/prompts/scan-for-insecure-apis.prompt.md b/prompts/scan-for-insecure-apis.prompt.md index 0641ce5..9b16c79 100644 --- a/prompts/scan-for-insecure-apis.prompt.md +++ b/prompts/scan-for-insecure-apis.prompt.md @@ -1,20 +1,36 @@ +--- +agent: "application-security-analyst" +name: scan-for-insecure-apis +description: "Scan for insecure or deprecated API usage and suggest safer alternatives." +--- + # ⚠️ Prompt: Insecure or Deprecated API Usage Scan -Act as a secure code auditor. Your goal is to identify any usage of **insecure, deprecated, or high-risk APIs** that may introduce vulnerabilities or future instability. +## ✅ Context / Assumptions + +- You can read project files in this workspace. +- Prefer evidence-first: cite file paths and (when possible) line ranges. +- Do **not** modify files; report findings and safer alternatives only. + +## 🔍 Procedure + +1. Search for high-risk APIs across languages/frameworks in the repo. +2. For each usage, determine: + - is input attacker-controlled? + - what is the sink/impact (RCE, injection, data exposure, SSRF, DoS)? + - is there a safe wrapper or mitigation already? +3. Recommend safer alternatives and explicit mitigation conditions. -Look for and flag the following patterns: +## 📦 Output Format -- Use of deprecated cryptographic functions (e.g. MD5, SHA1, `crypto.createCipher`, `System.Security.Cryptography.SHA1CryptoServiceProvider`) -- Legacy input/output APIs that lack sanitization or encoding features -- Insecure deserialization functions (e.g. `eval`, `JSON.parse` on external input, `ObjectInputStream`, `BinaryFormatter`) -- Unsafe file access or shell execution APIs (`fs.readFileSync` on user input, `Runtime.exec`, `ProcessBuilder`, `child_process.exec`) -- Unverified third-party libraries or packages not pinned to versions -- APIs that allow insecure HTTP communication (e.g. `http.get`, `fetch` without TLS, `WebClient` without `UseHttps`) +Return Markdown with: -Explain: +- **Summary**: top 3 risky APIs + overall risk +- **Findings table**: API | Risk | Severity | Where | Evidence | Safer alternative | Verification +- **Notes**: cases where usage might be acceptable with strict mitigations (state the mitigations required) -- Why the API is dangerous -- What safer alternative is recommended -- When/if it’s okay to use with proper mitigation +## ✅ Quality checks -Provide annotations or refactor suggestions where applicable. +- Each finding explains the risk in this repo’s context, not generically. +- Recommendations include at least one concrete safer alternative. +- Evidence includes file paths (+ line ranges if available). diff --git a/prompts/secure-code-review.prompt.md b/prompts/secure-code-review.prompt.md new file mode 100644 index 0000000..8ed771d --- /dev/null +++ b/prompts/secure-code-review.prompt.md @@ -0,0 +1,80 @@ +--- +agent: "application-security-analyst" +name: secure-code-review +description: "Perform a comprehensive secure code review and report prioritized findings." +--- + +# 🛡️ Prompt: Secure Code Review + +You are a senior software engineer performing a **comprehensive secure code review**. + +## ✅ Context / Assumptions + +- Start from a fresh read of the current workspace (and PR diff, if available). +- Prefer evidence-first: cite file paths and (when possible) line ranges. +- Do **not** modify files; report findings and recommendations only. +- If a PR diff is available, prioritize changed files first; expand repo-wide as needed. + +## 🔍 Procedure + +### ⚠️ Important + +- **Pay close attention to logic around:** + - input validation + - secrets or config handling + - logger redaction (request/response logging, error handlers, token/PII filters) + - access control +- environment-specific behavior +- Respond only after completing a fresh read of the codebase. + +### Steps + +1. Map the project (entry points, trust boundaries, sensitive assets). + - List all visible files and folders. + - For each, briefly describe its purpose or domain (e.g., "core logic," "auth," "logging utilities"). +2. Identify key subsystems/domains and their responsibilities. + - Identify the key **subsystems or functional domains** in this project. + - Explain what role each plays (e.g., request routing, encryption, config parsing). +3. Review by subsystem, focusing on high-risk classes: + - input validation, authn/authz, secrets/logging, crypto, deserialization, SSRF, dependency risks. + - For each subsystem: + - Highlight strengths + - Identify security observations + - Show file paths + relevant code + - Note code quality or maintainability issues + - Quote relevant code snippets or describe logic where needed. +4. Produce prioritized findings with remediation and verification steps. + +## 📦 Output Format + +Return Markdown with the following structure. If your environment supports writing files, also write it to `Secure Code Review - {{DATE}}.md` in the project root: + +```markdown +# 📋 Project Secure Code Review + +## ✅ Strengths + +- ... + +## 🛡️ Security Observations + +### [filename/path] + +- **Issue**: ... +- **Impact**: ... +- **Recommendation**: ... + +## 🔍 Code Quality Notes + +- ... + +## 🧭 Suggested Next Steps + +- ... +``` + +## ✅ Quality checks + +- Each finding includes **Where** + **Evidence**. +- Recommendations avoid “disable security controls” as the primary fix. +- Verification steps are actionable (test/request/scan). diff --git a/prompts/threat-model.prompt.md b/prompts/threat-model.prompt.md new file mode 100644 index 0000000..1dfb198 --- /dev/null +++ b/prompts/threat-model.prompt.md @@ -0,0 +1,705 @@ +--- +agent: "application-security-architect" +name: threat-model +description: "Threat model the system using Shostack’s 4Q framework and produce actionable artifacts with repo-grounded diagrams validated via Mermaid Chart tools." +--- + +# Prompt: 4Q Threat Model (DFDs + Supporting Diagrams, Tool-Validated Mermaid) + +## Mission & Scope + +**Goal:** Embed Adam Shostack’s **Four-Question** threat modeling into daily dev flow using VS Code + GitHub. Infer design from the repository (and/or PR diff), collaborate with the developer, and produce durable artifacts: + +1. a repo-grounded threat model Markdown report **with validated Mermaid diagrams** +2. a concise PR-ready summary (copy/paste) + +**4 Questions:** + +1. *What are we working on?* → Infer & confirm scope, assets, data flows, trust boundaries +2. *What can go wrong?* → Enumerate threats (context-specific), map to STRIDE + OWASP +3. *What are we going to do about it?* → Identify mitigations + gaps; status w/ evidence +4. *Did we do a good job?* → Validation plan: evidence to collect + owners + +**Where it runs:** + +- **Local:** VS Code Copilot Chat / Agent mode +- **PR review:** Same output format works as PR comment or issue description + +--- + +## ✅ Context / Assumptions + +- Threat model the current repository and/or current PR diff (if available). +- Persist output as a Markdown file in project root: + - `Threat Model Review - {{DATE}}.md` +- Evidence-first: cite file paths and (when possible) line ranges for claims about design, flows, mitigations, and trust boundaries. +- If you cannot confirm something from the repo/diff, label it **ASSUMPTION** or **UNKNOWN** (do not guess). +- Ask **2–4 clarifying questions** if scope, dataflows, deployment, or identities are unclear. +- Before generating the final report, perform a short mandatory **attack-surface and risk calibration intake** focused on **reachability, privileged actions, data sensitivity, runtime isolation, and score-driving controls**. +- Do not generate code changes unless explicitly requested. + +--- + +## 🧭 Evidence hierarchy and grounding rules + +Use this hierarchy for factual claims and prioritization: + +1. **Repo-confirmed** — code, config, IaC, docs, tests, manifests, or other repository evidence +2. **Runtime/deployment evidence** — screenshots, configs, logs, or operator-supplied deployment artifacts +3. **Operator-stated** — user answers not independently confirmed from evidence +4. **ASSUMPTION** +5. **UNKNOWN** + +Rules: + +- Prefer **Repo-confirmed** evidence over operator answers whenever both address the same claim. +- If operator answers conflict with repo evidence, prefer the repo evidence and explicitly call out the contradiction. +- If operator answers fill a gap the repo cannot answer, label those statements as **Operator-stated** rather than confirmed. +- Before using an operator answer to drive scoring or prioritization, attempt to **confirm, narrow, weaken, or contradict** it using repo evidence. +- Do not let operator answers quietly become “facts” in the report. +- The final report must include at least **3 code-anchored or IaC-anchored findings** that do not depend primarily on operator answers. +- Do not let unconfirmed edge/network unknowns overshadow a higher-confidence application or IaC weakness already evidenced in the repo. + +### Grounding examples + +- If the operator says **internal only**, check for public App Service, public ingress, open health endpoints, public webhooks, or externally reachable callback paths. +- If the operator says **no sensitive data**, inspect schemas, models, logging fields, secrets loading, import/export endpoints, and subscriber/user data stores. +- If the operator says **admins only**, inspect RBAC middleware, role enums, route guards, admin paths, and UI affordances. +- If the operator says **WAF present**, but there is no repo evidence, record it as **Operator-stated** and reduce confidence rather than treating it as fully confirmed. + +--- + +## 🛂 Mandatory pre-report attack-surface and risk calibration intake + +Before generating the final threat model, ask **4–8 concise questions** chosen to reduce ambiguity around: + +- reachable attack surface +- privileged pathways +- data sensitivity +- environment separation +- score-relevant controls + +Do **not** ask a flat generic questionnaire. +Instead, use a **branching intake**: + +### Stage 1 — Classify exposure and reachability first + +Ask 1–3 concise questions to place the application into an exposure bucket. + +Prefer plain language where possible so developers can answer accurately. + +Prioritize these: + +1. Which best describes the application’s reachability? + - isolated/internal segmented + - corp network only + - VPN/ZTNA only + - partner/vendor reachable + - public internet, non-prod only + - public internet, production + - mixed exposure + +2. Are any of these reachable differently from the main app: + - admin panel + - support tools + - debug endpoints + - APIs + - webhooks/callbacks + - file upload/import endpoints + - machine-to-machine interfaces + +3. If the application is **not** internet-exposed, what restricts access: + - network segmentation + - VPN + - ZTNA + - managed devices + - corporate SSO + - MFA for admins + - bastion/jump host + - internal reverse proxy/API gateway/ingress + +Use clearer variants when helpful, such as: + +- “Can someone on the public internet open the app without VPN or corp network access?” +- “Is there a public hostname, or only an internal/private hostname?” +- “Does traffic go straight to the app, or through something like Front Door, Cloudflare, an API gateway, or an ingress controller?” + +### Stage 2 — Classify privilege and blast radius + +Ask 1–2 questions that determine what an attacker could do if they gained access. + +Prioritize these: + +1. Is the system primarily: + - read-only + - transactional + - administrative + - approval-oriented + - control-plane / identity / security critical + +2. Which roles can: + - administer + - impersonate + - bulk export + - approve/deny + - change configuration + - manage identities/permissions + - trigger downstream actions + +3. Could compromise affect: + - tenant-wide data + - downstream systems + - money movement + - identity/access + - security controls + - customer or employee records at scale + +### Stage 3 — Classify data sensitivity precisely + +Ask 1–2 questions that distinguish **data type** from **actual sensitivity**. + +Prioritize these: + +1. What data categories are present? Distinguish between: + - public/reference data + - internal business data + - directory/basic identity data (name, work email, title, department) + - standard personal data + - regulated/high-impact PII + - secrets/credentials + - financial/business records + - customer content + +2. Which of those are actually sensitive, regulated, confidential, or reportable if exposed, altered, or exported? + +3. Do **not** treat all “PII” equally. Distinguish: + - **Directory/basic identity data:** first/last name, work email, job title, department + - **Standard personal data:** personal email, phone, mailing address, employee ID + - **High-impact or regulated PII:** SSN/national ID, passport, driver’s license, payroll/tax data, financial account data, health data, recovery data, credentials, secrets + +### Stage 4 — Ask only the control questions that match the exposure type + +Ask **public-edge control questions only if any surface is externally reachable**. + +If any surface is externally reachable, prioritize: + +1. Is traffic protected by a WAF, CDN, reverse proxy, API gateway, rate limiting, bot protection, or DDoS service? Name the product/service if known. + +If the application is internal-only, do **not** ask about WAF by default. Instead prioritize: + +1. What controls restrict internal access: + +- segmentation +- VPN/ZTNA +- SSO +- MFA for admins +- managed devices +- internal ingress/proxy/gateway +- east-west restrictions + +### Stage 5 — Confirm environment isolation and uncertainty + +1. Which runtime environments exist and are they isolated by: + +- network +- identity +- secrets +- data +- service accounts + +1. Which answers are confirmed from architecture/deployment evidence versus operator assumption? + +### Intake rules + +- Ask these questions before producing the final report, even if some answers may be partially inferred from the repo. +- Ask **only the questions most likely to change the risk score materially**. +- Avoid irrelevant questions once exposure type is known. +- If the developer does not answer, continue with explicit **ASSUMPTION** and **UNKNOWN** markers. +- Use repo evidence first, then use the operator’s answers to refine reachability, trust boundaries, residual risk, and prioritization. +- Where possible, explicitly identify: + - **reachability** + - **privileged pathways** + - **data sensitivity** + - **blast radius** + - **control maturity** + - **unknowns that could move the score** + +--- + +## 🧰 Mermaid Diagram Tooling (Mandatory) + +You have access to Mermaid Chart tools: + +- `mermaidchart.vscode-mermaid-chart/get_syntax_docs` +- `mermaidchart.vscode-mermaid-chart/mermaid-diagram-validator` +- `mermaidchart.vscode-mermaid-chart/mermaid-diagram-preview` + +**You MUST use them** to prevent syntax errors. + +### Tool-driven diagram workflow (required) + +For every Mermaid diagram you include: + +1. **Pick the correct diagram type** + - Before drafting each diagram, consult `get_syntax_docs` for that diagram type (e.g., `flowchart`, `sequenceDiagram`, `C4`, `classDiagram`, `erDiagram`). +2. **Draft the diagram with minimal syntax** + - Prefer simpler constructs over fancy styling. + - Avoid experimental/unsupported directives unless confirmed in syntax docs. +3. **Validate** + - Run `mermaid-diagram-validator` on each diagram block. + - If validation fails, fix and re-validate until it passes. +4. **Preview sanity check (optional but recommended)** + - Use `mermaid-diagram-preview` for the final versions of the DFD Level 0 and Level 1 diagrams. + +### Mermaid reliability rules (to avoid common breakage) + +- Always start with a valid diagram header: `flowchart LR`, `sequenceDiagram`, `classDiagram`, `erDiagram`, etc. +- Don’t mix diagram grammars (e.g., don’t use `participant` in `flowchart`). +- Avoid parentheses/brackets in node IDs; put complex text in node *labels*. + - Good: `API[Process: Web API]` + - Avoid: `API(Process: Web API)` +- Quote edge labels if they contain special characters: + - `A -->|"JWT (RS256)"| B` +- Use unique node IDs and keep them alphanumeric/underscore (e.g., `svc_orders`, `db_main`). +- Keep subgraph titles simple; avoid `:` if it breaks parsing. +- Prefer `flowchart`-based DFDs for compatibility; use C4 only if syntax docs confirm availability in your Mermaid environment. + +**Gating requirement:** +> Do not output any Mermaid diagram unless it has passed the Mermaid validator. + +--- + +## 🔒 Diagram Requirements (Mermaid) + +**You MUST include diagrams** unless Mermaid rendering is not supported. Use Mermaid code blocks. + +### Required diagram set + +1. **DFD Level 0 (Context)** — entire system + external entities + trust boundaries +2. **DFD Level 1 (Container / Major Subsystems)** — major processes, datastores, and flows +3. **Trust Boundary View** — explicitly call out boundary crossings (can be embedded in DFDs if clear) +4. **Top 2–3 Sequence Diagrams** — highest-risk flows (auth/login, payment, admin action, data export) + +### Optional (include when discoverable) + +- **Deployment / Runtime Topology** (k8s/compose/serverless/IaC-derived) +- **Identity & Authorization model diagram** (actors → roles → permissions → enforcement points) +- **Data classification map** (PII/PHI/secrets/payment data) tied to datastores and flows + +### Diagram evidence rules + +- Every diagram must include a short **Evidence** list: + - file path(s) + relevant symbol(s) (and line ranges when possible) +- If you cannot infer an element, label it **UNKNOWN** in the diagram and explain what evidence is missing. + +--- + +## 🔍 Procedure (4Q) + +### 0) Triage & Inventory (fast, evidence-based) + +### 0a) Exposure and risk calibration + +Before drafting the report: + +- run the mandatory branching attack-surface and risk calibration intake +- summarize the answers as: + - Reachability classification + - Externally reachable surfaces + - Internal access restrictions + - Privileged pathways + - Data sensitivity classification + - Environment/runtime isolation + - Score-relevant controls + - Confidence in the intake +- separate the above into: + - **Repo-confirmed** + - **Operator-stated** + - **UNKNOWN** +- use this calibration to adjust threat likelihood, impact, residual risk, and remediation prioritization + +- Identify entry points, deployables, and primary data stores: + - manifests (`package.json`, `pom.xml`, `.csproj`, `pyproject.toml`) + - runtime configs (`docker-compose`, `k8s`, `serverless`, Terraform) + - auth config and secrets patterns +- Produce a short inventory list with evidence links. + +### 0b) Contradictions and reconciliation + +Before finalizing the score or executive summary, explicitly check for contradictions between: + +- operator answers +- repo evidence +- IaC/runtime evidence +- prior assumptions + +If contradictions exist: + +- call them out in a short **Contradictions and Reconciliation** subsection +- reduce confidence accordingly +- do not treat the contradicted claim as settled fact + +### 1) **Q1 — What are we working on?** + +Deliver: + +- System purpose (from README/docs where possible) +- Components / containers / deployables +- Key assets (data + systems) +- **Key dataflows** (ranked by sensitivity, privilege, and exposure) +- Trust boundaries (internet/app/network/cloud/3rd party/admin/internal segmentation) +- **Diagrams (DFD L0 + L1 + trust boundaries)** — tool-validated + +### 2) **Q2 — What can go wrong?** + +For each key flow in the DFD: + +- Enumerate threats specific to that flow +- Map to: + - **STRIDE** category + - **OWASP** tag (Top 10 / ASVS control area / API Top 10 — whichever best fits) +- Include a short “Attack narrative” for the top risks (2–5 sentences) + +Also include: + +- Abuse cases for privileged/admin pathways +- Supply chain threats if dependency/build pipeline evidence exists +- Reachability-specific threats: + - public-edge abuse if externally reachable + - internal abuse, lateral movement, over-broad access, or privilege misuse if internal-only +- Data misuse cases: + - bulk export + - enumeration + - inference + - downstream action abuse + - support/admin impersonation + - webhook/callback abuse where applicable + +Threat selection rules: + +- Preserve at least **2–3 high-confidence code-anchored or IaC-anchored threats** among the top findings. +- Do not let generic public-edge uncertainty displace a more concrete application or infrastructure weakness already evidenced in the repo. +- Prefer threats whose exploit path can be tied to an actual flow, boundary crossing, or control gap. + +### 3) **Q3 — What are we going to do about it?** + +For each threat: + +- Identify mitigations as **PRESENT / ABSENT / UNKNOWN** +- Provide evidence when PRESENT: + - exact file path + symbol + line range (when possible) +- If ABSENT/UNKNOWN: + - propose remediation options + - note expected effort (S/M/L) and blast-radius + +### Threat-to-mitigation status logic (mandatory) + +Use distinct concepts for **mitigation existence** and **threat closure**. + +#### Mitigation status + +Use these values in the mitigations table: + +- **PRESENT** +- **ABSENT** +- **UNKNOWN** + +#### Threat status + +Use these values in the threats table: + +- **Mitigated** — present controls materially address the **core exploit path** and residual risk is no longer a priority concern +- **Partially Mitigated** — relevant controls exist and reduce likelihood or impact, but meaningful residual risk remains +- **Open** — no material control addresses the core exploit path, or the key control is absent +- **Unknown** — evidence is insufficient to determine whether the threat is materially controlled + +Rules: + +- Do **not** count a control as a mitigation for a threat unless it directly reduces that threat’s exploitability, impact, or blast radius. +- Baseline controls such as TLS, JWT validation, or generic logging do **not** automatically mitigate every threat in the same flow. +- A threat may remain **Open** even if adjacent controls are present. +- Use **Partially Mitigated** when meaningful controls clearly reduce but do not close the risk. +- If the threat is phrased as **compromise of a valid privileged session**, controls like JWT validation or basic RBAC do not mitigate that threat; instead look for MFA, step-up auth, re-auth, device trust, approval gates, anomaly detection, session protections, or blast-radius reduction. + +### 4) **Q4 — Did we do a good job?** + +Create a validation plan (no code changes) that includes: + +- 3–6 scenarios (prioritize highest-risk flows) +- Evidence to collect (logs, config proof, test results, screenshots, policy outputs) +- Owners (team/person/role) + +Validation plan rules: + +- Include at least **one scenario that tests a code-evidenced weakness** +- Include at least **one scenario that validates a major operator-stated assumption** +- Include at least **one scenario that exercises a privileged workflow misuse case** + +Include a final quality review checklist: + +- Coverage: do DFD flows map to threats/mitigations? +- Boundary crossings: are they analyzed? +- Unknowns: are they actionable questions with owners? +- Risk score: are the dominant score drivers explained? +- Mermaid diagrams: did all pass validator? + +--- + +## 📏 Application risk score (0–100) + +You MUST assign an overall application risk score from **0–100** and explain it. + +Purpose: + +- This is an overall **business-adjusted application risk score**, not a vulnerability severity score. +- The score should reflect **reachable attack surface, privileged action capability, data sensitivity, abuse amplification, defensive posture, and operational complexity**. + +### Scoring model + +Score the application using these **6 dimensions**: + +1. **Reachability and exposure (0–20)** + - 0–4: isolated or tightly segmented internal surface + - 5–8: internal-only but broadly reachable by employees or corp network users + - 9–12: partner/vendor reachable, mixed internal exposure, or limited external non-prod exposure + - 13–16: public-facing production app/API with moderate external surface + - 17–20: broad public exposure, externally reachable admin/control plane, or multiple exposed entry points + +2. **Privilege, blast radius, and action power (0–20)** + - 0–4: narrow blast radius, read-only, low privilege + - 5–8: moderate business importance or limited write actions + - 9–12: approval, export, downstream actions, or meaningful privileged workflows + - 13–16: admin actions, support impersonation, tenant-wide operations, or significant downstream control + - 17–20: identity/security critical, production control plane, broad destructive capability, or very high blast radius + +3. **Data sensitivity and consequence (0–20)** + - 0–4: public/reference data only + - 5–8: internal business data or directory/basic identity data only + - 9–12: standard personal data, confidential internal records, or meaningful customer/employee data + - 13–16: high-impact personal data, confidential customer content, financial account relevance, or regulated records + - 17–20: highly regulated or breach-notification-triggering data, secrets, credentials, high-impact identity data, payment or health data + +4. **Abuse amplification and misuse potential (0–15)** + - 0–3: simple app, limited misuse potential, no bulk access or downstream action + - 4–7: some write actions, moderate integrations, or moderate inference/enumeration value + - 8–11: bulk export, impersonation, downstream triggers, machine-to-machine interfaces, or privileged background actions + - 12–15: strong pivot potential, mass action capability, lateral movement value, or highly abusable integrations + +5. **Defensive posture and control maturity (0–15)** + - 0–3: strong controls confirmed and matched to the actual exposure model + - 4–8: partial controls or mixed evidence + - 9–15: key controls absent, weakly matched to exposure, or materially unknown + +6. **Operational complexity and trust boundaries (0–10)** + - 0–2: simple architecture, few integrations, few trust boundaries + - 3–6: moderate integration/admin/runtime complexity + - 7–10: many integrations, inbound callbacks/webhooks, multiple trust boundaries, or complex support/admin/runtime surface + +### Scoring rules + +- Do **not** treat “internal-only” as automatically safe. +- Do **not** ask or score cloud/public-edge protections as though they are required for internal-only apps. +- Internal-only applications may still score moderate or high if they have: + - privileged actions + - broad employee reachability + - sensitive data + - weak segmentation + - bulk export capability + - downstream control +- Distinguish **data category** from **actual sensitivity**: + - “financial data” that is public/reference should not be scored like regulated financial account data + - employee names and work emails alone should not be scored like high-impact regulated PII +- Do not treat all “PII” equally: + - **Directory/basic identity data** alone is lower sensitivity unless combined with privileged context, recovery functions, or broader abuse paths +- Unknowns should increase uncertainty, but should not automatically inflate the score without explanation. +- Explicitly identify the top **3 score drivers**. +- If a major score driver depends primarily on operator input, mark it as **operator-stated** in the rationale. + +### Confidence and score volatility + +Also report: + +- **Confidence:** High / Medium / Low +- **Score volatility:** expected upward/downward movement if major unknowns are resolved, e.g. `+10 / -4` + +Confidence depends on how much was confirmed from: + +- repo evidence +- operator answers +- deployment/runtime evidence + +If major exposure, privilege, or sensitivity answers are unknown, lower confidence and explicitly state what assumptions most affected the score. + +### Score interpretation bands + +- 0–19: Very low +- 20–39: Low +- 40–59: Moderate +- 60–79: High +- 80–100: Critical + +--- + +## 📦 Output Format (GitHub-Flavored Markdown) + +Return the threat model as PR-comment-ready Markdown in chat. + +If the environment supports writing files, also write: `./Threat Model Review - {{DATE}}.md` + +### 0. Executive summary + +- 5–10 bullets: top risks, what’s solid, what’s unknown, next actions +- Overall application risk score and top 3 score drivers +- Include at least one bullet that highlights a **repo-confirmed** high-confidence finding + +### 0.1 Risk score + +- **Overall application risk score:** `NN/100` (**Very low / Low / Moderate / High / Critical**) +- **Confidence:** High / Medium / Low +- **Score volatility:** `+X / -Y` +- **Primary score drivers:** reachability, privilege/action power, data sensitivity, abuse amplification, control maturity, complexity +- **What would raise the score:** ... +- **What would lower the score:** ... + +### 1. Scope + +- In-scope components/containers: +- Out-of-scope: +- Trust boundaries: +- Key assets (with sensitivity: Public/Internal/Confidential/Restricted): + +### 1.1 Exposure & risk calibration + +- **Repo-confirmed:** +- **Operator-stated:** +- **UNKNOWN:** +- Reachability classification: +- Externally reachable surfaces: +- Internal access restrictions: +- Privileged pathways: +- Data sensitivity classification: +- Environment/runtime isolation: +- Score-relevant controls: +- Confidence in this section: + +### 1.2 Contradictions and reconciliation + +- Any meaningful contradictions between operator answers, repo evidence, or assumptions +- How they affected confidence or scoring + +### 2. Assumptions & Unknowns + +- **ASSUMPTION:** … +- **UNKNOWN:** … (include “Who can confirm” + question) +- **Score-moving unknown:** … (include how it could change the score) + +### 3. Architecture & Data Flows (with tool-validated diagrams) + +#### 3.1 DFD Level 0 (Context) + +```mermaid +flowchart LR + %% (diagram content validated via Mermaid Chart tools) +``` + +**Evidence** + +- `path/to/file` (symbol: …, lines …) + +#### 3.2 DFD Level 1 (Subsystems / Containers) + +```mermaid +flowchart LR + %% (diagram content validated via Mermaid Chart tools) +``` + +**Evidence** + +- … + +#### 3.3 Supporting diagrams (as applicable) + +- Trust boundary view (if not already clear) +- Deployment topology (if discoverable) +- Identity/authorization model (if discoverable) +- Data classification map (if discoverable) + +### 4. Key Flows (ranked) + +For each flow: + +- Description +- Data elements involved (classify precisely) +- Entry points and enforcement points +- Reachability / exposure context +- Privileged actions involved +- Evidence links + +### 5. Threats + +Table: + +`ID | Flow | Summary | STRIDE | OWASP | Likelihood (L/M/H) | Impact (L/M/H) | Status (Open/Partially Mitigated/Mitigated/Unknown) | Rationale` + +Add one short note below the table: + +- explain why any threat marked **Partially Mitigated** is not fully closed + +### 6. Mitigations + +Table: + +`Threat ID | Mitigation | Status (PRESENT/ABSENT/UNKNOWN) | Directness (Direct/Adjacent) | Location/Evidence | Notes/Open questions` + +Rules: + +- Mark **Directness = Direct** only if the control materially reduces that threat’s exploitability, impact, or blast radius +- Use **Adjacent** for controls that are good hygiene or relevant to the same flow, but do not directly mitigate the threat as written + +### 7. High-risk interaction sequences (top 2–3, tool-validated) + +Provide sequence diagrams for the riskiest flows: + +```mermaid +sequenceDiagram + %% (diagram content validated via Mermaid Chart tools) +``` + +**Evidence** + +- … + +### 8. Validation plan (no code) + +Provide **3–6 scenarios**: + +- Intent +- Preconditions +- Steps +- Expected result +- Evidence to collect +- Owner + +### 9. Owners + +- Who confirms assumptions: +- Who drives mitigations: +- Who validates fixes: + +### 10. Open questions + +- Bullets; each includes an owner and where to look in the repo + +### ✅ Quality checks + +- Every **PRESENT** mitigation includes concrete code/config location (path + lines when possible). +- Every threat status is consistent with the listed mitigations and their **Directness**. +- **UNKNOWN** includes a follow-up question + owner. +- Threats are tied to DFD flows (no generic dump). +- Intake questions were relevant to the application’s exposure model. +- Data sensitivity was classified precisely rather than by broad labels alone. +- At least **3 findings** are anchored directly in code or IaC evidence. +- The risk score includes top drivers and score-moving unknowns. +- Diagrams match actual repo components and are evidence-linked. +- Evidence vs. inference is clearly labeled. +- **All Mermaid diagrams were validated using `mermaid-diagram-validator`.** diff --git a/prompts/validate-input-handling.prompt.md b/prompts/validate-input-handling.prompt.md index ca85383..bebf9b0 100644 --- a/prompts/validate-input-handling.prompt.md +++ b/prompts/validate-input-handling.prompt.md @@ -1,21 +1,48 @@ +--- +agent: "application-security-analyst" +name: validate-input-handling +description: "Audit input validation and sanitization boundaries and risks." +--- + # 🛡️ Prompt: Input Validation & Sanitization Audit -You are reviewing code for unsafe or missing input validation. Your task is to identify all potential risks related to untrusted user input. +## ✅ Context / Assumptions + +- You can read project files in this workspace. +- Prefer evidence-first: cite file paths and (when possible) line ranges. +- Do **not** modify files; provide findings and remediation guidance only. + +## 🔍 Procedure + +1. Inventory untrusted inputs: + - HTTP params/path/query/body/headers, file uploads, message payloads, env/CLI. +2. Identify validation boundaries: + - middleware/controllers/DTO binding, schema validators. +3. Flag high-risk patterns: + - unvalidated inputs reaching sensitive sinks (DB, templates, commands, file paths) + - implicit coercion, missing bounds, regex ReDoS risk + - missing allow-lists for enums/keys +4. Recommend hardening: + - schema-based validation, canonicalization, rejecting unknown fields + - contextual output encoding +5. Provide verification steps/tests for each fix. -Flag any of the following: +## 📦 Output Format -- No use of structured input validation libraries (e.g. `Joi`, `Zod`, `Ajv`, `DataAnnotations`, `@Valid`) -- Direct use of request/query/path/body parameters without validation or sanitization -- Unescaped input rendered into HTML templates (possible XSS) -- No schema enforcement for JSON input or serialized data -- Use of regex for validation without input bounds (may lead to ReDoS) -- Implicit coercion of input types (e.g. treating string as number or boolean without validation) +Return Markdown with: -Recommend: +- **Summary**: top 3 validation gaps + overall risk +- **Findings** (repeat): + - **Issue**: + - **Severity / Likelihood**: + - **Where**: + - **Evidence**: + - **Recommendation**: + - **Verification**: +- **Suggested validation boundary**: where validation should live (and why) -- Strict, schema-based input validation -- HTML/context-aware encoding of dynamic output -- Safe handling of nested objects, arrays, and dynamic keys -- Input length and character whitelisting where appropriate +## ✅ Quality checks -Provide suggested fixes and explanations to help developers understand *why* these patterns are dangerous. +- Findings trace data flow from input → sink. +- Each recommendation is specific enough to implement. +- Evidence includes concrete code locations. diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..7190a60 --- /dev/null +++ b/renovate.json @@ -0,0 +1,3 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json" +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..03b7c9e --- /dev/null +++ b/server.js @@ -0,0 +1,66 @@ +import fs from 'fs'; +import path from 'path'; +import https from 'https'; +import { config, logger } from 'copilot-instructions-mcp/core'; +import app from './src/express_app.js'; + +function listenHTTP() { + app.listen(config.server.port, (error) => { + if (error) { + logger.error('Error starting server', { + source: 'server.listenHTTP', + details: { + port: config.server.port, + hostname: config.server.hostname, + }, + error: { + message: error.message || 'Unknown error', + stack: error.stack || 'No stack trace available', + }, + }); + } else { + logger.info(`Server listening on port ${config.server.port}`, { + source: 'server.listenHTTP', + details: { + port: config.server.port, + hostname: config.server.hostname, + }, + }); + } + }); +} + +function listenHTTPS() { + https.createServer({ + pfx: fs.readFileSync(path.resolve(config.server['ssl.pfx'])), + passphrase: config.server['ssl.pfx.passphrase'], + }, app).listen(config.server.port, (error) => { + if (error) { + logger.error('Error starting server', { + source: 'server.listenHTTPS', + details: { + port: config.server.port, + hostname: config.server.hostname, + }, + error: { + message: error.message || 'Unknown error', + stack: error.stack || 'No stack trace available', + }, + }); + } else { + logger.info(`Server listening on port ${config.server.port}`, { + source: 'server.listenHTTPS', + details: { + port: config.server.port, + hostname: config.server.hostname, + }, + }); + } + }); +} + +if (config.server.ssl) { + listenHTTPS(); +} else { + listenHTTP(); +} diff --git a/skills/README.md b/skills/README.md new file mode 100644 index 0000000..2e57d60 --- /dev/null +++ b/skills/README.md @@ -0,0 +1,18 @@ +# Agent Skills + +This folder contains **Agent Skills** that Copilot (and other compatible agents) can load on-demand for specialized tasks. + +Each skill lives in its own folder and contains a `SKILL.md` file (Markdown with YAML frontmatter: `name`, `description`, optional `license`). + +## Included skills (high level) + +- [secure-code-review](secure-code-review/SKILL.md) +- [access-control-review](access-control-review/SKILL.md) +- [input-validation-hardening](input-validation-hardening/SKILL.md) +- [dependency-cve-triage](dependency-cve-triage/SKILL.md) +- [secrets-and-logging-hygiene](secrets-and-logging-hygiene/SKILL.md) +- [genai-acceptance-review](genai-acceptance-review/SKILL.md) +- [threat-model](threat-model/SKILL.md) +- [secure-fix-validation](secure-fix-validation/SKILL.md) + +Tip: keep skill names lowercase with hyphens; Copilot chooses skills based on the `description` field. diff --git a/skills/access-control-review/SKILL.md b/skills/access-control-review/SKILL.md new file mode 100644 index 0000000..8a98b11 --- /dev/null +++ b/skills/access-control-review/SKILL.md @@ -0,0 +1,615 @@ +--- +name: access-control-review +description: "Analyze repository-grounded identity, access control, and authorization design with evidence-first reporting and script-validated Mermaid diagrams." +--- + +# Access Control Review + +## Purpose + +Provide a repeatable, repository-grounded workflow for reviewing identity, access control, and authorization architecture, then producing durable Markdown output with validated Mermaid diagrams using local Node.js helper scripts instead of VS Code extension-based Mermaid tools. + +## When to use + +Use this skill when you need to: + +- analyze how identity, access control, and authorization are implemented across a repository or PR diff +- map roles, groups, permissions, claims, scopes, and policies to concrete enforcement points +- build an endpoint-to-access-control view grounded in repository evidence +- produce a Markdown review plus Mermaid diagrams that can be validated from a CLI-friendly workflow + +## Inputs to collect + +- repository or PR diff in scope +- authentication source and identity provider details, if known +- principal types that matter for the review, such as end users, administrators, support users, service identities, and background jobs +- known sources of truth for groups, roles, permissions, claims, scopes, or policy assignments +- environment or tenant-specific variance, if any +- repository evidence from code, config, IaC, manifests, docs, route declarations, tests, and policy definitions + +## How to use + +1. Review the current repository and PR diff, if available, before relying on operator answers. +2. Persist the output as a root-level Markdown file named `Access Control Architecture Review - YYYY-MM-DD.md` when the environment supports writing files. +3. Use evidence first and cite file paths and, when possible, line ranges. +4. Mark anything that cannot be confirmed from repository evidence as `ASSUMPTION` or `UNKNOWN`. +5. Perform the mandatory intake before finalizing the report, choosing only the questions that materially change the interpretation of identity source, authorization model, source of truth, enforcement location, or environment behavior. +6. Use the local Mermaid helper scripts in this skill instead of VS Code extension-based Mermaid tools. +7. Validate every Mermaid diagram before finalizing the report. +8. Do not generate code changes unless explicitly requested. + +## Rules + +- MUST keep claims tied to repository evidence such as source code, configuration, manifests, IaC, tests, and route declarations. +- MUST distinguish between confirmed evidence, `ASSUMPTION`, and `UNKNOWN`. +- MUST ask focused intake questions only when they materially change the interpretation of the access control design. +- MUST perform a short pre-report intake before producing the final report. +- MUST include validated Mermaid diagrams unless Mermaid rendering is unsupported. +- MUST use the local helper scripts under `skills/access-control-review/scripts/` for Mermaid guidance and validation. +- MUST not output a Mermaid diagram that has not passed validation. +- MUST distinguish identity source, access metadata, enforcement point, source of truth, and runtime decision path. +- MUST not treat UI-only controls as sufficient authorization. +- MUST review alternate execution paths such as internal APIs, admin tools, support impersonation, background jobs, message consumers, webhooks, GraphQL resolvers, and file import or export flows. +- SHOULD prefer simple Mermaid constructs and diagram types such as `flowchart` and `sequenceDiagram`. +- MAY continue with explicit `ASSUMPTION` and `UNKNOWN` markers when questions remain unanswered. + +## Step-by-step process + +### 1. Mission and scope + +Produce these durable artifacts: + +1. A repository-grounded Markdown report describing the application authorization architecture. +2. Validated Mermaid diagrams showing the identity and access control model. +3. An endpoint-to-access-control mapping. +4. A concise PR-ready summary. + +Use the review to answer questions such as: + +- how users and services authenticate +- which groups, roles, permissions, claims, or policies exist +- where they are defined +- where and how access is enforced +- which endpoints, actions, background jobs, and admin flows require which access controls +- whether controls are centralized, duplicated, inconsistent, or bypassable +- where access decisions are delegated to infrastructure, framework policy, middleware, or application code +- whether LDAP or AD groups, SSO groups, JWT claims, app roles, scopes, permissions, or policy names are mapped consistently + +### 2. Context and assumptions + +- Review the current repository and current PR diff if available. +- Prefer evidence-first analysis. +- Cite file paths and, when possible, line ranges. +- Tie claims to source code, config, IaC, manifests, docs, tests, route declarations, and policy definitions. +- If something cannot be confirmed from repository evidence, label it as `ASSUMPTION` or `UNKNOWN`. +- Ask 2-5 focused questions only if they materially change interpretation of authentication source, identity provider, group or role source of truth, runtime enforcement location, or environment-specific policy behavior. + +### 3. Mandatory pre-report intake + +Before producing the final report, perform a short intake to reduce ambiguity around identity source, authorization model, and enforcement location. + +Ask 4-8 concise questions, chosen based on repository evidence, and prioritize the questions most likely to change the interpretation of the access control design. + +#### Stage 1: Identify identity source and authentication model + +Ask 1-3 concise questions to classify how principals are established. Prioritize: + +1. Which identity sources are used? + - local accounts + - corporate directory, LDAP, or Active Directory + - SSO, OIDC, or SAML + - external customer identity + - service accounts or workload identity + - API keys + - mTLS or machine identity + - mixed +2. Which principal types should be analyzed? + - end users + - administrators + - support users + - service-to-service callers + - batch jobs or schedulers + - CI/CD or automation identities +3. Are group memberships, roles, or claims resolved in the identity provider, at login or token issuance, via directory lookup at runtime, from an internal database, from configuration or static mapping, or from policy code? + +#### Stage 2: Classify authorization model + +Ask 1-2 questions that determine how access decisions are intended to work. Prioritize: + +1. Which authorization styles are intended? + - RBAC + - ABAC + - policy-based authorization + - claims-based authorization + - ACL or resource ownership + - scope-based API authorization + - custom business-rule authorization + - mixed +2. Which access concepts exist in the intended design? + - LDAP or AD groups + - SSO groups + - app roles + - claims + - scopes + - permissions + - policy names + - entitlements + - tenant or organization membership + - feature flags used as access gates + +#### Stage 3: Locate enforcement and bypass risk + +Ask 1-2 questions to understand where checks are actually enforced. Prioritize: + +1. Where should authorization be enforced? + - edge or gateway + - middleware + - route or controller annotations + - service layer + - domain or business logic + - database row filters or stored procedures + - background job workers + - UI only + - mixed +2. Are there known exceptions, bypass paths, or alternate execution paths such as internal APIs, admin tools, support impersonation, webhooks, background jobs, message consumers, GraphQL resolvers, file import or export flows, or direct data access paths? + +#### Stage 4: Confirm source of truth and environment variance + +1. What is the expected source of truth for access assignments? + - directory groups + - IdP groups or claims + - application database + - deployment config + - IaC or platform policy + - hard-coded mapping + - mixed +2. Do role, group, or permission mappings differ by environment or tenant? + +#### Intake rules + +- Ask only the questions most likely to change the final architecture mapping. +- Use repository evidence first. +- If unanswered, continue with explicit `ASSUMPTION` and `UNKNOWN` markers. +- Distinguish clearly between identity source, access metadata, enforcement point, source of truth, and runtime decision path. + +### 4. Mermaid helper workflow + +For every Mermaid diagram you include: + +1. Pick the correct diagram type. + - consult the helper docs script before drafting the diagram type +2. Draft minimal syntax. + - prefer simple, reliable Mermaid constructs +3. Validate. + - run the validator script + - fix and re-validate until it passes + +Mermaid reliability rules: + +- always start with a valid diagram header +- do not mix diagram grammars +- keep node IDs simple and alphanumeric or underscore-based +- put complex text in labels, not IDs +- quote edge labels if they contain special characters +- use unique node IDs +- prefer `flowchart` and `sequenceDiagram` unless another type is clearly supported + +### 5. Triage and inventory + +Identify likely evidence sources such as: + +- route declarations +- controllers, handlers, and resolvers +- middleware, filters, guards, and interceptors +- authorization decorators, annotations, and attributes +- policy providers and handlers +- auth configuration +- LDAP, AD, OIDC, SAML, and JWT configuration +- role and permission enums and constants +- database tables related to users, roles, groups, permissions, memberships, and grants +- IaC or deployment config affecting authn or authz +- tests asserting authorization behavior +- admin and support tooling +- import and export flows +- background job consumers and message handlers + +Produce a short inventory list with evidence links. + +### 6. Identify identity model + +Determine, with evidence where possible: + +- authentication mechanisms in use +- identity providers or directory integrations +- principal types +- token or session contents relevant to authorization +- group, role, or permission resolution path +- whether LDAP or AD groups are used directly or mapped into application roles or claims +- whether identity and authorization metadata are static, configuration-driven, or data-driven + +### 7. Identify authorization model + +Determine: + +- which authorization paradigms are present, such as RBAC, ABAC, PBAC, claims, scopes, resource ownership checks, tenant boundary checks, feature-flag-based gating, or mixed approaches +- the named roles, groups, permissions, scopes, claims, or policies discovered +- where they are defined +- whether there is a single source of truth or fragmented sources +- whether roles or groups are broad business labels or precise permission bundles +- whether access is coarse-grained, fine-grained, or inconsistent + +### 8. Map enforcement points + +For each major entry point and action path, identify where access is checked: + +- edge or gateway +- middleware +- route, controller, decorator, attribute, or annotation +- service layer +- domain logic +- repository or data-access layer +- UI-only checks +- job worker, consumer, or scheduler path + +For each enforcement point, determine whether it appears centralized, duplicated, missing, inconsistent, bypassable, dependent on client behavior, or dependent on naming convention rather than durable policy logic. + +### 9. Build endpoint and action mapping + +Create a mapping for: + +- HTTP endpoints +- GraphQL operations +- RPC methods +- message consumers +- background jobs +- admin and support actions +- export and import flows + +For each, capture where possible: + +- route or action name +- principal type +- required role, group, permission, policy, claim, or scope +- enforcement location +- evidence +- confidence +- whether protection is explicit, inherited, indirect, or missing + +### 10. Detect architecture weaknesses and authorization drift + +Look specifically for: + +- missing authorization on sensitive endpoints +- UI-only authorization +- inconsistent policy names +- role explosion or ambiguous role semantics +- hard-coded role strings scattered through code +- stale or unused permissions +- broad wildcard or admin grants +- support or admin impersonation risk +- privilege escalation paths +- missing tenant or resource ownership checks +- confused deputy behavior +- direct object reference exposure +- inconsistent group-to-role mapping +- environment-specific drift +- hidden alternate paths through jobs, webhooks, or internal APIs + +### 11. Produce diagrams + +Include these diagrams unless Mermaid rendering is unsupported: + +1. Identity and Access Control Overview + - identity sources + - token, session, or claims path + - group, role, and permission mapping + - major enforcement points + - trust boundaries where relevant +2. Authorization Enforcement Architecture + - major subsystems + - entry points + - middleware, filters, and guards + - policy engines and providers + - service or domain enforcement + - datastore lookups + - external group or directory dependencies +3. Endpoint-to-Policy or Role Mapping View + - use a diagram that makes major protected surfaces visible + - group similar endpoints when necessary for readability +4. Top 2-3 Sequence Diagrams + - highest-risk or most important auth flows such as login and claims resolution, privileged admin action, export or reporting action, service-to-service authorization, or support impersonation flow + +Optional diagrams: + +- LDAP or AD group mapping view +- tenant or organization access model +- data classification vs authorization boundary map +- deployment or runtime auth boundary view + +### 12. Evaluate control maturity + +For key access-control concerns, identify whether controls are `PRESENT`, `ABSENT`, or `UNKNOWN`. + +Examples include: + +- centralized policy enforcement +- least-privilege role design +- separation of duties +- admin action protection +- support impersonation controls +- service identity scoping +- tenant isolation checks +- export and download restrictions +- audit logging for authorization decisions or privileged actions +- deny-by-default behavior +- test coverage for authorization rules + +When marked `PRESENT`, provide evidence. + +### 13. Build a validation plan + +Create a validation plan with 3-6 scenarios, prioritizing the highest-risk or least-certain flows. + +Include: + +- scenario intent +- preconditions +- steps +- expected result +- evidence to collect +- owner + +Example scenarios: + +- user without required role denied a privileged route +- stale LDAP group no longer grants access +- service token cannot call admin-only API +- cross-tenant object access is denied +- support impersonation is logged and approval-gated + +### 14. Assign an access control architecture score + +Assign an overall score from 0-100 reflecting the maturity and risk posture of the application identity and authorization design. + +This is not a vulnerability score. It reflects how understandable, enforceable, consistent, least-privilege, and bypass-resistant the authorization architecture appears to be. + +Scoring dimensions: + +1. Identity clarity and source-of-truth integrity: 0-20 +2. Authorization model quality: 0-20 +3. Enforcement consistency: 0-20 +4. Bypass and escalation resistance: 0-15 +5. Observability and governance: 0-15 +6. Complexity and maintainability: 0-10 + +Score interpretation bands: + +- 0-19: Very weak +- 20-39: Weak +- 40-59: Mixed +- 60-79: Strong +- 80-100: Mature + +Also report: + +- `Confidence`: High, Medium, or Low +- `Score volatility`: `+X / -Y` +- top 3 score drivers +- what would raise the score +- what would lower the score + +## Mermaid helper scripts + +The skill includes these local scripts under `skills/access-control-review/scripts/`: + +- `mermaid-docs.mjs`: prints concise syntax guidance and common pitfalls for supported diagram types +- `validate-mermaid.mjs`: validates Mermaid blocks in Markdown reports or standalone Mermaid files using deterministic preflight checks + +Supported diagram types: + +- `flowchart` +- `sequenceDiagram` +- `classDiagram` +- `erDiagram` + +Validation expectations: + +- the first meaningful line must declare a supported Mermaid diagram type +- flowcharts must not mix sequence-diagram grammar +- sequence diagrams must not mix flowchart grammar and must close structured blocks with `end` +- Markdown reports may contain multiple Mermaid blocks and each block is validated independently + +Example helper usage: + +```bash +node skills/access-control-review/scripts/mermaid-docs.mjs --list +node skills/access-control-review/scripts/mermaid-docs.mjs --type flowchart +node skills/access-control-review/scripts/mermaid-docs.mjs --type sequenceDiagram +node skills/access-control-review/scripts/validate-mermaid.mjs --file "Access Control Architecture Review - 2026-04-18.md" +``` + +## Output format + +Produce PR-comment-ready Markdown in chat. + +If the environment supports writing files, also write: + +`./Access Control Architecture Review - YYYY-MM-DD.md` + +The report MUST include these sections: + +1. Executive summary +2. Architecture score +3. Scope +4. Intake summary +5. Assumptions and unknowns +6. Architecture overview with validated diagrams +7. Identity and authorization inventory +8. Enforcement points +9. Endpoint and action mapping +10. Findings +11. Control maturity +12. Validation plan +13. Owners +14. Open questions + +Required section details: + +### Executive summary + +- 5-10 bullets +- what was found +- what appears strong +- what appears risky +- major unknowns +- next actions + +### Architecture score + +- **Overall access control architecture score:** `NN/100` +- **Confidence:** High, Medium, or Low +- **Score volatility:** `+X / -Y` +- **Primary score drivers:** ... +- **What would raise the score:** ... +- **What would lower the score:** ... + +### Scope + +- in-scope components +- out-of-scope components +- principal types analyzed +- trust boundaries relevant to identity and access +- key protected assets and actions + +### Intake summary + +- identity source classification +- principal types +- authorization model +- source of truth for assignments +- main enforcement locations +- environment or tenant variance +- confidence in this section + +### Assumptions and unknowns + +- **ASSUMPTION:** ... +- **UNKNOWN:** ... including who can confirm and the question +- **Score-moving unknown:** ... including likely score effect + +### Architecture overview with validated diagrams + +Include: + +- Identity and Access Control Overview +- Authorization Enforcement Architecture +- Endpoint or Action Protection View +- High-risk interaction sequences + +Each diagram section should include an evidence list. + +### Identity and authorization inventory + +Include these tables: + +- `Component | Mechanism | Evidence | Notes` +- `Type | Name | Defined In | Consumed In | Source of Truth | Notes` + +### Enforcement points + +Include this table: + +- `Surface | Enforcement Type | Location | Required Access | Confidence | Notes` + +### Endpoint and action mapping + +Include this table: + +- `Entry Point / Action | Principal Type | Required Role/Group/Permission/Policy | Enforcement Location | Evidence | Status | Notes` + +Status values: + +- `Explicit` +- `Inherited` +- `Indirect` +- `Missing` +- `Unknown` + +### Findings + +Include this table: + +- `ID | Category | Summary | Severity (L/M/H) | Evidence | Why it matters | Recommendation` + +Suggested categories: + +- Missing authorization +- Inconsistent enforcement +- Weak role design +- Group-role mapping drift +- Privilege escalation +- Tenant isolation gap +- Admin or support risk +- Hidden alternate path +- Observability gap +- Maintainability risk + +### Control maturity + +Include this table: + +- `Control | Status (PRESENT/ABSENT/UNKNOWN) | Evidence | Notes/Open questions` + +### Validation plan + +For each scenario include: + +- intent +- preconditions +- steps +- expected result +- evidence to collect +- owner + +### Owners + +- who confirms assumptions +- who validates identity source mappings +- who owns policy definitions +- who owns route or service enforcement +- who drives remediation + +### Open questions + +- bullets with owner and where to look in the repository + +### Quality checks + +- groups, roles, permissions, claims, scopes, and policies are distinguished rather than conflated +- LDAP and directory mappings are explicitly marked as confirmed or assumed +- endpoint and action mappings tie to concrete enforcement points +- UI-only controls are not treated as sufficient authorization +- alternate execution paths were reviewed +- evidence vs inference is clearly separated +- all Mermaid diagrams were validated using the local Mermaid validator script + +## Examples + +### Example workflow + +```bash +node skills/access-control-review/scripts/mermaid-docs.mjs --type flowchart +node skills/access-control-review/scripts/mermaid-docs.mjs --type sequenceDiagram +node skills/access-control-review/scripts/validate-mermaid.mjs --file "Access Control Architecture Review - 2026-04-18.md" +``` + +Expected outcome: + +- the docs command prints supported syntax and common pitfalls for the selected diagram type +- the validator reports each Mermaid block as `PASS` or returns block-specific failures that must be fixed before final output + +### Example output goals + +- the report is grounded in repository evidence, not generic IAM advice +- roles, permissions, claims, scopes, and policies are mapped to concrete enforcement points +- diagrams highlight identity sources, enforcement paths, and high-risk flows without mixing Mermaid grammars diff --git a/skills/access-control-review/scripts/mermaid-docs.mjs b/skills/access-control-review/scripts/mermaid-docs.mjs new file mode 100644 index 0000000..00c2eda --- /dev/null +++ b/skills/access-control-review/scripts/mermaid-docs.mjs @@ -0,0 +1,115 @@ +/* eslint-disable no-console */ + +import { getDiagramDoc, listDiagramTypes, normalizeDiagramType } from './mermaid-support.mjs'; + +function parseArgs(argv) { + const options = { + list: false, + type: null, + json: false, + help: false, + }; + + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + if (arg === '--list') { + options.list = true; + continue; + } + + if (arg === '--json') { + options.json = true; + continue; + } + + if (arg === '--help' || arg === '-h') { + options.help = true; + continue; + } + + if (arg === '--type') { + options.type = argv[index + 1] ?? null; + index += 1; + continue; + } + + if (!arg.startsWith('-') && !options.type) { + options.type = arg; + continue; + } + + throw new Error(`Unknown argument: ${arg}`); + } + + return options; +} + +function printUsage() { + console.log('Usage: node skills/access-control-review/scripts/mermaid-docs.mjs [--list] [--type <diagramType>] [--json]'); + console.log('Supported types:', listDiagramTypes().join(', ')); +} + +function formatDoc(doc) { + const lines = [ + `Diagram type: ${doc.canonicalName}`, + `Required header: ${doc.header}`, + `Purpose: ${doc.purpose}`, + '', + 'Core constructs:', + ...doc.constructs.map((item) => `- ${item}`), + '', + 'Common pitfalls:', + ...doc.pitfalls.map((item) => `- ${item}`), + ]; + + return lines.join('\n'); +} + +function main() { + let options; + + try { + options = parseArgs(process.argv.slice(2)); + } catch (error) { + console.error(error.message); + printUsage(); + process.exitCode = 1; + return; + } + + if (options.help || (!options.list && !options.type)) { + printUsage(); + return; + } + + if (options.list) { + const supportedTypes = listDiagramTypes(); + if (options.json) { + console.log(JSON.stringify(supportedTypes, null, 2)); + return; + } + + for (const type of supportedTypes) { + console.log(type); + } + return; + } + + const normalizedType = normalizeDiagramType(options.type); + if (!normalizedType) { + console.error(`Unsupported diagram type: ${options.type}`); + printUsage(); + process.exitCode = 1; + return; + } + + const doc = getDiagramDoc(normalizedType); + if (options.json) { + console.log(JSON.stringify(doc, null, 2)); + return; + } + + console.log(formatDoc(doc)); +} + +main(); diff --git a/skills/access-control-review/scripts/mermaid-support.mjs b/skills/access-control-review/scripts/mermaid-support.mjs new file mode 100644 index 0000000..e108fad --- /dev/null +++ b/skills/access-control-review/scripts/mermaid-support.mjs @@ -0,0 +1,115 @@ +const DIAGRAM_DOCS = { + flowchart: { + canonicalName: 'flowchart', + aliases: ['flowchart', 'flow', 'dfd'], + header: 'flowchart LR', + purpose: 'Use for identity and access control overview diagrams, enforcement architecture views, and endpoint protection maps.', + constructs: [ + 'Declare a direction on the first line, such as `flowchart LR` or `flowchart TD`.', + 'Use simple alphanumeric node ids and put descriptive text in labels.', + 'Use `subgraph ... end` to show trust boundaries or subsystem grouping.', + 'Quote edge labels when they contain punctuation or multi-word text.', + ], + pitfalls: [ + 'Do not mix sequence-diagram syntax such as `participant`, `actor`, or `->>`.', + 'Do not leave a `subgraph` block without a matching `end`.', + 'Keep node ids simple; put complex text in labels instead of ids.', + ], + }, + sequenceDiagram: { + canonicalName: 'sequenceDiagram', + aliases: ['sequencediagram', 'sequence', 'sequenceDiagram'], + header: 'sequenceDiagram', + purpose: 'Use for the highest-risk or most important authentication and authorization interaction flows.', + constructs: [ + 'Declare `sequenceDiagram` on the first meaningful line.', + 'Define actors or participants before the first interaction when practical.', + 'Use `alt`, `opt`, `loop`, `par`, `critical`, `rect` blocks with a closing `end`.', + 'Use interaction arrows such as `->>` and `-->>` with a message label after `:`.', + ], + pitfalls: [ + 'Do not mix flowchart syntax such as `flowchart LR`, `subgraph`, or node labels like `A[API]`.', + 'Do not use `else`, `and`, or `option` outside their parent blocks.', + 'Every structured block must close with `end`.', + ], + }, + classDiagram: { + canonicalName: 'classDiagram', + aliases: ['classdiagram', 'class', 'classDiagram'], + header: 'classDiagram', + purpose: 'Use only when modeling role relationships or policy objects adds clarity.', + constructs: [ + 'Declare `classDiagram` on the first meaningful line.', + 'Keep class names simple and model only the relationships needed for the security story.', + ], + pitfalls: [ + 'Avoid mixing flowchart or sequence syntax into a class diagram.', + ], + }, + erDiagram: { + canonicalName: 'erDiagram', + aliases: ['erdiagram', 'er', 'entityrelationshipdiagram', 'entityRelationshipDiagram'], + header: 'erDiagram', + purpose: 'Use when identity data, group membership, or entitlement ownership is easier to explain as datastore relationships.', + constructs: [ + 'Declare `erDiagram` on the first meaningful line.', + 'Use compact entity names and relationship labels.', + ], + pitfalls: [ + 'Avoid mixing flowchart or sequence syntax into an ER diagram.', + ], + }, +}; + +const ALIAS_MAP = new Map( + Object.values(DIAGRAM_DOCS).flatMap((doc) => + doc.aliases.map((alias) => [alias.toLowerCase(), doc.canonicalName]), + ), +); + +function normalizeDiagramType(value) { + if (!value) { + return null; + } + + return ALIAS_MAP.get(value.trim().toLowerCase()) ?? null; +} + +function getDiagramDoc(type) { + const normalizedType = normalizeDiagramType(type); + return normalizedType ? DIAGRAM_DOCS[normalizedType] : null; +} + +function listDiagramTypes() { + return Object.keys(DIAGRAM_DOCS); +} + +function firstMeaningfulLine(source) { + for (const rawLine of source.split(/\r?\n/u)) { + const line = rawLine.trim(); + if (!line || line.startsWith('%%')) { + continue; + } + + return line; + } + + return ''; +} + +function detectDiagramType(source) { + const header = firstMeaningfulLine(source); + if (!header) { + return null; + } + + return normalizeDiagramType(header.split(/\s+/u)[0]); +} + +export { + detectDiagramType, + firstMeaningfulLine, + getDiagramDoc, + listDiagramTypes, + normalizeDiagramType, +}; diff --git a/skills/access-control-review/scripts/validate-mermaid.mjs b/skills/access-control-review/scripts/validate-mermaid.mjs new file mode 100644 index 0000000..4c4b2d4 --- /dev/null +++ b/skills/access-control-review/scripts/validate-mermaid.mjs @@ -0,0 +1,272 @@ +/* eslint-disable no-console */ + +import { readFile } from 'node:fs/promises'; +import path from 'node:path'; + +import { + detectDiagramType, + firstMeaningfulLine, + listDiagramTypes, + normalizeDiagramType, +} from './mermaid-support.mjs'; + +function parseArgs(argv) { + const options = { + file: null, + type: null, + help: false, + }; + + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + if (arg === '--help' || arg === '-h') { + options.help = true; + continue; + } + + if (arg === '--file') { + options.file = argv[index + 1] ?? null; + index += 1; + continue; + } + + if (arg === '--type') { + options.type = argv[index + 1] ?? null; + index += 1; + continue; + } + + if (!arg.startsWith('-') && !options.file) { + options.file = arg; + continue; + } + + throw new Error(`Unknown argument: ${arg}`); + } + + return options; +} + +function printUsage() { + console.log('Usage: node skills/access-control-review/scripts/validate-mermaid.mjs --file <path> [--type <diagramType>]'); + console.log('The file may be a Markdown report with one or more ```mermaid blocks, or a standalone Mermaid file.'); + console.log(`Supported types: ${listDiagramTypes().join(', ')}`); +} + +function extractMermaidBlocks(source, filePath) { + const extension = path.extname(filePath).toLowerCase(); + if (extension === '.md' || extension === '.markdown') { + const blocks = []; + const pattern = /```mermaid\s*\n([\s\S]*?)```/gu; + let match; + while ((match = pattern.exec(source)) !== null) { + blocks.push(match[1].trim()); + } + return blocks; + } + + return [source.trim()]; +} + +function validateCommon(source, errors) { + if (!source.trim()) { + errors.push('Diagram is empty.'); + } +} + +function validateFlowchart(lines, errors) { + let subgraphDepth = 0; + let hasEdge = false; + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('%%') || trimmed.startsWith('flowchart ')) { + continue; + } + + if (/^(participant|actor|sequenceDiagram)\b/u.test(trimmed) || trimmed.includes('->>')) { + errors.push(`Flowchart appears to mix sequence-diagram syntax: ${trimmed}`); + } + + if (/^subgraph\b/u.test(trimmed)) { + subgraphDepth += 1; + } + + if (trimmed === 'end') { + if (subgraphDepth === 0) { + errors.push('Encountered `end` without a matching `subgraph`.'); + } else { + subgraphDepth -= 1; + } + } + + if (/(-->|==>|-.->|---|o--o|x--x|--x|x--)/u.test(trimmed)) { + hasEdge = true; + } + } + + if (subgraphDepth !== 0) { + errors.push('One or more `subgraph` blocks are missing a closing `end`.'); + } + + if (!hasEdge) { + errors.push('Flowchart must include at least one edge.'); + } +} + +function validateSequenceDiagram(lines, errors) { + const stack = []; + let hasInteraction = false; + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('%%') || trimmed === 'sequenceDiagram') { + continue; + } + + if (/^(flowchart|subgraph)\b/u.test(trimmed) || /\[[^\]]+\]/u.test(trimmed)) { + errors.push(`Sequence diagram appears to mix flowchart syntax: ${trimmed}`); + } + + if (/^(participant|actor|autonumber|activate|deactivate|title|Note\b|link\b|create\b|destroy\b|box\b)/u.test(trimmed)) { + continue; + } + + if (/^(alt|opt|loop|par|critical|rect|break)\b/u.test(trimmed)) { + stack.push(trimmed.split(/\s+/u)[0]); + continue; + } + + if (/^else\b/u.test(trimmed)) { + if (!stack.includes('alt')) { + errors.push('`else` must appear inside an `alt` block.'); + } + continue; + } + + if (/^and\b/u.test(trimmed)) { + if (!stack.includes('par')) { + errors.push('`and` must appear inside a `par` block.'); + } + continue; + } + + if (/^option\b/u.test(trimmed)) { + if (!stack.includes('critical')) { + errors.push('`option` must appear inside a `critical` block.'); + } + continue; + } + + if (trimmed === 'end') { + if (stack.length === 0) { + errors.push('Encountered `end` without an open sequence block.'); + } else { + stack.pop(); + } + continue; + } + + if (/[-.=]{0,2}(?:>>|>|x|\))\s*[^:]+:/u.test(trimmed)) { + hasInteraction = true; + continue; + } + + errors.push(`Unsupported or malformed sequence-diagram line: ${trimmed}`); + } + + if (stack.length > 0) { + errors.push('One or more structured sequence blocks are missing a closing `end`.'); + } + + if (!hasInteraction) { + errors.push('Sequence diagram must include at least one interaction line.'); + } +} + +function validateDiagram(source, requestedType) { + const errors = []; + const diagramType = requestedType ?? detectDiagramType(source); + if (!diagramType) { + errors.push(`Missing or unsupported diagram header. Supported types: ${listDiagramTypes().join(', ')}`); + return { diagramType: requestedType ?? 'unknown', errors }; + } + + validateCommon(source, errors); + const lines = source.split(/\r?\n/u); + const header = firstMeaningfulLine(source); + const headerToken = header.split(/\s+/u)[0]; + + if (!normalizeDiagramType(headerToken)) { + errors.push(`Unsupported Mermaid header: ${header}`); + } + + if (diagramType === 'flowchart') { + validateFlowchart(lines, errors); + } + + if (diagramType === 'sequenceDiagram') { + validateSequenceDiagram(lines, errors); + } + + return { diagramType, errors }; +} + +async function main() { + let options; + + try { + options = parseArgs(process.argv.slice(2)); + } catch (error) { + console.error(error.message); + printUsage(); + process.exitCode = 1; + return; + } + + if (options.help || !options.file) { + printUsage(); + return; + } + + const requestedType = options.type ? normalizeDiagramType(options.type) : null; + if (options.type && !requestedType) { + console.error(`Unsupported diagram type: ${options.type}`); + printUsage(); + process.exitCode = 1; + return; + } + + const source = await readFile(options.file, 'utf8'); + const blocks = extractMermaidBlocks(source, options.file); + if (blocks.length === 0) { + console.error('No Mermaid content found in the supplied file.'); + process.exitCode = 1; + return; + } + + let hasFailures = false; + blocks.forEach((block, index) => { + const result = validateDiagram(block, requestedType); + const label = `Block ${index + 1} (${result.diagramType})`; + if (result.errors.length === 0) { + console.log(`PASS ${label}`); + return; + } + + hasFailures = true; + console.error(`FAIL ${label}`); + for (const error of result.errors) { + console.error(` - ${error}`); + } + }); + + if (hasFailures) { + process.exitCode = 1; + } +} + +main().catch((error) => { + console.error(error.message); + process.exitCode = 1; +}); diff --git a/skills/dependency-cve-triage/SKILL.md b/skills/dependency-cve-triage/SKILL.md new file mode 100644 index 0000000..6a92cb7 --- /dev/null +++ b/skills/dependency-cve-triage/SKILL.md @@ -0,0 +1,87 @@ +--- +name: dependency-cve-triage +description: "Triage a dependency CVE using local repo evidence and remediation guidance." +--- + +# Dependency CVE Triage + +## Purpose + +Act as a security vulnerability analyst investigating a known CVE in the context of a web application dependency. + +## When to use + +Use this skill when asked to triage a dependency CVE using repository evidence and remediation guidance. + +## Inputs to collect + +- `${input:cve-number:Which CVE would you like me to analyze? (e.g., CVE-2024-12345)}` +- `${input:package-name:What dependency/package is this about? (optional if obvious from repo)}` +- Repository evidence for dependency usage, if available +- External advisory links from reputable sources such as NVD or vendor advisories, if browsing is available + +## How to use + +- Prefer evidence-first analysis and cite file paths and, when possible, line ranges for dependency usage. +- If external browsing is available, use reputable sources such as NVD or vendor advisories. +- If external browsing is not available, state that limitation explicitly. +- If implementation context is missing, ask for what is needed to make a grounded assessment. +- Avoid speculation. If the impact cannot be determined, state that explicitly. + +## Rules + +- MUST confirm the vulnerable component, including package name, ecosystem, affected versions, and whether the dependency is direct or transitive. +- MUST summarize the vulnerability, including exploit vector, preconditions, impact, and common exploitation patterns. +- MUST assess local context, including dependency usage locations, reachability of the vulnerable path, configuration or environment preconditions, and existing mitigations such as sandboxing, WAF, or auth boundaries. +- MUST recommend remediation with upgrade to a fixed version as the preferred option. +- MUST label any workaround or mitigation as a stopgap when it is not the preferred long-term fix. +- MUST provide validation steps such as tests or requests to prove non-reachability or confirm the fix works. +- MUST clearly separate what is proven by local code evidence from what comes from external advisories. +- MUST state when a claim cannot be confirmed and provide the next best verification step. +- MUST avoid recommending disabling security controls as the primary remediation. + +## Step-by-step process + +1. Confirm the vulnerable component. + - Package name, ecosystem, affected versions, direct vs transitive. +2. Summarize the vulnerability. + - Exploit vector, preconditions, impact, and common exploitation patterns. +3. Assess local context. + - Where the dependency is used, including imports and call sites. + - Reachability of the vulnerable path. + - Configuration and environment preconditions. + - Existing mitigations such as sandboxing, WAF, and auth boundaries. +4. Recommend remediation. + - Upgrade to a fixed version as the preferred option. + - Safe workaround or mitigation, explicitly labeled as a stopgap. +5. Provide validation steps. + - Tests or requests to prove non-reachability or that the fix works. + +## Output format + +Return two sections. + +### Dependency Tracker fields + +Use exactly this format: + +```txt +- **Comment:** TEXT_FIELD +- **Analysis:** [Not Set, Exploitable, In Triage, Resolved, False Positive, Not Affected] +- **Justification:** [Not Set, Code not present, Code not reachable, Requires configuration, Requires dependency, Requires environment, Protected by compiler, Protected at runtime, Protected at perimeter, Protected by mitigating control] +- **Vendor Response:** [Not Set, Can not fix, Will not fix, Update, Rollback, Workaround available] +- **Details:** TEXT_FIELD +``` + +### Evidence and validation + +- **Local evidence**: where the dependency is referenced, including file paths and line ranges if available +- **Reachability reasoning**: why the vulnerable path is reachable or not reachable +- **Remediation plan**: upgrade or workaround with rollout notes +- **Verification steps**: commands, tests, or requests to confirm the conclusion or fix + +## Examples + +### Example use + +- Triage `${input:cve-number:Which CVE would you like me to analyze? (e.g., CVE-2024-12345)}` for `${input:package-name:What dependency/package is this about? (optional if obvious from repo)}` by confirming the affected package and versions, checking repository usage and reachability, documenting mitigations, and returning the tracker fields plus evidence and validation details. diff --git a/skills/genai-acceptance-review/SKILL.md b/skills/genai-acceptance-review/SKILL.md new file mode 100644 index 0000000..643ad47 --- /dev/null +++ b/skills/genai-acceptance-review/SKILL.md @@ -0,0 +1,66 @@ +--- +name: genai-acceptance-review +description: Review workflow for AI/LLM output usage to prevent over-trust, injection, and unsafe automation. +--- + +# GenAI Acceptance Review + +## When to use + +Use this skill when a system **consumes LLM output** to make decisions or perform actions. + +## Inputs to collect (if available) + +- What the model output is used for (advisory vs actionable) +- Tools/capabilities available to the system (file writes, network calls, deploys) +- Data entering prompts (PII/secrets? retrieved content sources?) +- Approval model (human-in-the-loop? step-up auth?) + +## Threats to consider + +- Prompt injection (content causes the model to ignore instructions) +- Over-trust / tool misuse (model output drives privileged actions) +- Data leakage (secrets/PII included in prompts or outputs) +- Indirect injection via retrieved content (RAG, web pages, PDFs) + +## Step-by-step process + +1. **Map the AI boundary** + - Where prompts are built, where tools are called, what data enters/leaves. +2. **Classify outputs** + - *Advisory*: suggestions for humans + - *Actionable*: used by code to execute, write files, call APIs, change permissions +3. **Apply controls by class** + - Advisory: disclaimers, human review, logging with redaction + - Actionable: strict schema validation, allow-lists, capability gating, step-up approvals +4. **Prompt & retrieval hardening** + - Separate system instructions from untrusted content + - Use structured output (JSON schema) and reject invalid outputs + - Limit context sources; sanitize retrieved content where possible +5. **Add misuse tests** + - Include injection strings and verify they don’t trigger privileged actions +6. **Document safe usage** + - Clear rules for what the model may decide vs what code must enforce + +## Output + +- Boundary diagram (textual is fine) +- Control recommendations (prevent/detect/respond) +- Test cases for injection and over-trust scenarios + +## Repo integration (optional) + +Related prompt: + +- `check-for-unvalidated-genai-acceptances.prompt.md` + +## Output format + +- **Boundary map**: where untrusted content enters, where model output leaves +- **Threats**: top 5 with likelihood/impact +- **Controls**: prevent/detect/respond mapped to advisory vs actionable use +- **Validation**: misuse/prompt-injection test scenarios + +## Examples + +- “LLM suggests shell commands that CI executes” → require allow-listed command templates + schema validation + human approval for privileged operations. diff --git a/skills/input-validation-hardening/SKILL.md b/skills/input-validation-hardening/SKILL.md new file mode 100644 index 0000000..a78fa8b --- /dev/null +++ b/skills/input-validation-hardening/SKILL.md @@ -0,0 +1,59 @@ +--- +name: input-validation-hardening +description: Process for tightening input validation, canonicalization, and safe parsing to prevent injection and logic abuse. +--- + +# Input Validation Hardening + +## When to use + +Use this skill when asked to **validate inputs**, harden request parsing, or prevent injection/abuse. + +## Inputs to collect (if available) + +- Entry points (HTTP endpoints, consumers, file parsers) +- Data sensitivity and trust boundaries +- Existing validation libraries/patterns in the codebase +- Known attack/abuse cases (payloads, bypass attempts) + +## Step-by-step process + +1. **Inventory inputs** + - HTTP params/body/headers, file uploads, message payloads, env vars, CLI args +2. **Define schemas** + - Prefer typed schemas (DTOs) and allow-lists + - Enforce length, charset, ranges, and required fields +3. **Canonicalize early** + - Normalize encoding, trim, and apply consistent parsing (dates, IDs, enums) +4. **Validate before use** + - Reject unknown fields if possible + - Ensure IDs map to authorized resources (ownership/tenant checks) +5. **Protect sinks** + - Parameterize DB queries + - Avoid dynamic execution (eval, shell, template injection) +6. **Add tests** + - Boundary tests (min/max), malformed inputs, and common payloads + +## Output + +- Proposed schema(s) +- Where to enforce validation (middleware/controller boundary) +- Tests added/updated + +## Repo integration (optional) + +Related prompts: + +- `validate-input-handling.prompt.md` +- `scan-for-insecure-apis.prompt.md` + +## Output format + +- **Inventory**: inputs and boundaries +- **Proposed schema(s)** (high-level) +- **Enforcement point**: where validation should occur +- **Test plan**: boundary + malicious inputs + +## Examples + +- “Public JSON API” → reject unknown fields, enforce max sizes, and add negative tests for type confusion and oversized payloads. diff --git a/skills/secrets-and-logging-hygiene/SKILL.md b/skills/secrets-and-logging-hygiene/SKILL.md new file mode 100644 index 0000000..e3ec98a --- /dev/null +++ b/skills/secrets-and-logging-hygiene/SKILL.md @@ -0,0 +1,60 @@ +--- +name: secrets-and-logging-hygiene +description: Workflow for preventing secret leaks and sensitive logging (PII/credentials) and adding redaction defaults. +--- + +# Secrets and Logging Hygiene + +## When to use + +Use this skill when asked to **scan for secrets**, harden logging, or reduce sensitive data exposure. + +## Inputs to collect (if available) + +- Data classification (PII, auth/session, payments) +- Logging/telemetry stack (logger, APM, sinks) +- Secret management approach (vault, env injection, KMS) +- Incident/audit requirements (retention, access controls) + +## Step-by-step process + +1. **Identify sensitive data** + - Credentials, tokens, API keys, connection strings + - PII (emails, phone, addresses), financial identifiers +2. **Locate sources and sinks** + - Sources: env, config, secrets managers, request payloads + - Sinks: logs, telemetry, error pages, analytics, support dumps +3. **Harden logging** + - Default to structured logs + - Redact known patterns (Authorization headers, cookies, tokens) + - Avoid logging full request/response bodies by default +4. **Prevent secret introduction** + - Replace hardcoded strings with env/secret manager references + - Add guardrails: git hooks, CI secret scanning, unit tests for redaction +5. **Verify** + - Add tests ensuring redaction occurs + - Run a lightweight grep for common secret patterns and known keys + +## Output + +- List of leak points found (if any) +- Recommended redaction policy + implementation location +- Tests and verification steps + +## Repo integration (optional) + +Related prompts: + +- `check-for-secrets.prompt.md` +- `assess-logging.prompt.md` + +## Output format + +- **Leak points found** (table): Type | Where | Evidence | Risk +- **Redaction policy**: defaults + allow-listed fields +- **Guardrails**: CI secret scanning, pre-commit, tests +- **Verification**: how to confirm redaction and rotation + +## Examples + +- “Authorization header logged” → redact `Authorization` and cookies by default; verify logs no longer contain bearer tokens. diff --git a/skills/secure-code-review/SKILL.md b/skills/secure-code-review/SKILL.md new file mode 100644 index 0000000..d4a6bbe --- /dev/null +++ b/skills/secure-code-review/SKILL.md @@ -0,0 +1,68 @@ +--- +name: secure-code-review +description: Repeatable process for an application security code review that produces prioritized findings and fix guidance. +--- + +# Secure Code Review + +## When to use + +Use this skill when asked to **review code for security**, produce findings, or prepare guidance for remediation. + +## Inputs to collect (if available) + +- What component(s) are in scope (API, UI, worker, infra scripts) +- Data sensitivity (PII, auth/session, payments) +- Deployment assumptions (internet-facing, internal, multi-tenant) +- Any known incidents, CVEs, or audit requirements + +## Step-by-step process + +1. **Map entry points & trust boundaries** + - Enumerate request handlers, background consumers, file parsers, template renderers, and admin endpoints. + - Identify where untrusted input crosses into privileged actions or sensitive sinks. +2. **Scan for high-risk classes** + - Injection: SQL/NoSQL/LDAP/OS/template + - Authn/authz: missing checks, insecure defaults, confused deputy + - Deserialization & file handling: unsafe loads, path traversal, upload + - Crypto: homegrown crypto, weak randomness, token validation mistakes + - Logging: secrets/PII exposure, overly verbose errors + - SSRF: URL fetchers, webhook validation gaps +3. **Deep-dive the highest impact areas** + - Trace data flow from input → validation → authorization → sink. + - Look for missing allow-lists, type confusion, and implicit conversions. +4. **Write findings in a consistent format** + - Title, severity, confidence + - Where (file/function) + - Risk + prerequisites + - Repro steps + - Recommendation + verification steps +5. **Close with a remediation plan** + - Quick wins (hours), medium fixes (days), structural guardrails (weeks). + +## Output template + +### Summary + +- Scope reviewed: +- Top issues: +- Overall risk: Low / Medium / High / Critical + +### Findings (repeat) + +- **Title** +- **Severity / Confidence** +- **Where** +- **Risk** +- **Repro** +- **Recommendation** +- **Verification** + +## Repo integration (optional) + +If this repo includes prompt files under `/prompts`, the following are commonly relevant: + +- `secure-code-review.prompt.md` +- `scan-for-insecure-apis.prompt.md` +- `validate-input-handling.prompt.md` +- `review-auth-flows.prompt.md` diff --git a/skills/secure-fix-validation/SKILL.md b/skills/secure-fix-validation/SKILL.md new file mode 100644 index 0000000..28fc206 --- /dev/null +++ b/skills/secure-fix-validation/SKILL.md @@ -0,0 +1,56 @@ +--- +name: secure-fix-validation +description: Standard validation checklist to prove a security fix works and doesn’t regress behavior. +--- + +# Secure Fix Validation + +## When to use + +Use this skill after implementing a security fix, or when reviewing a PR. + +## Inputs to collect (if available) + +- Vulnerability description and expected secure behavior +- Repro steps (request, payload, or test) +- Affected components and entry points +- Deployment/rollout constraints (feature flags, backwards compatibility) + +## Step-by-step process + +1. **Reproduce the issue pre-fix** + - Minimal failing test or request example +2. **Verify the fix** + - Confirm the repro now fails safely +3. **Regression coverage** + - Add unit/integration tests for: + - expected valid inputs + - malicious/edge inputs + - authorization bypass attempts (if relevant) +4. **Non-functional checks** + - Error handling (no stack traces/secret leakage) + - Logging redaction (no PII/secrets) + - Performance impact in hot paths +5. **Rollout safety** + - Feature flags where appropriate + - Backwards compatibility notes + - Monitoring/alerts to detect new failure modes + +## Output + +- Commands run +- Tests added/updated +- Verification evidence (logs/screenshots/snippets) +- Rollout notes + +## Output format + +- **Repro (pre-fix)**: how it failed +- **Verification (post-fix)**: what now happens +- **Tests**: added/updated + what they cover +- **Evidence**: logs/screenshots/snippets (redacted) +- **Rollout notes**: monitoring, flags, compatibility + +## Examples + +- “Fix: block IDOR on /users/:id” → add negative test for cross-user access; verify 403 and tenant scoping on DB query. diff --git a/skills/threat-model/SKILL.MD b/skills/threat-model/SKILL.MD new file mode 100644 index 0000000..7e0abec --- /dev/null +++ b/skills/threat-model/SKILL.MD @@ -0,0 +1,160 @@ +--- +name: threat-model +description: "Threat model a system, feature, service, or PR using Shostack's 4Q workflow, evidence-first analysis, risk scoring, and CLI-friendly Mermaid helper scripts." +--- + +# Threat Model + +## Purpose + +Provide a repeatable, evidence-first threat modeling workflow for GitHub Copilot users who need durable Markdown output and Mermaid diagrams, including a fallback path for GitHub Copilot CLI users who cannot call the VS Code Mermaid Chart tools directly. + +## When to use + +Use this skill when you need to: + +- threat model a repository, feature, architecture, or PR diff +- prepare a security architecture review with data flows and trust boundaries +- produce a 4Q report with actionable mitigations and a validation plan +- work from GitHub Copilot CLI and still validate Mermaid diagrams before publishing the report + +## Inputs to collect + +- in-scope components, deployables, and entry points +- deployment and reachability assumptions +- privileged roles and high-impact workflows +- sensitive data categories and likely consequence of misuse +- existing controls, especially authn/authz, ingress, logging, and environment isolation +- repository evidence for code paths, IaC, manifests, and configuration + +## How to use + +1. Collect repository evidence before relying on operator answers. +2. Ask only the branching intake questions that materially change exposure, privilege, or data-sensitivity scoring. +3. Draft the report in a root-level file named `Threat Model Review - YYYY-MM-DD.md`. +4. Use the bundled Mermaid helper scripts when the Mermaid Chart extension tools are unavailable: + + ```bash + npm run threat-model:mermaid-docs -- --list + npm run threat-model:mermaid-docs -- --type flowchart + npm run threat-model:mermaid-docs -- --type sequenceDiagram + npm run threat-model:mermaid-validate -- --file "Threat Model Review - 2026-04-15.md" + ``` + +5. Fix Mermaid failures and rerun validation until the script exits successfully. +6. Deliver the final report plus a short PR-ready summary. + +## Rules + +- MUST use this evidence hierarchy for factual claims: repo-confirmed, runtime/deployment evidence, operator-stated, ASSUMPTION, UNKNOWN. +- MUST keep confirmed facts separate from inference. +- MUST ask 4-8 concise intake questions when reachability, privileged workflows, data sensitivity, or environment isolation are unclear. +- MUST produce at least these diagrams unless the repository clearly cannot support them: DFD Level 0, DFD Level 1, trust-boundary view, and top 2-3 sequence diagrams. +- MUST validate every Mermaid block before finalizing the report. +- MUST include at least 3 code-anchored or IaC-anchored findings that do not depend primarily on operator answers. +- MUST assign an overall application risk score from 0-100 with confidence, volatility, and top score drivers. +- MUST mark mitigations as PRESENT, ABSENT, or UNKNOWN. +- MUST mark threats as Mitigated, Partially Mitigated, Open, or Unknown based on whether controls materially close the exploit path. +- SHOULD prefer simple Mermaid syntax over advanced styling. +- SHOULD call out contradictions between repo evidence and operator statements before finalizing prioritization. +- MAY omit optional diagrams when the repository does not expose the needed evidence; label the gap as UNKNOWN. + +## Step-by-step process + +1. **Triage and calibrate risk** + - Identify the primary application surface, deployables, and datastore paths. + - Classify reachability first: internal, mixed, partner-reachable, or public. + - Capture repo-confirmed versus operator-stated exposure details separately. +2. **Q1: What are we working on?** + - Summarize system purpose, components, identities, assets, and trust boundaries. + - Rank key flows by sensitivity, privilege, and exposure. + - Draft DFD Level 0 and Level 1 diagrams. +3. **Q2: What can go wrong?** + - Enumerate flow-specific threats with STRIDE and OWASP mapping. + - Include abuse cases for admin paths, bulk actions, impersonation, exports, webhooks, and downstream triggers where relevant. + - Preserve at least 2-3 high-confidence threats directly anchored in code or IaC. +4. **Q3: What are we going to do about it?** + - Evaluate controls as PRESENT, ABSENT, or UNKNOWN. + - Distinguish direct mitigations from adjacent hygiene controls. + - Recommend practical fixes with expected effort and blast-radius reduction. +5. **Q4: Did we do a good job?** + - Build a validation plan with 3-6 scenarios. + - Include one scenario for a code-evidenced weakness, one for an operator-stated assumption, and one for privileged workflow misuse. +6. **Validate diagrams and finish the report** + - Run the helper scripts for Mermaid docs and validation. + - Confirm that diagram evidence, findings, scoring, and validation scenarios are internally consistent. + +## Mermaid helper scripts + +The skill includes these local scripts under `skills/threat-model/scripts/`: + +- `mermaid-docs.mjs`: prints concise syntax guidance and common pitfalls for supported diagram types. +- `validate-mermaid.mjs`: validates Mermaid blocks in Markdown reports or standalone diagram files using deterministic preflight checks. + +Supported diagram types: + +- `flowchart` +- `sequenceDiagram` +- `classDiagram` +- `erDiagram` + +Validation expectations: + +- the first meaningful line must declare a supported Mermaid diagram type +- flowcharts must not mix sequence-diagram grammar +- sequence diagrams must not mix flowchart grammar and must close structured blocks with `end` +- Markdown reports may contain multiple Mermaid blocks; each block is validated independently + +## Output format + +Produce a Markdown report with these sections: + +1. Executive summary +2. Risk score +3. Scope +4. Exposure and risk calibration +5. Contradictions and reconciliation +6. Assumptions and unknowns +7. Architecture and data flows with validated diagrams +8. Key flows +9. Threats table +10. Mitigations table +11. High-risk interaction sequences +12. Validation plan +13. Owners +14. Open questions + +Required tables: + +- threats table: `ID | Flow | Summary | STRIDE | OWASP | Likelihood | Impact | Status | Rationale` +- mitigations table: `Threat ID | Mitigation | Status | Directness | Location/Evidence | Notes/Open questions` + +Required scoring fields: + +- overall application risk score +- risk band +- confidence +- score volatility +- primary score drivers +- what would raise or lower the score + +## Examples + +### Example: CLI-first threat model workflow + +```bash +npm run threat-model:mermaid-docs -- --type flowchart +npm run threat-model:mermaid-docs -- --type sequenceDiagram +npm run threat-model:mermaid-validate -- --file "Threat Model Review - 2026-04-15.md" +``` + +Expected outcome: + +- the docs command prints the required header, allowed constructs, and common pitfalls +- the validation command reports each Mermaid block as `PASS` or fails with block-specific errors + +### Example: threat model output goals + +- Top findings are prioritized by real reachability, privilege, and blast radius. +- Evidence is anchored to repository files, symbols, and line ranges when available. +- Unknowns include an owner and a question that can be answered later. diff --git a/skills/threat-model/scripts/mermaid-docs.mjs b/skills/threat-model/scripts/mermaid-docs.mjs new file mode 100644 index 0000000..f3abb7b --- /dev/null +++ b/skills/threat-model/scripts/mermaid-docs.mjs @@ -0,0 +1,115 @@ +/* eslint-disable no-console */ + +import { getDiagramDoc, listDiagramTypes, normalizeDiagramType } from './mermaid-support.mjs'; + +function parseArgs(argv) { + const options = { + list: false, + type: null, + json: false, + help: false, + }; + + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + if (arg === '--list') { + options.list = true; + continue; + } + + if (arg === '--json') { + options.json = true; + continue; + } + + if (arg === '--help' || arg === '-h') { + options.help = true; + continue; + } + + if (arg === '--type') { + options.type = argv[index + 1] ?? null; + index += 1; + continue; + } + + if (!arg.startsWith('-') && !options.type) { + options.type = arg; + continue; + } + + throw new Error(`Unknown argument: ${arg}`); + } + + return options; +} + +function printUsage() { + console.log('Usage: node skills/threat-model/scripts/mermaid-docs.mjs [--list] [--type <diagramType>] [--json]'); + console.log('Supported types:', listDiagramTypes().join(', ')); +} + +function formatDoc(doc) { + const lines = [ + `Diagram type: ${doc.canonicalName}`, + `Required header: ${doc.header}`, + `Purpose: ${doc.purpose}`, + '', + 'Core constructs:', + ...doc.constructs.map((item) => `- ${item}`), + '', + 'Common pitfalls:', + ...doc.pitfalls.map((item) => `- ${item}`), + ]; + + return lines.join('\n'); +} + +function main() { + let options; + + try { + options = parseArgs(process.argv.slice(2)); + } catch (error) { + console.error(error.message); + printUsage(); + process.exitCode = 1; + return; + } + + if (options.help || (!options.list && !options.type)) { + printUsage(); + return; + } + + if (options.list) { + const supportedTypes = listDiagramTypes(); + if (options.json) { + console.log(JSON.stringify(supportedTypes, null, 2)); + return; + } + + for (const type of supportedTypes) { + console.log(type); + } + return; + } + + const normalizedType = normalizeDiagramType(options.type); + if (!normalizedType) { + console.error(`Unsupported diagram type: ${options.type}`); + printUsage(); + process.exitCode = 1; + return; + } + + const doc = getDiagramDoc(normalizedType); + if (options.json) { + console.log(JSON.stringify(doc, null, 2)); + return; + } + + console.log(formatDoc(doc)); +} + +main(); diff --git a/skills/threat-model/scripts/mermaid-support.mjs b/skills/threat-model/scripts/mermaid-support.mjs new file mode 100644 index 0000000..5ccb8bd --- /dev/null +++ b/skills/threat-model/scripts/mermaid-support.mjs @@ -0,0 +1,115 @@ +const DIAGRAM_DOCS = { + flowchart: { + canonicalName: 'flowchart', + aliases: ['flowchart', 'flow', 'dfd'], + header: 'flowchart LR', + purpose: 'Use for DFD Level 0, DFD Level 1, and trust-boundary views.', + constructs: [ + 'Declare a direction on the first line, such as `flowchart LR` or `flowchart TD`.', + 'Use simple alphanumeric node ids and put descriptive text in labels.', + 'Use `subgraph ... end` to show trust boundaries or subsystem grouping.', + 'Quote edge labels when they contain punctuation or multi-word text.', + ], + pitfalls: [ + 'Do not mix sequence-diagram syntax such as `participant`, `actor`, or `->>`.', + 'Do not leave a `subgraph` block without a matching `end`.', + 'Keep node ids simple; put complex text in labels instead of ids.', + ], + }, + sequenceDiagram: { + canonicalName: 'sequenceDiagram', + aliases: ['sequencediagram', 'sequence', 'sequenceDiagram'], + header: 'sequenceDiagram', + purpose: 'Use for the highest-risk interaction flows such as login, admin actions, or data export.', + constructs: [ + 'Declare `sequenceDiagram` on the first meaningful line.', + 'Define actors or participants before the first interaction when practical.', + 'Use `alt`, `opt`, `loop`, `par`, `critical`, or `rect` blocks with a closing `end`.', + 'Use interaction arrows such as `->>` and `-->>` with a message label after `:`.', + ], + pitfalls: [ + 'Do not mix flowchart syntax such as `flowchart LR`, `subgraph`, or node labels like `A[API]`.', + 'Do not use `else`, `and`, or `option` outside their parent blocks.', + 'Every structured block must close with `end`.', + ], + }, + classDiagram: { + canonicalName: 'classDiagram', + aliases: ['classdiagram', 'class', 'classDiagram'], + header: 'classDiagram', + purpose: 'Use only when modeling role relationships or policy objects adds clarity.', + constructs: [ + 'Declare `classDiagram` on the first meaningful line.', + 'Keep class names simple and model only the relationships needed for the security story.', + ], + pitfalls: [ + 'Avoid mixing flowchart or sequence syntax into a class diagram.', + ], + }, + erDiagram: { + canonicalName: 'erDiagram', + aliases: ['erdiagram', 'er', 'entityrelationshipdiagram', 'entityRelationshipDiagram'], + header: 'erDiagram', + purpose: 'Use when data classification or ownership is easier to explain as datastore relationships.', + constructs: [ + 'Declare `erDiagram` on the first meaningful line.', + 'Use compact entity names and relationship labels.', + ], + pitfalls: [ + 'Avoid mixing flowchart or sequence syntax into an ER diagram.', + ], + }, +}; + +const ALIAS_MAP = new Map( + Object.values(DIAGRAM_DOCS).flatMap((doc) => + doc.aliases.map((alias) => [alias.toLowerCase(), doc.canonicalName]), + ), +); + +function normalizeDiagramType(value) { + if (!value) { + return null; + } + + return ALIAS_MAP.get(value.trim().toLowerCase()) ?? null; +} + +function getDiagramDoc(type) { + const normalizedType = normalizeDiagramType(type); + return normalizedType ? DIAGRAM_DOCS[normalizedType] : null; +} + +function listDiagramTypes() { + return Object.keys(DIAGRAM_DOCS); +} + +function firstMeaningfulLine(source) { + for (const rawLine of source.split(/\r?\n/u)) { + const line = rawLine.trim(); + if (!line || line.startsWith('%%')) { + continue; + } + + return line; + } + + return ''; +} + +function detectDiagramType(source) { + const header = firstMeaningfulLine(source); + if (!header) { + return null; + } + + return normalizeDiagramType(header.split(/\s+/u)[0]); +} + +export { + detectDiagramType, + firstMeaningfulLine, + getDiagramDoc, + listDiagramTypes, + normalizeDiagramType, +}; diff --git a/skills/threat-model/scripts/validate-mermaid.mjs b/skills/threat-model/scripts/validate-mermaid.mjs new file mode 100644 index 0000000..8653216 --- /dev/null +++ b/skills/threat-model/scripts/validate-mermaid.mjs @@ -0,0 +1,272 @@ +/* eslint-disable no-console */ + +import { readFile } from 'node:fs/promises'; +import path from 'node:path'; + +import { + detectDiagramType, + firstMeaningfulLine, + listDiagramTypes, + normalizeDiagramType, +} from './mermaid-support.mjs'; + +function parseArgs(argv) { + const options = { + file: null, + type: null, + help: false, + }; + + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + if (arg === '--help' || arg === '-h') { + options.help = true; + continue; + } + + if (arg === '--file') { + options.file = argv[index + 1] ?? null; + index += 1; + continue; + } + + if (arg === '--type') { + options.type = argv[index + 1] ?? null; + index += 1; + continue; + } + + if (!arg.startsWith('-') && !options.file) { + options.file = arg; + continue; + } + + throw new Error(`Unknown argument: ${arg}`); + } + + return options; +} + +function printUsage() { + console.log('Usage: node skills/threat-model/scripts/validate-mermaid.mjs --file <path> [--type <diagramType>]'); + console.log('The file may be a Markdown report with one or more ```mermaid blocks, or a standalone Mermaid file.'); + console.log(`Supported types: ${listDiagramTypes().join(', ')}`); +} + +function extractMermaidBlocks(source, filePath) { + const extension = path.extname(filePath).toLowerCase(); + if (extension === '.md' || extension === '.markdown') { + const blocks = []; + const pattern = /```mermaid\s*\n([\s\S]*?)```/gu; + let match; + while ((match = pattern.exec(source)) !== null) { + blocks.push(match[1].trim()); + } + return blocks; + } + + return [source.trim()]; +} + +function validateCommon(source, errors) { + if (!source.trim()) { + errors.push('Diagram is empty.'); + } +} + +function validateFlowchart(lines, errors) { + let subgraphDepth = 0; + let hasEdge = false; + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('%%') || trimmed.startsWith('flowchart ')) { + continue; + } + + if (/^(participant|actor|sequenceDiagram)\b/u.test(trimmed) || trimmed.includes('->>')) { + errors.push(`Flowchart appears to mix sequence-diagram syntax: ${trimmed}`); + } + + if (/^subgraph\b/u.test(trimmed)) { + subgraphDepth += 1; + } + + if (trimmed === 'end') { + if (subgraphDepth === 0) { + errors.push('Encountered `end` without a matching `subgraph`.'); + } else { + subgraphDepth -= 1; + } + } + + if (/(-->|==>|-.->|---|o--o|x--x|--x|x--)/u.test(trimmed)) { + hasEdge = true; + } + } + + if (subgraphDepth !== 0) { + errors.push('One or more `subgraph` blocks are missing a closing `end`.'); + } + + if (!hasEdge) { + errors.push('Flowchart must include at least one edge.'); + } +} + +function validateSequenceDiagram(lines, errors) { + const stack = []; + let hasInteraction = false; + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('%%') || trimmed === 'sequenceDiagram') { + continue; + } + + if (/^(flowchart|subgraph)\b/u.test(trimmed) || /\[[^\]]+\]/u.test(trimmed)) { + errors.push(`Sequence diagram appears to mix flowchart syntax: ${trimmed}`); + } + + if (/^(participant|actor|autonumber|activate|deactivate|title|Note\b|link\b|create\b|destroy\b|box\b)/u.test(trimmed)) { + continue; + } + + if (/^(alt|opt|loop|par|critical|rect|break)\b/u.test(trimmed)) { + stack.push(trimmed.split(/\s+/u)[0]); + continue; + } + + if (/^else\b/u.test(trimmed)) { + if (!stack.includes('alt')) { + errors.push('`else` must appear inside an `alt` block.'); + } + continue; + } + + if (/^and\b/u.test(trimmed)) { + if (!stack.includes('par')) { + errors.push('`and` must appear inside a `par` block.'); + } + continue; + } + + if (/^option\b/u.test(trimmed)) { + if (!stack.includes('critical')) { + errors.push('`option` must appear inside a `critical` block.'); + } + continue; + } + + if (trimmed === 'end') { + if (stack.length === 0) { + errors.push('Encountered `end` without an open sequence block.'); + } else { + stack.pop(); + } + continue; + } + + if (/[-.=]{0,2}(?:>>|>|x|\))\s*[^:]+:/u.test(trimmed)) { + hasInteraction = true; + continue; + } + + errors.push(`Unsupported or malformed sequence-diagram line: ${trimmed}`); + } + + if (stack.length > 0) { + errors.push('One or more structured sequence blocks are missing a closing `end`.'); + } + + if (!hasInteraction) { + errors.push('Sequence diagram must include at least one interaction line.'); + } +} + +function validateDiagram(source, requestedType) { + const errors = []; + const diagramType = requestedType ?? detectDiagramType(source); + if (!diagramType) { + errors.push(`Missing or unsupported diagram header. Supported types: ${listDiagramTypes().join(', ')}`); + return { diagramType: requestedType ?? 'unknown', errors }; + } + + validateCommon(source, errors); + const lines = source.split(/\r?\n/u); + const header = firstMeaningfulLine(source); + const headerToken = header.split(/\s+/u)[0]; + + if (!normalizeDiagramType(headerToken)) { + errors.push(`Unsupported Mermaid header: ${header}`); + } + + if (diagramType === 'flowchart') { + validateFlowchart(lines, errors); + } + + if (diagramType === 'sequenceDiagram') { + validateSequenceDiagram(lines, errors); + } + + return { diagramType, errors }; +} + +async function main() { + let options; + + try { + options = parseArgs(process.argv.slice(2)); + } catch (error) { + console.error(error.message); + printUsage(); + process.exitCode = 1; + return; + } + + if (options.help || !options.file) { + printUsage(); + return; + } + + const requestedType = options.type ? normalizeDiagramType(options.type) : null; + if (options.type && !requestedType) { + console.error(`Unsupported diagram type: ${options.type}`); + printUsage(); + process.exitCode = 1; + return; + } + + const source = await readFile(options.file, 'utf8'); + const blocks = extractMermaidBlocks(source, options.file); + if (blocks.length === 0) { + console.error('No Mermaid content found in the supplied file.'); + process.exitCode = 1; + return; + } + + let hasFailures = false; + blocks.forEach((block, index) => { + const result = validateDiagram(block, requestedType); + const label = `Block ${index + 1} (${result.diagramType})`; + if (result.errors.length === 0) { + console.log(`PASS ${label}`); + return; + } + + hasFailures = true; + console.error(`FAIL ${label}`); + for (const error of result.errors) { + console.error(` - ${error}`); + } + }); + + if (hasFailures) { + process.exitCode = 1; + } +} + +main().catch((error) => { + console.error(error.message); + process.exitCode = 1; +}); diff --git a/src/core/config.js b/src/core/config.js new file mode 100644 index 0000000..8f84c7b --- /dev/null +++ b/src/core/config.js @@ -0,0 +1,62 @@ +import dotenv from 'dotenv'; + +dotenv.config({ quiet: true }); + +const server = { + port: process.env['server.port'] ? parseInt(process.env['server.port'], 10) : 8080, + hostname: process.env['server.hostname'] || 'localhost', + ssl: process.env['server.ssl'] === 'true' || false, + 'ssl.pfx': process.env['server.ssl.pfx'] || 'localhost.pfx', + 'ssl.pfx.passphrase': process.env['server.ssl.pfx.passphrase'] || 'PFX_PASSPHRASE', +}; + +const logger = { + levels: { + error: 0, + warn: 1, + info: 2, + http: 4, + debug: 5, + silly: 6, + }, + colors: { + error: 'red', + warn: 'magenta', + info: 'blue', + http: 'yellow', + debug: 'green', + silly: 'gray', + }, + transports: { + console: { + enabled: process.env['logger.transports.console.enabled'] === 'true' || false, + level: process.env['logger.transports.console.level'] || 'info', + }, + amqp: { + enabled: process.env['logger.transports.amqp.enabled'] === 'true' || false, + level: process.env['logger.transports.amqp.level'] || 'http', + protocol: 'amqp', + exchange: process.env['logger.transports.amqp.exchange'] || 'logs', + hostname: process.env['logger.transports.amqp.hostname'] || 'localhost', + port: process.env['logger.transports.amqp.port'] ? parseInt(process.env['logger.transports.amqp.port'], 10) : 5672, + username: process.env['logger.transports.amqp.username'] || 'guest', + password: process.env['logger.transports.amqp.password'] || 'guest', + vhost: process.env['logger.transports.amqp.vhost'] || '/logs', + heartbeat: process.env['logger.transports.amqp.heartbeat'] ? parseInt(process.env['logger.transports.amqp.heartbeat'], 10) : 60, + locale: process.env['logger.transports.amqp.locale'] || 'en_US', + type: process.env['logger.transports.amqp.type'] || 'direct', + durable: process.env['logger.transports.amqp.durable'] === 'true' || false, + app: process.env['server.hostname'] || 'localhost', + }, + }, +}; + +export { + server, + logger, +}; + +export default { + server, + logger, +}; diff --git a/src/core/index.js b/src/core/index.js new file mode 100644 index 0000000..64f159f --- /dev/null +++ b/src/core/index.js @@ -0,0 +1,12 @@ +import logger from './logger.js'; +import config from './config.js'; + +export { + logger, + config, +}; + +export default { + logger, + config, +}; diff --git a/src/core/logger.js b/src/core/logger.js new file mode 100644 index 0000000..69edbf9 --- /dev/null +++ b/src/core/logger.js @@ -0,0 +1,10 @@ +import { createLogger } from 'winston'; +import transports from './transports/index.js'; +import { logger as loggerConfig } from './config.js'; + +const logger = createLogger({ + levels: loggerConfig.levels, + transports, +}); + +export default logger; diff --git a/src/core/transports/amqp.js b/src/core/transports/amqp.js new file mode 100644 index 0000000..23025da --- /dev/null +++ b/src/core/transports/amqp.js @@ -0,0 +1,55 @@ +import Transport from 'winston-transport'; +import amqp from 'amqplib'; + +class AMQPTransport extends Transport { + #options; + + constructor(options) { + super({ + format: 'json', + level: options.level, + }); + this.#options = options; + this.initiated = false; + } + + async #connect() { + this.connection = await amqp.connect({ + protocol: this.#options.protocol, + hostname: this.#options.hostname, + port: this.#options.port, + username: this.#options.username, + password: this.#options.password, + vhost: this.#options.vhost, + heartbeat: this.#options.heartbeat, + locale: this.#options.locale, + }); + this.channel = await this.connection.createChannel(); + await this.channel.assertExchange(this.#options.exchange, this.#options.type, { + durable: this.#options.durable, + }); + this.connection.on('close', this.#connect.bind(this)); + } + + log(info, callback) { + const message = JSON.parse(JSON.stringify(info)); + message.app = this.#options.app; + process.nextTick(async () => { + if (!this.initiated) { + await this.#connect(); + this.initiated = true; + } + this.channel.publish( + this.#options.exchange, + message.level, + Buffer.from(JSON.stringify(message)), + ); + if (callback) { + return callback(null, true); + } + return true; + }); + } +} + +export default AMQPTransport; diff --git a/src/core/transports/console.js b/src/core/transports/console.js new file mode 100644 index 0000000..5586da1 --- /dev/null +++ b/src/core/transports/console.js @@ -0,0 +1,19 @@ +import { transports, addColors, format } from 'winston'; + +class ConsoleTransport extends transports.Console { + constructor(options, colors) { + addColors(colors); + const consoleOptions = options; + delete consoleOptions.enabled; + super(consoleOptions); + this.format = format.combine( + format.colorize({ + colors, + level: true, + }), + format.simple(), + ); + } +}; + +export default ConsoleTransport; diff --git a/src/core/transports/index.js b/src/core/transports/index.js new file mode 100644 index 0000000..2d26da3 --- /dev/null +++ b/src/core/transports/index.js @@ -0,0 +1,21 @@ +import { logger } from '../config.js'; + +import ConsoleTransport from './console.js'; +import AMQPTransport from './amqp.js'; + +const transports = []; + +if (logger.transports.console.enabled) { + transports.push(new ConsoleTransport( + logger.transports.console, + logger.colors, + )); +} + +if (logger.transports.amqp.enabled) { + transports.push(new AMQPTransport( + logger.transports.amqp, + )); +} + +export default transports; diff --git a/src/express_app.js b/src/express_app.js new file mode 100644 index 0000000..bacbfa8 --- /dev/null +++ b/src/express_app.js @@ -0,0 +1,113 @@ +import express from 'express'; +import rateLimit from 'express-rate-limit'; +import { logger } from 'copilot-instructions-mcp/core'; +import { reqInfo } from 'copilot-instructions-mcp/middlewares'; +import mcpServer from './mcp_server.js'; + +const app = express(); + +app.disable('x-powered-by'); + +app.use(express.json({ + limit: '1mb', + type: ['application/json', 'application/*+json'], +})); + +app.use(reqInfo); + +const mcpRateLimit = rateLimit({ + windowMs: 60 * 1000, + limit: 120, + standardHeaders: true, + legacyHeaders: false, +}); + +app.get('/', (req, res) => { + logger.info('Received request for homepage', { + source: 'express_app.get(/)', + reqInfo: req.info, + }); + // TODO: Read in frontend homepage + const homeHTML = '<html><head><title>Robotti MCP

Welcome to Robotti MCP

'; + return res.status(200).send(homeHTML); +}); + +app.get('/health', (req, res) => res.status(200).send('OK')); + +app.get('/mcp', mcpRateLimit, (req, res) => { + // TODO: Log request details + /** TODO: Implement MCP Server GET Specification + Specification 1: The client MAY issue an HTTP GET to the MCP endpoint. This can be used to open an SSE stream, allowing the server to communicate to the client, without the client first sending data via HTTP POST. + Specification 2: The client MUST include an Accept header, listing text/event-stream as a supported content type. + Specification 3: The server MUST either return Content-Type: text/event-stream in response to this HTTP GET, or else return HTTP 405 Method Not Allowed, indicating that the server does not offer an SSE stream at this endpoint. + Specification 4: If the server initiates an SSE stream: + - The server MAY send JSON-RPC requests and notifications on the stream. + - These messages SHOULD be unrelated to any concurrently-running JSON-RPC request from the client. + - The server MUST NOT send a JSON-RPC response on the stream unless resuming a stream associated with a previous client request. + - The server MAY close the SSE stream at any time. + - The client MAY close the SSE stream at any time. + */ + logger.info('Received MCP GET request', { + source: 'express_app.get(/mcp)', + reqInfo: req.info, + }); + return res.status(405).send('Method Not Allowed'); +}); + +app.post('/mcp', mcpRateLimit, async (req, res) => { + logger.info('Received MCP POST request', { + source: 'express_app.post(/mcp)', + reqInfo: req.info, + }); + + try { + const mcp = mcpServer(); + await mcp.server.connect(mcp.transport); + return await mcp.transport.handleRequest(req, res, req.body); + } catch (error) { + logger.error('Unhandled error while processing MCP POST request', { + source: 'express_app.post(/mcp)', + reqInfo: req.info, + error: { + message: error?.message || String(error), + stack: error?.stack, + }, + }); + + if (!res.headersSent) { + return res.status(500).json({ + jsonrpc: '2.0', + error: { + code: -32603, + message: 'Internal error', + }, + id: null, + }); + } + } + return res.status(500).end(); +}); + +// Ensure JSON parse errors are returned consistently (and do not leak stacks) +// This runs after route handlers when express.json() throws. +app.use((err, req, res, next) => { + if (err?.type === 'entity.parse.failed') { + logger.warn('Invalid JSON body received', { + source: 'express_app.jsonParser', + reqInfo: req.info, + }); + + return res.status(400).json({ + jsonrpc: '2.0', + error: { + code: -32700, + message: 'Parse error: Invalid JSON', + }, + id: null, + }); + } + + return next(err); +}); + +export default app; diff --git a/src/mcp_prompts/index.js b/src/mcp_prompts/index.js new file mode 100644 index 0000000..a160a04 --- /dev/null +++ b/src/mcp_prompts/index.js @@ -0,0 +1,16 @@ +import fs from 'fs'; +import path from 'path'; + +const prompts = {}; + +fs.readdirSync(path.join(process.cwd(), 'prompts')).forEach(file => { + prompts[file] = fs.readFileSync(path.join(process.cwd(), 'prompts', file), 'utf-8'); +}); + +export { + prompts, +}; + +export default { + prompts, +}; diff --git a/src/mcp_resources/index.js b/src/mcp_resources/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/mcp_resources/resource_files/index.js b/src/mcp_resources/resource_files/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/mcp_server.js b/src/mcp_server.js new file mode 100644 index 0000000..02dacd3 --- /dev/null +++ b/src/mcp_server.js @@ -0,0 +1,49 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import mcpTools from 'copilot-instructions-mcp/mcp_tools'; +import { prompts } from 'copilot-instructions-mcp/mcp_prompts'; + +function makeMCPServer() { + const server = new McpServer({ + name: 'copilot-security-mcp', + version: '1.0.0', + title: 'Copilot Security Instructions MCP', + + }); + + // Register Tools + mcpTools.list_resources(server); + mcpTools.list_prompts(server); + mcpTools.get_prompt(server); + + // Register MCP-native prompts for client compatibility (prompts/list + prompts/get) + for (const [promptName, promptText] of Object.entries(prompts)) { + server.registerPrompt( + promptName, + { + title: promptName, + description: `Prompt loaded from prompts/${promptName}`, + }, + async () => ({ + description: `Prompt loaded from prompts/${promptName}`, + messages: [ + { + role: 'user', + content: { + type: 'text', + text: promptText, + }, + }, + ], + }), + ); + } + + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: undefined, + }); + + return { server, transport }; +} + +export default makeMCPServer; diff --git a/src/mcp_tools/get_prompt.js b/src/mcp_tools/get_prompt.js new file mode 100644 index 0000000..2a247df --- /dev/null +++ b/src/mcp_tools/get_prompt.js @@ -0,0 +1,43 @@ +import { z } from 'zod'; +import { logger } from 'copilot-instructions-mcp/core'; +import { prompts } from 'copilot-instructions-mcp/mcp_prompts'; + +function get_prompt(server) { + server.registerTool( + 'get_prompt', + { + title: 'get_prompt', + description: 'A tool to get a specific prompt by name', + inputSchema: { + promptName: z.enum(Object.keys(prompts)).describe('The name of the prompt to retrieve'), + }, + }, + async (args, extra) => { + logger.info('Get prompt tool called', { + source: 'mcp_tools.get_prompt', + details: { + args, + extra, + }, + }); + const prompt = prompts[args.promptName]; + if (!prompt) { + return { + content: [{ + type: 'text', + text: `Prompt not found: ${args.promptName}`, + }], + isError: true, + }; + } + return { + content: [{ + type: 'text', + text: prompt, + }], + }; + }, + ); +} + +export default get_prompt; diff --git a/src/mcp_tools/index.js b/src/mcp_tools/index.js new file mode 100644 index 0000000..63baf0e --- /dev/null +++ b/src/mcp_tools/index.js @@ -0,0 +1,16 @@ +import list_resources from './list_resources.js'; +import list_prompts from './list_prompts.js'; +import get_prompt from './get_prompt.js'; + + +export { + list_resources, + list_prompts, + get_prompt, +}; + +export default { + list_resources, + list_prompts, + get_prompt, +}; diff --git a/src/mcp_tools/list_prompts.js b/src/mcp_tools/list_prompts.js new file mode 100644 index 0000000..cd4ecb8 --- /dev/null +++ b/src/mcp_tools/list_prompts.js @@ -0,0 +1,26 @@ +import { logger } from 'copilot-instructions-mcp/core'; +import { prompts } from 'copilot-instructions-mcp/mcp_prompts'; + +function list_prompts(server) { + server.registerTool( + 'list-prompts', + { + title: 'List Prompts', + description: 'A tool to list all available prompts', + inputSchema: {}, + }, + async () => { + logger.info('List prompts tool called', { + source: 'mcp_tools.list_prompts', + }); + return { + content: [{ + type: 'text', + text: JSON.stringify(Object.keys(prompts), null, 2), + }], + }; + }, + ); +} + +export default list_prompts; diff --git a/src/mcp_tools/list_resources.js b/src/mcp_tools/list_resources.js new file mode 100644 index 0000000..fa3e537 --- /dev/null +++ b/src/mcp_tools/list_resources.js @@ -0,0 +1,44 @@ +import { z } from 'zod'; +import { logger } from 'copilot-instructions-mcp/core'; + + + +function list_resources(server) { + server.registerTool( + 'list_resources', + { + title: 'List Resources', + description: 'Lists all available resources based on filtered scope.', + inputSchema: { + resourceType: z.enum(['user', 'framework', 'websites']).optional().describe('Optional resource type to filter results by. If omitted, all resource types are returned.'), + }, + }, + async (args, extra) => { + logger.info('List all resources tool call', { + source: 'mcp_tools.list_resources', + details: { + args, + extra, + }, + }); + if (args?.resourceType && typeof args.resourceType !== 'string') { + logger.warn('Invalid resource type provided', { + source: 'mcp_tools.list_resources', + details: { + args, + extra, + }, + }); + } + // TODO: Implement resources map retrieval + return { + content: [{ + type: 'text', + text: 'No resources available', + }], + }; + }, + ); +} + +export default list_resources; diff --git a/src/middlewares/index.js b/src/middlewares/index.js new file mode 100644 index 0000000..9d88fb0 --- /dev/null +++ b/src/middlewares/index.js @@ -0,0 +1,9 @@ +import reqInfo from './reqInfo.js'; + +export { + reqInfo, +}; + +export default { + reqInfo, +}; diff --git a/src/middlewares/reqInfo.js b/src/middlewares/reqInfo.js new file mode 100644 index 0000000..0055ab4 --- /dev/null +++ b/src/middlewares/reqInfo.js @@ -0,0 +1,40 @@ +import { ulid } from 'ulid'; + +function redactHeaders(headers) { + const redactedKeys = new Set([ + 'authorization', + 'proxy-authorization', + 'cookie', + 'set-cookie', + 'x-api-key', + 'x-auth-token', + 'x-forwarded-authorization', + ]); + + const safe = {}; + for (const [key, value] of Object.entries(headers || {})) { + if (redactedKeys.has(String(key).toLowerCase())) { + safe[key] = '[REDACTED]'; + continue; + } + + // Avoid huge header values in logs + const str = Array.isArray(value) ? value.join(',') : String(value); + safe[key] = str.length > 2048 ? `${str.slice(0, 2048)}...` : str; + } + return safe; +} + +function reqInfoMiddleware(req, res, next) { + req.info = { + id: ulid(), + headers: redactHeaders(req.headers), + method: req.method, + url: req.url, + timestamp: new Date().toISOString(), + }; + + return next(); +} + +export default reqInfoMiddleware; diff --git a/tests/insecure-api.cs b/tests/insecure-api.cs deleted file mode 100644 index c63739d..0000000 --- a/tests/insecure-api.cs +++ /dev/null @@ -1,7 +0,0 @@ -// Risk: Deprecated crypto -SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider(); -byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(password)); - -// Risk: Unsecured HTTP client -HttpClient client = new HttpClient(); -var result = await client.GetAsync("http://example.com/data"); diff --git a/tests/logs-sensitive-data.go b/tests/logs-sensitive-data.go deleted file mode 100644 index df94639..0000000 --- a/tests/logs-sensitive-data.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import "log" - -func login(user string, password string) { - log.Printf("User login attempt: %s with password: %s", user, password) // don't do this -} diff --git a/tests/overtrusted-genai-snippet.js b/tests/overtrusted-genai-snippet.js deleted file mode 100644 index 85a74e8..0000000 --- a/tests/overtrusted-genai-snippet.js +++ /dev/null @@ -1,11 +0,0 @@ -// Suggested by Copilot (hallucinated library) -const security = require("secure-auth-lite"); // this package doesn't exist - -// Non-existent API -auth.initSecureSession(user); // likely hallucinated method - -// Undocumented validation logic -function validatePayload(input) { - // Copilot suggested regex - return /^[a-z0-9_]+$/.test(input); -} diff --git a/tests/secret-hardcode.js b/tests/secret-hardcode.js deleted file mode 100644 index 12888ac..0000000 --- a/tests/secret-hardcode.js +++ /dev/null @@ -1,9 +0,0 @@ -// Bad: Hardcoded API key -const API_KEY = "sk_test_abc123XYZsecure"; - -// Bad: Credentials inline -const dbUser = "admin"; -const dbPass = "hunter2"; - -// Acceptable: Loading from environment variable (but should still validate usage) -const token = process.env.JWT_SECRET; diff --git a/tests/unsafe-deserialization.py b/tests/unsafe-deserialization.py deleted file mode 100644 index 10a4576..0000000 --- a/tests/unsafe-deserialization.py +++ /dev/null @@ -1,11 +0,0 @@ -# Dangerous: loading raw JSON into Python objects -import json - -def load_user(data): - return json.loads(data) # unvalidated input - -# Worse: pickled input from untrusted source -import pickle - -def restore_object(payload): - return pickle.loads(payload) # arbitrary code exec risk diff --git a/tests/weak-auth-flow.ts b/tests/weak-auth-flow.ts deleted file mode 100644 index 6584e3a..0000000 --- a/tests/weak-auth-flow.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Token with no expiration -const jwt = require("jsonwebtoken"); -const token = jwt.sign({ user: "admin" }, "shhhhh"); // no expiration - -// No CSRF protection -app.post("/update-profile", (req, res) => { - updateUser(req.body); - res.send("Updated"); -});