From d62ac0ec5e54c0e52eb4639f26af7122af768ab6 Mon Sep 17 00:00:00 2001 From: Omar Santos Date: Wed, 29 Oct 2025 09:16:56 -0700 Subject: [PATCH 01/40] Update mkdocs.yml to remove extra footer --- mkdocs.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mkdocs.yml b/mkdocs.yml index 948cb35..c9c4925 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -55,3 +55,7 @@ markdown_extensions: - pymdownx.keys - pymdownx.tabbed: alternate_style: true + +extra: + generator: false + From 52489c764c1e2643cb420923e7d2d8bc86b58424 Mon Sep 17 00:00:00 2001 From: Ramraj Bishnoie Date: Wed, 29 Oct 2025 14:18:05 -0400 Subject: [PATCH 02/40] fix: updated safe-c-function rule to apply to c/cpp languages only --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- pyproject.toml | 2 +- skills/software-security/SKILL.md | 7 +++---- ...safe-c-functions.md => codeguard-0-safe-c-functions.md} | 7 +++++-- ...safe-c-functions.md => codeguard-0-safe-c-functions.md} | 6 ++++-- sources/core/codeguard-SKILLS.md.template | 3 +-- uv.lock | 4 ++-- 8 files changed, 18 insertions(+), 15 deletions(-) rename skills/software-security/rules/{codeguard-1-safe-c-functions.md => codeguard-0-safe-c-functions.md} (99%) rename sources/core/{codeguard-1-safe-c-functions.md => codeguard-0-safe-c-functions.md} (99%) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index af3c432..4ef74fd 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -13,7 +13,7 @@ "name": "codeguard-security", "source": "./", "description": "Comprehensive security rules for AI coding agents", - "version": "1.0.0", + "version": "1.0.1", "repository": "https://github.com/project-codeguard/rules.git", "tags": [ "security", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index d0b13b4..21f1021 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "codeguard-security", "description": "Security code review skill based on Project CodeGuard's comprehensive security rules. Helps AI coding agents write secure code and prevent common vulnerabilities.", - "version": "1.0.0", + "version": "1.0.1", "author": { "name": "Project CodeGuard", "url": "https://project-codeguard.org" diff --git a/pyproject.toml b/pyproject.toml index 430c325..4160584 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "project-codeguard" -version = "1.0.0" +version = "1.0.1" description = "AI Coding Rules for Security and Best Practices" requires-python = ">=3.11" dependencies = [ diff --git a/skills/software-security/SKILL.md b/skills/software-security/SKILL.md index 6205242..fc79ed1 100644 --- a/skills/software-security/SKILL.md +++ b/skills/software-security/SKILL.md @@ -1,8 +1,7 @@ --- name: software-security description: A software security skill that integrates with Project CodeGuard to help AI coding agents write secure code and prevent common vulnerabilities. Use this skill when writing, reviewing, or modifying code to ensure secure-by-default practices are followed. -metadata: -codeguard-version: "1.0.0" +codeguard-version: "1.0.1" framework: "Project CodeGuard" purpose: "Embed secure-by-default practices into AI coding workflows" --- @@ -25,13 +24,13 @@ When writing or reviewing code: - `codeguard-1-hardcoded-credentials.md` - Never hardcode secrets, passwords, API keys, or tokens - `codeguard-1-crypto-algorithms.md` - Use only modern, secure cryptographic algorithms - `codeguard-1-digital-certificates.md` - Validate and manage digital certificates securely -- `codeguard-1-safe-c-functions.md` - Avoid unsafe C/C++ functions and use safe alternatives 2. Context-Specific Rules: Apply rules from /rules directory based on the language of the feature being implemented using the table given below: | Language | Rule Files to Apply | |----------|---------------------| -| c | codeguard-0-additional-cryptography.md, codeguard-0-api-web-services.md, codeguard-0-authentication-mfa.md, codeguard-0-authorization-access-control.md, codeguard-0-client-side-web-security.md, codeguard-0-data-storage.md, codeguard-0-file-handling-and-uploads.md, codeguard-0-framework-and-languages.md, codeguard-0-iac-security.md, codeguard-0-input-validation-injection.md, codeguard-0-logging.md, codeguard-0-session-management-and-cookies.md, codeguard-0-xml-and-serialization.md | +| c | codeguard-0-additional-cryptography.md, codeguard-0-api-web-services.md, codeguard-0-authentication-mfa.md, codeguard-0-authorization-access-control.md, codeguard-0-client-side-web-security.md, codeguard-0-data-storage.md, codeguard-0-file-handling-and-uploads.md, codeguard-0-framework-and-languages.md, codeguard-0-iac-security.md, codeguard-0-input-validation-injection.md, codeguard-0-logging.md, codeguard-0-safe-c-functions.md, codeguard-0-session-management-and-cookies.md, codeguard-0-xml-and-serialization.md | +| cpp | codeguard-0-safe-c-functions.md | | d | codeguard-0-iac-security.md | | docker | codeguard-0-devops-ci-cd-containers.md, codeguard-0-supply-chain-security.md | | go | codeguard-0-additional-cryptography.md, codeguard-0-api-web-services.md, codeguard-0-authentication-mfa.md, codeguard-0-authorization-access-control.md, codeguard-0-file-handling-and-uploads.md, codeguard-0-input-validation-injection.md, codeguard-0-session-management-and-cookies.md, codeguard-0-xml-and-serialization.md | diff --git a/skills/software-security/rules/codeguard-1-safe-c-functions.md b/skills/software-security/rules/codeguard-0-safe-c-functions.md similarity index 99% rename from skills/software-security/rules/codeguard-1-safe-c-functions.md rename to skills/software-security/rules/codeguard-0-safe-c-functions.md index 113105b..6d8d470 100644 --- a/skills/software-security/rules/codeguard-1-safe-c-functions.md +++ b/skills/software-security/rules/codeguard-0-safe-c-functions.md @@ -1,9 +1,12 @@ --- description: Safe C Functions and Memory and String Safety Guidelines -alwaysApply: true +languages: +- c +- cpp +alwaysApply: false --- -rule_id: codeguard-1-safe-c-functions +rule_id: codeguard-0-safe-c-functions # Prioritize Safe Memory and String Functions in C/C++ diff --git a/sources/core/codeguard-1-safe-c-functions.md b/sources/core/codeguard-0-safe-c-functions.md similarity index 99% rename from sources/core/codeguard-1-safe-c-functions.md rename to sources/core/codeguard-0-safe-c-functions.md index b8db6ee..668b545 100644 --- a/sources/core/codeguard-1-safe-c-functions.md +++ b/sources/core/codeguard-0-safe-c-functions.md @@ -1,7 +1,9 @@ --- description: Safe C Functions and Memory and String Safety Guidelines -languages: [] -alwaysApply: true +languages: +- c +- cpp +alwaysApply: false --- # Prioritize Safe Memory and String Functions in C/C++ diff --git a/sources/core/codeguard-SKILLS.md.template b/sources/core/codeguard-SKILLS.md.template index c856023..f0db8e9 100644 --- a/sources/core/codeguard-SKILLS.md.template +++ b/sources/core/codeguard-SKILLS.md.template @@ -1,8 +1,7 @@ --- name: software-security description: A software security skill that integrates with Project CodeGuard to help AI coding agents write secure code and prevent common vulnerabilities. Use this skill when writing, reviewing, or modifying code to ensure secure-by-default practices are followed. -metadata: -codeguard-version: "1.0.0" +codeguard-version: "x.x.x" framework: "Project CodeGuard" purpose: "Embed secure-by-default practices into AI coding workflows" --- diff --git a/uv.lock b/uv.lock index e9143af..876f078 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.11" [[package]] @@ -340,7 +340,7 @@ wheels = [ [[package]] name = "project-codeguard" -version = "1.0.0" +version = "1.0.1" source = { virtual = "." } dependencies = [ { name = "mkdocs" }, From 72bfdb5b3b6d98ddccbb4270aefaddbd07339448 Mon Sep 17 00:00:00 2001 From: Ramraj Bishnoie Date: Wed, 29 Oct 2025 14:35:22 -0400 Subject: [PATCH 03/40] chore: updated doc and SKILLS file to reflect correct rule count --- docs/claude-code-skill-plugin.md | 18 +++++++++++------- sources/core/codeguard-SKILLS.md.template | 1 - 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/claude-code-skill-plugin.md b/docs/claude-code-skill-plugin.md index f7888c2..c6caca7 100644 --- a/docs/claude-code-skill-plugin.md +++ b/docs/claude-code-skill-plugin.md @@ -70,14 +70,13 @@ When generating or reviewing code, Claude follows this 3-step workflow: ### Rule Categories -**Always-Apply Rules** (4 critical rules checked on every code operation): +**Always-Apply Rules** (3 critical rules checked on every code operation): - `codeguard-1-hardcoded-credentials` - Never hardcode secrets or credentials - `codeguard-1-crypto-algorithms` - Use modern cryptographic algorithms - `codeguard-1-digital-certificates` - Validate certificate security -- `codeguard-1-safe-c-functions` - Replace unsafe C/C++ functions -**Context-Specific Rules** (18 rules applied based on technology and features): -- Input validation, authentication, authorization, APIs, data storage, privacy, logging, cryptography, file handling, serialization, supply chain, DevOps, cloud, Kubernetes, IaC, frameworks, and mobile security +**Context-Specific Rules** (19 rules applied based on technology and features): +- Input validation, authentication, authorization, APIs, data storage, privacy, logging, cryptography, file handling, serialization, supply chain, DevOps, cloud, Kubernetes, IaC, frameworks, mobile security, and memory safety (C/C++) ## Usage Examples @@ -155,7 +154,7 @@ For organizations, deploy CodeGuard to all developers automatically: The plugin includes 22 comprehensive security rules organized into two categories: -### Always-Apply Rules (4 rules) +### Always-Apply Rules (3 rules) These critical rules are checked on **every** code operation: @@ -164,9 +163,8 @@ These critical rules are checked on **every** code operation: | `codeguard-1-hardcoded-credentials` | Prevent secrets, passwords, API keys, tokens in source code | | `codeguard-1-crypto-algorithms` | Ban weak algorithms (MD5, SHA-1, DES); use modern alternatives | | `codeguard-1-digital-certificates` | Validate certificate expiration, key strength, signature algorithms | -| `codeguard-1-safe-c-functions` | Replace unsafe C/C++ functions (gets, strcpy, strcat, sprintf) | -### Context-Specific Rules (18 rules) +### Context-Specific Rules (19 rules) These rules apply based on the programming language, framework, or feature being implemented. Claude automatically selects relevant rules based on context: @@ -182,6 +180,7 @@ These rules apply based on the programming language, framework, or feature being | **Files & Serialization** | `codeguard-0-file-handling-and-uploads`, `codeguard-0-xml-and-serialization` | | **Infrastructure** | `codeguard-0-supply-chain-security`, `codeguard-0-devops-ci-cd-containers`, `codeguard-0-cloud-orchestration-kubernetes`, `codeguard-0-iac-security` | | **Platforms** | `codeguard-0-framework-and-languages`, `codeguard-0-mobile-apps` | +| **Memory Safety (C/C++)** | `codeguard-0-safe-c-functions` | > **Note:** Each rule file contains detailed guidance, checklists, and examples. Claude references these automatically based on the code context. @@ -385,6 +384,11 @@ Found an issue with the plugin or want to improve it? ## Version History +### Version 1.0.1 +- Changed `codeguard-1-safe-c-functions` from always-apply to `codeguard-0-safe-c-functions` context-specific rule (C/C++ only) +- Updated rule counts: 3 always-apply rules, 19 context-specific rules +- Fixed GitHub Copilot instructions to use `description` field instead of `title` + ### Version 1.0.0 - Initial release - 22 comprehensive security rules diff --git a/sources/core/codeguard-SKILLS.md.template b/sources/core/codeguard-SKILLS.md.template index f0db8e9..ff7420b 100644 --- a/sources/core/codeguard-SKILLS.md.template +++ b/sources/core/codeguard-SKILLS.md.template @@ -24,7 +24,6 @@ When writing or reviewing code: - `codeguard-1-hardcoded-credentials.md` - Never hardcode secrets, passwords, API keys, or tokens - `codeguard-1-crypto-algorithms.md` - Use only modern, secure cryptographic algorithms - `codeguard-1-digital-certificates.md` - Validate and manage digital certificates securely -- `codeguard-1-safe-c-functions.md` - Avoid unsafe C/C++ functions and use safe alternatives 2. Context-Specific Rules: Apply rules from /rules directory based on the language of the feature being implemented using the table given below: From 0dc685033506ead406eda209c364551bb8a20e30 Mon Sep 17 00:00:00 2001 From: Ramraj Bishnoie Date: Wed, 29 Oct 2025 14:38:10 -0400 Subject: [PATCH 04/40] chore: removed codeguard-1-safe-c-functions from validation --- .github/workflows/validate-rules.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/validate-rules.yml b/.github/workflows/validate-rules.yml index 3e6f903..10d83f3 100644 --- a/.github/workflows/validate-rules.yml +++ b/.github/workflows/validate-rules.yml @@ -51,7 +51,6 @@ jobs: "sources/core/codeguard-1-hardcoded-credentials.md" "sources/core/codeguard-1-crypto-algorithms.md" "sources/core/codeguard-1-digital-certificates.md" - "sources/core/codeguard-1-safe-c-functions.md" "sources/core/codeguard-SKILLS.md.template" ) From 86a8bde6ef66b04ac98099cec5ccea4c87cb285f Mon Sep 17 00:00:00 2001 From: Aaron Carter Date: Thu, 30 Oct 2025 12:31:14 -0400 Subject: [PATCH 05/40] Adding a FAQ around security scanners and CodeGuard --- docs/faq.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/faq.md b/docs/faq.md index 648a4dc..a3d867d 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -110,6 +110,12 @@ See [CONTRIBUTING.md](https://github.com/project-codeguard/rules/blob/main/CONTR --- +## Q: Does Project CodeGuard replace my security scanners? + +**A:** No, Project CodeGuard rules do not replace your security scanners. The primary purpose of CodeGuard is to help you avoid introducing new security vulnerabilities as you write code, by providing agentic rules and guidance directly in your IDE. If you perform a code review using these rules, Project CodeGuard will most likely identify many of the same vulnerabilities that security scanning tools would find. However, CodeGuard is not a comprehensive substitute for security scanners—automated security tools are designed to thoroughly analyze your entire codebase and catch a broader range of issues. For best results, use CodeGuard rules in combination with your existing security scanners to maximize your code’s security. + +--- + ## Still have questions? **Can't find your answer?** From 538aa4fa612f7741c42780d62ddb8ee26a9ac38f Mon Sep 17 00:00:00 2001 From: Omar Santos Date: Mon, 3 Nov 2025 09:43:18 -0500 Subject: [PATCH 06/40] Update faq.md --- docs/faq.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index a3d867d..2043a59 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -86,17 +86,6 @@ We welcome all feedback - whether it's a bug report, success story, or enhanceme --- -## Q: Why do I get the following error message in GitHub for some of the rules? - -``` -Error in user YAML: (): did not find expected alphabetic -or numeric character while scanning an alias at line x column x -``` - -**A:** You can safely ignore this error. GitHub attempts to parse YAML headers combined with markdown content, which can cause this warning. It does not affect rule functionality - the rules will work correctly in your IDE regardless of this GitHub display issue. - ---- - ## Q: How can I contribute to these rules and this project? **A:** You can contribute at any time by: From ddf331d2b58d7ba5346bd1f153039da53cb9f03e Mon Sep 17 00:00:00 2001 From: Omar Santos Date: Mon, 3 Nov 2025 09:56:14 -0500 Subject: [PATCH 07/40] Update FAQ to include installation instructions for Project CodeGuard plugin in Claude Code --- docs/faq.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 2043a59..438b534 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -69,9 +69,14 @@ Once hidden files are visible, you can copy the appropriate directory (`.cursor/ ## Q: Can I use this with Claude Code? -**A:** Yes! Claude Code automatically reads and follows instructions from a `CLAUDE.md` file in your project root. To use Project CodeGuard rules with Claude Code you can point to the Project CodeGuard rules in your `CLAUDE.md` file. +**A:** Yes! Install the Project CodeGuard Claude Code plugin (Agent Skill) and Claude will apply the security rules automatically while you code. -When Claude Code operates in your project, it treats the Project CodeGuard security rules in `CLAUDE.md` as authoritative system instructions. +```bash +/plugin marketplace add project-codeguard/rules +/plugin install codeguard-security@project-codeguard +``` + +For team/repo defaults, add the plugin in `.claude/settings.json` so it’s enabled for all contributors. See the [Claude Code Plugin documentation](claude-code-skill-plugin.md) for details and troubleshooting. ## Q: How can I report a problem or enhancement to any of the rules? From 4e5628664713fb9fd5985c0849617aa8078de531 Mon Sep 17 00:00:00 2001 From: Aaron Carter Date: Mon, 3 Nov 2025 11:08:48 -0500 Subject: [PATCH 08/40] Adding details around context window usage --- docs/faq.md | 15 ++++++++++++++- docs/images/context-window-no-rules.png | Bin 0 -> 24710 bytes docs/images/context-window-with-rules.png | Bin 0 -> 47872 bytes 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 docs/images/context-window-no-rules.png create mode 100644 docs/images/context-window-with-rules.png diff --git a/docs/faq.md b/docs/faq.md index 438b534..c2a2aed 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -26,7 +26,20 @@ This FAQ document provides clear, concise answers to help developers seamlessly --- ## Q: Will these rules consume a lot of the AI agent's **context window**? -**A:** No. The always-on rules are designed to be lightweight and efficient, and should not consume a lot of the AI agent's context window. The "glob" rules are designed to be applied only to the related file types specified in the rule. +**A:** The always‑on rules are lightweight and have minimal impact on the AI agent’s context window. Glob‑scoped rules only apply to their matching file types. Below are Cursor examples: left, no rules; right, three always‑on rules enabled. + +

+ Cursor AI agent context window usage without Project CodeGuard rules + Cursor AI agent context window usage with Project CodeGuard rules enabled +

+ +
+ + Left: Context window usage without any rules in place.
+ Right: Context window usage with three always-on rules enabled. +
+
+ --- ## Q: What are the OWASP supplementary rules? diff --git a/docs/images/context-window-no-rules.png b/docs/images/context-window-no-rules.png new file mode 100644 index 0000000000000000000000000000000000000000..7356dfdf36d354dc3f2e64e4849e6f986415c966 GIT binary patch literal 24710 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 literal 0 HcmV?d00001 From f27657f67f0e44223df79d274f7f95d0cc8371d1 Mon Sep 17 00:00:00 2001 From: Aaron Carter Date: Mon, 3 Nov 2025 11:36:32 -0500 Subject: [PATCH 09/40] Fixing location of images for the FAQ --- docs/faq.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index c2a2aed..fc4bc2c 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -29,8 +29,8 @@ This FAQ document provides clear, concise answers to help developers seamlessly **A:** The always‑on rules are lightweight and have minimal impact on the AI agent’s context window. Glob‑scoped rules only apply to their matching file types. Below are Cursor examples: left, no rules; right, three always‑on rules enabled.

- Cursor AI agent context window usage without Project CodeGuard rules - Cursor AI agent context window usage with Project CodeGuard rules enabled + Cursor AI agent context window usage without Project CodeGuard rules + Cursor AI agent context window usage with Project CodeGuard rules enabled

From 6c2042355e3ec273611c6651ea1f9de3b7eee3eb Mon Sep 17 00:00:00 2001 From: Omar Santos Date: Wed, 5 Nov 2025 10:02:55 -0500 Subject: [PATCH 10/40] Update getting-started.md --- docs/getting-started.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/getting-started.md b/docs/getting-started.md index 687fae4..df42335 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -2,6 +2,11 @@ Get up and running with Project CodeGuard in just a few steps. +## Project CodeGuard Introduction Video +[This video](https://www.youtube.com/watch?v=O03MDxUWjsE) introduces Project CodeGuard and includes several demos on how to use it during code generation and code review with Claude Code, Codex, and other coding agents. + +[![Project CodeGuard Introduction Video and Demos](http://img.youtube.com/vi/O03MDxUWjsE/0.jpg)](http://www.youtube.com/watch?v=O03MDxUWjsE "Project CodeGuard Introduction Video and Demos") + ## Prerequisites Before you begin, familiarize yourself with how rules work in your IDE: From e0996b00cbdb16b701f4e51c6584eff9795b0a33 Mon Sep 17 00:00:00 2001 From: Ramraj Bishnoie Date: Wed, 5 Nov 2025 10:21:12 -0500 Subject: [PATCH 11/40] docs: enhanced video thumbnail --- docs/getting-started.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index df42335..81add1a 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -5,7 +5,9 @@ Get up and running with Project CodeGuard in just a few steps. ## Project CodeGuard Introduction Video [This video](https://www.youtube.com/watch?v=O03MDxUWjsE) introduces Project CodeGuard and includes several demos on how to use it during code generation and code review with Claude Code, Codex, and other coding agents. -[![Project CodeGuard Introduction Video and Demos](http://img.youtube.com/vi/O03MDxUWjsE/0.jpg)](http://www.youtube.com/watch?v=O03MDxUWjsE "Project CodeGuard Introduction Video and Demos") +
+ +
## Prerequisites From 0850522ce877c5931a266dc535fb032880950e1d Mon Sep 17 00:00:00 2001 From: Thomas Bartlett <67928676+thomas-bartlett@users.noreply.github.com> Date: Mon, 17 Nov 2025 13:41:58 -0500 Subject: [PATCH 12/40] Added optional tags field and filtering support --- sources/core/codeguard-0-api-web-services.md | 4 ++ .../core/codeguard-0-authentication-mfa.md | 3 + src/convert_to_ide_formats.py | 60 ++++++++++++++++--- src/converter.py | 14 ++++- src/formats/base.py | 2 + src/utils.py | 35 +++++++++++ src/validate_unified_rules.py | 9 ++- 7 files changed, 117 insertions(+), 10 deletions(-) diff --git a/sources/core/codeguard-0-api-web-services.md b/sources/core/codeguard-0-api-web-services.md index 94a51fc..0539f56 100644 --- a/sources/core/codeguard-0-api-web-services.md +++ b/sources/core/codeguard-0-api-web-services.md @@ -12,6 +12,10 @@ languages: - typescript - xml - yaml +tags: +- api +- web-security +- microservices alwaysApply: false --- diff --git a/sources/core/codeguard-0-authentication-mfa.md b/sources/core/codeguard-0-authentication-mfa.md index 2be26cc..ff8cd20 100644 --- a/sources/core/codeguard-0-authentication-mfa.md +++ b/sources/core/codeguard-0-authentication-mfa.md @@ -13,6 +13,9 @@ languages: - ruby - swift - typescript +tags: +- authentication +- web-security alwaysApply: false --- diff --git a/src/convert_to_ide_formats.py b/src/convert_to_ide_formats.py index 1d4f5fb..120a455 100644 --- a/src/convert_to_ide_formats.py +++ b/src/convert_to_ide_formats.py @@ -36,6 +36,23 @@ def sync_plugin_metadata(version: str) -> None: print(f"✅ Synced plugin metadata to {version}") +def matches_tag_filter(rule_tags: list[str], filter_tags: list[str]) -> bool: + """ + Check if rule has all required tags (case-insensitive AND logic). + + Args: + rule_tags: List of tags from the rule (already lowercase from parsing) + filter_tags: List of tags to filter by + + Returns: + True if rule has all filter tags (or no filter), False otherwise + """ + if not filter_tags: + return True # No filter means all pass + + return all(tag.lower() in rule_tags for tag in filter_tags) + + def update_skill_md(language_to_rules: dict[str, list[str]], skill_path: str) -> None: """ Update SKILL.md with language-to-rules mapping table. @@ -81,7 +98,7 @@ def update_skill_md(language_to_rules: dict[str, list[str]], skill_path: str) -> print(f"Updated SKILL.md with language mappings") -def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: bool = True, version: str = None) -> dict[str, list[str]]: +def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: bool = True, version: str = None, filter_tags: list[str] = None) -> dict[str, list[str]]: """ Convert rule file(s) to all supported IDE formats using RuleConverter. @@ -90,6 +107,7 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: output_dir: Output directory (default: 'dist/') include_claudecode: Whether to generate Claude Code plugin (default: True, only for core rules) version: Version string to use (default: read from pyproject.toml) + filter_tags: Optional list of tags to filter by (AND logic, case-insensitive) Returns: Dictionary with 'success' and 'errors' lists: @@ -138,7 +156,7 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: # Setup output directory output_base = Path(output_dir) - results = {"success": [], "errors": []} + results = {"success": [], "errors": [], "skipped": []} language_to_rules = defaultdict(list) # Process each file @@ -146,6 +164,11 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: try: # Convert the file (raises exceptions on error) result = converter.convert(md_file) + + # Apply tag filter if specified + if filter_tags and not matches_tag_filter(result.tags, filter_tags): + results["skipped"].append(result.filename) + continue # Write each format output_files = [] @@ -192,9 +215,14 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: results["errors"].append(error_msg) # Summary - print( - f"\nResults: {len(results['success'])} success, {len(results['errors'])} errors" - ) + if filter_tags: + print( + f"\nResults: {len(results['success'])} success, {len(results['skipped'])} skipped (tag filter), {len(results['errors'])} errors" + ) + else: + print( + f"\nResults: {len(results['success'])} success, {len(results['errors'])} errors" + ) # Generate SKILL.md with language mappings (only if Claude Code is included) if include_claudecode and language_to_rules: @@ -256,6 +284,12 @@ def _resolve_source_paths(args) -> list[Path]: default="dist", help="Output directory for generated bundles (default: dist).", ) + parser.add_argument( + "--tag", + "--tags", + dest="tags", + help="Filter rules by tags (comma-separated, case-insensitive, AND logic). Example: --tag api,web-security", + ) cli_args = parser.parse_args() source_paths = _resolve_source_paths(cli_args) @@ -316,7 +350,16 @@ def _resolve_source_paths(args) -> list[Path]: print() # Convert all sources - aggregated = {"success": [], "errors": []} + aggregated = {"success": [], "errors": [], "skipped": []} + # Parse comma-separated tags + filter_tags = None + if cli_args.tags: + filter_tags = [tag.strip() for tag in cli_args.tags.split(",") if tag.strip()] + + # Print tag filter info if active + if filter_tags: + print(f"Tag filter active: {', '.join(filter_tags)} (AND logic - rules must have all tags)\n") + for source_path in source_paths: is_core = source_path == Path("sources/core") @@ -325,11 +368,14 @@ def _resolve_source_paths(args) -> list[Path]: str(source_path), cli_args.output_dir, include_claudecode=is_core, - version=version + version=version, + filter_tags=filter_tags ) aggregated["success"].extend(results["success"]) aggregated["errors"].extend(results["errors"]) + if "skipped" in results: + aggregated["skipped"].extend(results["skipped"]) print("") if aggregated["errors"]: diff --git a/src/converter.py b/src/converter.py index 39f4fce..4a71a94 100644 --- a/src/converter.py +++ b/src/converter.py @@ -12,7 +12,7 @@ from pathlib import Path from language_mappings import languages_to_globs -from utils import parse_frontmatter_and_content +from utils import parse_frontmatter_and_content, validate_tags from formats import ( BaseFormat, ProcessedRule, @@ -45,6 +45,7 @@ class ConversionResult: basename: Filename without extension (e.g., 'my-rule') outputs: Dictionary mapping format names to their outputs languages: List of programming languages the rule applies to, empty list if always applies + tags: List of tags for categorizing and filtering rules Example: result = ConversionResult( filename="my-rule.md", @@ -56,7 +57,8 @@ class ConversionResult: subpath=".cursor/rules" ) }, - languages=["python", "javascript"] + languages=["python", "javascript"], + tags=["authentication", "web-security"] ) """ @@ -64,6 +66,7 @@ class ConversionResult: basename: str outputs: dict[str, FormatOutput] languages: list[str] + tags: list[str] class RuleConverter: @@ -159,6 +162,11 @@ def parse_rule(self, content: str, filename: str) -> ProcessedRule: f"'languages' must be a non-empty list in {filename} when alwaysApply is false" ) + # Parse and validate tags (optional field) + tags = [] + if "tags" in frontmatter: + tags = validate_tags(frontmatter["tags"], filename) + # Adding rule_id to the beginning of the content rule_id = Path(filename).stem markdown_content = f"rule_id: {rule_id}\n\n{markdown_content}" @@ -169,6 +177,7 @@ def parse_rule(self, content: str, filename: str) -> ProcessedRule: always_apply=always_apply, content=markdown_content, filename=filename, + tags=tags, ) def generate_globs(self, languages: list[str]) -> str: @@ -242,4 +251,5 @@ def convert(self, filepath: str) -> ConversionResult: basename=basename, outputs=outputs, languages=rule.languages, + tags=rule.tags, ) diff --git a/src/formats/base.py b/src/formats/base.py index 5af8732..65c75e5 100644 --- a/src/formats/base.py +++ b/src/formats/base.py @@ -25,6 +25,7 @@ class ProcessedRule: always_apply: Whether this rule should apply to all files content: The actual rule content in markdown format filename: Original filename of the rule + tags: List of tags for categorizing and filtering rules """ description: str @@ -32,6 +33,7 @@ class ProcessedRule: always_apply: bool content: str filename: str + tags: list[str] class BaseFormat(ABC): diff --git a/src/utils.py b/src/utils.py index fb0fed6..cc64646 100644 --- a/src/utils.py +++ b/src/utils.py @@ -57,6 +57,41 @@ def parse_frontmatter_and_content(content: str) -> tuple[dict | None, str]: return frontmatter, markdown_content.strip() +def validate_tags(tags, filename=None) -> list[str]: + """ + Validate tags list and return normalized (lowercase) tags. + + Args: + tags: The tags value to validate (should be a list) + filename: Optional filename for better error messages + + Returns: + List of normalized (lowercase) tags + + Raises: + ValueError: If tags are invalid (wrong type, contain whitespace, empty, etc.) + """ + context = f" in {filename}" if filename else "" + + if not isinstance(tags, list): + raise ValueError(f"'tags' must be a list{context}") + + normalized = [] + for tag in tags: + if not isinstance(tag, str): + raise ValueError(f"All tags must be strings{context}, found: {type(tag).__name__}") + + if any(c.isspace() for c in tag): + raise ValueError(f"Tags cannot contain whitespace: '{tag}'{context}") + + if not tag: + raise ValueError(f"Empty tag found{context}") + + normalized.append(tag.lower()) + + return normalized + + def get_version_from_pyproject() -> str: """ Read version from pyproject.toml using Python's built-in TOML parser. diff --git a/src/validate_unified_rules.py b/src/validate_unified_rules.py index bd509bc..a30e56c 100755 --- a/src/validate_unified_rules.py +++ b/src/validate_unified_rules.py @@ -12,7 +12,7 @@ from pathlib import Path from language_mappings import LANGUAGE_TO_EXTENSIONS -from utils import parse_frontmatter_and_content +from utils import parse_frontmatter_and_content, validate_tags def validate_rule(file_path: Path) -> dict[str, list[str]]: @@ -54,6 +54,13 @@ def validate_rule(file_path: Path) -> dict[str, list[str]]: if unknown: warnings.append(f"Unknown languages: {', '.join(unknown)}") + # Validate tags if present + if "tags" in frontmatter: + try: + validate_tags(frontmatter["tags"], file_path.name) + except ValueError as e: + errors.append(str(e)) + # Check content exists if not markdown_content.strip(): errors.append("Rule content cannot be empty") From 4f489ba2c6c340f027cbcf8a0c5a355ddd4ff19e Mon Sep 17 00:00:00 2001 From: Thomas Bartlett <67928676+thomas-bartlett@users.noreply.github.com> Date: Tue, 18 Nov 2025 09:30:13 -0500 Subject: [PATCH 13/40] Improved tag validation and normalization --- src/convert_to_ide_formats.py | 12 ++++++------ src/utils.py | 13 ++++++++++--- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/convert_to_ide_formats.py b/src/convert_to_ide_formats.py index 120a455..3c3cab3 100644 --- a/src/convert_to_ide_formats.py +++ b/src/convert_to_ide_formats.py @@ -38,11 +38,11 @@ def sync_plugin_metadata(version: str) -> None: def matches_tag_filter(rule_tags: list[str], filter_tags: list[str]) -> bool: """ - Check if rule has all required tags (case-insensitive AND logic). + Check if rule has all required tags (AND logic). Args: - rule_tags: List of tags from the rule (already lowercase from parsing) - filter_tags: List of tags to filter by + rule_tags: List of tags from the rule (already normalized to lowercase) + filter_tags: List of tags to filter by (already normalized to lowercase) Returns: True if rule has all filter tags (or no filter), False otherwise @@ -50,7 +50,7 @@ def matches_tag_filter(rule_tags: list[str], filter_tags: list[str]) -> bool: if not filter_tags: return True # No filter means all pass - return all(tag.lower() in rule_tags for tag in filter_tags) + return all(tag in rule_tags for tag in filter_tags) def update_skill_md(language_to_rules: dict[str, list[str]], skill_path: str) -> None: @@ -351,10 +351,10 @@ def _resolve_source_paths(args) -> list[Path]: # Convert all sources aggregated = {"success": [], "errors": [], "skipped": []} - # Parse comma-separated tags + # Parse comma-separated tags and normalize to lowercase filter_tags = None if cli_args.tags: - filter_tags = [tag.strip() for tag in cli_args.tags.split(",") if tag.strip()] + filter_tags = [tag.strip().lower() for tag in cli_args.tags.split(",") if tag.strip()] # Print tag filter info if active if filter_tags: diff --git a/src/utils.py b/src/utils.py index cc64646..b37d2e9 100644 --- a/src/utils.py +++ b/src/utils.py @@ -62,20 +62,27 @@ def validate_tags(tags, filename=None) -> list[str]: Validate tags list and return normalized (lowercase) tags. Args: - tags: The tags value to validate (should be a list) + tags: The tags value to validate (should be a non-empty list) filename: Optional filename for better error messages Returns: List of normalized (lowercase) tags Raises: - ValueError: If tags are invalid (wrong type, contain whitespace, empty, etc.) + ValueError: If tags are invalid (wrong type, empty list, contain whitespace, etc.) + + Note: + An empty tags list (tags: []) is considered invalid. If you have no tags, + omit the 'tags' field entirely from the frontmatter. """ context = f" in {filename}" if filename else "" if not isinstance(tags, list): raise ValueError(f"'tags' must be a list{context}") + if not tags: + raise ValueError(f"'tags' list cannot be empty{context}. Omit the field if you have no tags.") + normalized = [] for tag in tags: if not isinstance(tag, str): @@ -89,7 +96,7 @@ def validate_tags(tags, filename=None) -> list[str]: normalized.append(tag.lower()) - return normalized + return list(set(normalized)) def get_version_from_pyproject() -> str: From 09839b8bcfeda3fa01eaf432a5264865914387b6 Mon Sep 17 00:00:00 2001 From: Thomas Bartlett <67928676+thomas-bartlett@users.noreply.github.com> Date: Tue, 18 Nov 2025 11:58:59 -0500 Subject: [PATCH 14/40] Added tag system for rule categorization and filtering. --- .../codeguard-0-additional-cryptography.md | 3 +++ sources/core/codeguard-0-api-web-services.md | 4 +--- .../core/codeguard-0-authentication-mfa.md | 2 +- .../codeguard-0-client-side-web-security.md | 2 ++ ...eguard-0-cloud-orchestration-kubernetes.md | 2 ++ sources/core/codeguard-0-data-storage.md | 3 +++ .../codeguard-0-devops-ci-cd-containers.md | 2 ++ sources/core/codeguard-0-iac-security.md | 2 ++ .../codeguard-0-input-validation-injection.md | 2 ++ sources/core/codeguard-0-logging.md | 2 ++ .../codeguard-0-privacy-data-protection.md | 2 ++ ...eguard-0-session-management-and-cookies.md | 3 +++ .../core/codeguard-1-digital-certificates.md | 2 ++ .../core/codeguard-1-hardcoded-credentials.md | 2 ++ src/tag_mappings.py | 21 +++++++++++++++++++ src/validate_unified_rules.py | 7 ++++++- 16 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 src/tag_mappings.py diff --git a/sources/core/codeguard-0-additional-cryptography.md b/sources/core/codeguard-0-additional-cryptography.md index 6bcb4fd..db5edae 100644 --- a/sources/core/codeguard-0-additional-cryptography.md +++ b/sources/core/codeguard-0-additional-cryptography.md @@ -14,6 +14,9 @@ languages: - typescript - xml - yaml +tags: +- data-security +- secrets alwaysApply: false --- diff --git a/sources/core/codeguard-0-api-web-services.md b/sources/core/codeguard-0-api-web-services.md index 0539f56..2c78e45 100644 --- a/sources/core/codeguard-0-api-web-services.md +++ b/sources/core/codeguard-0-api-web-services.md @@ -13,9 +13,7 @@ languages: - xml - yaml tags: -- api -- web-security -- microservices +- web alwaysApply: false --- diff --git a/sources/core/codeguard-0-authentication-mfa.md b/sources/core/codeguard-0-authentication-mfa.md index ff8cd20..580af4e 100644 --- a/sources/core/codeguard-0-authentication-mfa.md +++ b/sources/core/codeguard-0-authentication-mfa.md @@ -15,7 +15,7 @@ languages: - typescript tags: - authentication -- web-security +- web alwaysApply: false --- diff --git a/sources/core/codeguard-0-client-side-web-security.md b/sources/core/codeguard-0-client-side-web-security.md index 17a4e89..c2b0c68 100644 --- a/sources/core/codeguard-0-client-side-web-security.md +++ b/sources/core/codeguard-0-client-side-web-security.md @@ -8,6 +8,8 @@ languages: - php - typescript - vlang +tags: +- web alwaysApply: false --- diff --git a/sources/core/codeguard-0-cloud-orchestration-kubernetes.md b/sources/core/codeguard-0-cloud-orchestration-kubernetes.md index ec2e982..828edd9 100644 --- a/sources/core/codeguard-0-cloud-orchestration-kubernetes.md +++ b/sources/core/codeguard-0-cloud-orchestration-kubernetes.md @@ -4,6 +4,8 @@ description: Kubernetes hardening (RBAC, admission policies, network policies, s languages: - javascript - yaml +tags: +- infrastructure alwaysApply: false --- diff --git a/sources/core/codeguard-0-data-storage.md b/sources/core/codeguard-0-data-storage.md index 6bd68f5..e01057b 100644 --- a/sources/core/codeguard-0-data-storage.md +++ b/sources/core/codeguard-0-data-storage.md @@ -6,6 +6,9 @@ languages: - javascript - sql - yaml +tags: +- data-security +- infrastructure alwaysApply: false --- diff --git a/sources/core/codeguard-0-devops-ci-cd-containers.md b/sources/core/codeguard-0-devops-ci-cd-containers.md index 1db3562..52bb26c 100644 --- a/sources/core/codeguard-0-devops-ci-cd-containers.md +++ b/sources/core/codeguard-0-devops-ci-cd-containers.md @@ -8,6 +8,8 @@ languages: - shell - xml - yaml +tags: +- infrastructure alwaysApply: false --- diff --git a/sources/core/codeguard-0-iac-security.md b/sources/core/codeguard-0-iac-security.md index 0785120..17fe6de 100644 --- a/sources/core/codeguard-0-iac-security.md +++ b/sources/core/codeguard-0-iac-security.md @@ -8,6 +8,8 @@ languages: - ruby - shell - yaml +tags: +- infrastructure alwaysApply: false --- diff --git a/sources/core/codeguard-0-input-validation-injection.md b/sources/core/codeguard-0-input-validation-injection.md index 9ae2ab1..fc15368 100644 --- a/sources/core/codeguard-0-input-validation-injection.md +++ b/sources/core/codeguard-0-input-validation-injection.md @@ -14,6 +14,8 @@ languages: - shell - sql - typescript +tags: +- web alwaysApply: false --- diff --git a/sources/core/codeguard-0-logging.md b/sources/core/codeguard-0-logging.md index 659be01..2a354aa 100644 --- a/sources/core/codeguard-0-logging.md +++ b/sources/core/codeguard-0-logging.md @@ -5,6 +5,8 @@ languages: - c - javascript - yaml +tags: +- privacy alwaysApply: false --- diff --git a/sources/core/codeguard-0-privacy-data-protection.md b/sources/core/codeguard-0-privacy-data-protection.md index f28876d..22f522d 100644 --- a/sources/core/codeguard-0-privacy-data-protection.md +++ b/sources/core/codeguard-0-privacy-data-protection.md @@ -5,6 +5,8 @@ languages: - javascript - matlab - yaml +tags: +- privacy alwaysApply: false --- diff --git a/sources/core/codeguard-0-session-management-and-cookies.md b/sources/core/codeguard-0-session-management-and-cookies.md index be73bf8..e0d53e8 100644 --- a/sources/core/codeguard-0-session-management-and-cookies.md +++ b/sources/core/codeguard-0-session-management-and-cookies.md @@ -11,6 +11,9 @@ languages: - python - ruby - typescript +tags: +- authentication +- web alwaysApply: false --- diff --git a/sources/core/codeguard-1-digital-certificates.md b/sources/core/codeguard-1-digital-certificates.md index 3d73c70..c333fa2 100644 --- a/sources/core/codeguard-1-digital-certificates.md +++ b/sources/core/codeguard-1-digital-certificates.md @@ -1,6 +1,8 @@ --- description: Certificate Best Practices languages: [] +tags: +- secrets alwaysApply: true --- diff --git a/sources/core/codeguard-1-hardcoded-credentials.md b/sources/core/codeguard-1-hardcoded-credentials.md index 5f885ec..978d48a 100644 --- a/sources/core/codeguard-1-hardcoded-credentials.md +++ b/sources/core/codeguard-1-hardcoded-credentials.md @@ -1,6 +1,8 @@ --- description: No Hardcoded Credentials languages: [] +tags: +- secrets alwaysApply: true --- diff --git a/src/tag_mappings.py b/src/tag_mappings.py new file mode 100644 index 0000000..304992b --- /dev/null +++ b/src/tag_mappings.py @@ -0,0 +1,21 @@ +# Copyright 2025 Cisco Systems, Inc. and its affiliates +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Tag Mappings + +Centralized list of known tags for categorizing security rules. +""" + +# Known tags used in rules +# Add new tags here as they are introduced in rules +KNOWN_TAGS = { + "authentication", + "data-security", + "infrastructure", + "privacy", + "secrets", + "web", +} + diff --git a/src/validate_unified_rules.py b/src/validate_unified_rules.py index a30e56c..8fd454c 100755 --- a/src/validate_unified_rules.py +++ b/src/validate_unified_rules.py @@ -12,6 +12,7 @@ from pathlib import Path from language_mappings import LANGUAGE_TO_EXTENSIONS +from tag_mappings import KNOWN_TAGS from utils import parse_frontmatter_and_content, validate_tags @@ -57,7 +58,11 @@ def validate_rule(file_path: Path) -> dict[str, list[str]]: # Validate tags if present if "tags" in frontmatter: try: - validate_tags(frontmatter["tags"], file_path.name) + normalized_tags = validate_tags(frontmatter["tags"], file_path.name) + # Error on tags not in known list + unknown_tags = [tag for tag in normalized_tags if tag not in KNOWN_TAGS] + if unknown_tags: + errors.append(f"Unknown tags (add to KNOWN_TAGS): {', '.join(sorted(unknown_tags))}") except ValueError as e: errors.append(str(e)) From 46bac17e8723472a5286a83dd4473930c59192a4 Mon Sep 17 00:00:00 2001 From: Thomas Bartlett <67928676+thomas-bartlett@users.noreply.github.com> Date: Tue, 18 Nov 2025 12:11:20 -0500 Subject: [PATCH 15/40] Preserve tag order when deduplicating in validate_tags --- src/utils.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/utils.py b/src/utils.py index b37d2e9..a360e74 100644 --- a/src/utils.py +++ b/src/utils.py @@ -66,14 +66,17 @@ def validate_tags(tags, filename=None) -> list[str]: filename: Optional filename for better error messages Returns: - List of normalized (lowercase) tags + List of normalized (lowercase) tags with duplicates removed. + Original order is preserved. Raises: ValueError: If tags are invalid (wrong type, empty list, contain whitespace, etc.) Note: - An empty tags list (tags: []) is considered invalid. If you have no tags, - omit the 'tags' field entirely from the frontmatter. + - An empty tags list (tags: []) is considered invalid. If you have no tags, + omit the 'tags' field entirely from the frontmatter. + - Duplicate tags (after normalization) are automatically removed while + preserving the order of first occurrence. """ context = f" in {filename}" if filename else "" @@ -96,7 +99,7 @@ def validate_tags(tags, filename=None) -> list[str]: normalized.append(tag.lower()) - return list(set(normalized)) + return list(dict.fromkeys(normalized)) def get_version_from_pyproject() -> str: From 6eae5cdf4912eebaff7eaed3094d83bf3ce42ba4 Mon Sep 17 00:00:00 2001 From: Omar Santos Date: Wed, 19 Nov 2025 10:57:30 -0500 Subject: [PATCH 16/40] Updating documentation and README --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 69c48a2..1553ae9 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,12 @@ This project is an AI model-agnostic security framework and ruleset (internally AI coding agents are transforming software engineering, but this speed can introduce security vulnerabilities. Is your AI coding agent implementation introducing security vulnerabilities? -- ❌ Skipping input validation -- ❌ Hardcoding secrets and credentials -- ❌ Using weak cryptographic algorithms -- ❌ Relying on unsafe functions -- ❌ Missing authentication/authorization checks -- ❌ Missing any other security best practice +- Skipping input validation +- Hardcoding secrets and credentials +- Using weak cryptographic algorithms +- Relying on unsafe functions +- Missing authentication/authorization checks +- Missing any other security best practice Project CodeGuard solves this by embedding security best practices directly into AI coding agent workflows. @@ -31,14 +31,14 @@ Project CodeGuard is designed to integrate seamlessly across the entire AI codin Our rules cover essential security domains: -- **🔐 Cryptography**: Safe algorithms (including post-quantum cryptography), secure key management, certificate validation -- **🛡️ Input Validation**: SQL injection prevention, XSS protection, command injection defense -- **🔑 Authentication**: MFA best practices, OAuth/OIDC, secure session management -- **⚡ Authorization**: RBAC/ABAC, access control, IDOR prevention -- **📦 Supply Chain**: Dependency security, SBOM generation, vulnerability management -- **☁️ Cloud Security**: IaC hardening, container security, Kubernetes best practices -- **📱 Platform Security**: Mobile apps, web services, API security -- **🔍 Data Protection**: Privacy, encryption at rest/transit, secure storage +- **Cryptography**: Safe algorithms (including post-quantum cryptography), secure key management, certificate validation +- **Input Validation**: SQL injection prevention, XSS protection, command injection defense +- **Authentication**: MFA best practices, OAuth/OIDC, secure session management +- **Authorization**: RBAC/ABAC, access control, IDOR prevention +- **Supply Chain**: Dependency security, SBOM generation, vulnerability management +- **Cloud Security**: IaC hardening, container security, Kubernetes best practices +- **Platform Security**: Mobile apps, web services, API security +- **Data Protection**: Privacy, encryption at rest/transit, secure storage ## Quick Start @@ -97,4 +97,4 @@ This project uses dual licensing: This licensing approach ensures the security rules remain freely accessible and reusable while providing appropriate terms for software components. -Copyright © 2025 Cisco Systems, Inc. \ No newline at end of file +Copyright © 2025 Cisco Systems, Inc. From 33df7213767d7d31dd77546c829b46f10443e431 Mon Sep 17 00:00:00 2001 From: Omar Santos Date: Wed, 19 Nov 2025 10:58:32 -0500 Subject: [PATCH 17/40] Update index.md --- docs/index.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/index.md b/docs/index.md index 02a60f7..6aa3d18 100644 --- a/docs/index.md +++ b/docs/index.md @@ -6,12 +6,12 @@ AI coding agents are transforming software engineering, but this speed can introduce security vulnerabilities. Is your AI coding agent implementation introducing security vulnerabilities? -- ❌ Skipping input validation -- ❌ Hardcoding secrets and credentials -- ❌ Using weak cryptographic algorithms -- ❌ Relying on unsafe functions -- ❌ Missing authentication/authorization checks -- ❌ Missing any other security best practice +- Skipping input validation +- Hardcoding secrets and credentials +- Using weak cryptographic algorithms +- Relying on unsafe functions +- Missing authentication/authorization checks +- Missing any other security best practice Project CodeGuard solves this by embedding security best practices directly into AI coding agent workflows. @@ -28,14 +28,14 @@ This multi-stage methodology ensures that security considerations are woven thro Our rules cover essential security domains: -- **🔐 Cryptography**: Safe algorithms (including post-quantum cryptography), secure key management, certificate validation -- **🛡️ Input Validation**: SQL injection prevention, XSS protection, command injection defense -- **🔑 Authentication**: MFA best practices, OAuth/OIDC, secure session management -- **⚡ Authorization**: RBAC/ABAC, access control, IDOR prevention -- **📦 Supply Chain**: Dependency security, SBOM generation, vulnerability management -- **☁️ Cloud Security**: IaC hardening, container security, Kubernetes best practices -- **📱 Platform Security**: Mobile apps, web services, API security -- **🔍 Data Protection**: Privacy, encryption at rest/transit, secure storage +- **Cryptography**: Safe algorithms (including post-quantum cryptography), secure key management, certificate validation +- **Input Validation**: SQL injection prevention, XSS protection, command injection defense +- **Authentication**: MFA best practices, OAuth/OIDC, secure session management +- **Authorization**: RBAC/ABAC, access control, IDOR prevention +- **Supply Chain**: Dependency security, SBOM generation, vulnerability management +- **Cloud Security**: IaC hardening, container security, Kubernetes best practices +- **Platform Security**: Mobile apps, web services, API security +- **Data Protection**: Privacy, encryption at rest/transit, secure storage ## Quick Start From 45c47ef03c6450b609a5c4c5af68d1a3116644a1 Mon Sep 17 00:00:00 2001 From: Parveen Birthaliya Date: Sat, 29 Nov 2025 10:20:22 +0530 Subject: [PATCH 18/40] add feature support of antigravity,with unit test and integration test, with make changes in supported files --- README.md | 2 +- docs/getting-started.md | 8 +++++ src/convert_to_ide_formats.py | 3 +- src/formats/__init__.py | 2 ++ src/formats/antigravity.py | 66 +++++++++++++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 src/formats/antigravity.py diff --git a/README.md b/README.md index 1553ae9..088cdb8 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Get started in minutes: ## How It Works 1. **Security rules** are written in unified markdown format (`sources/` directory) -2. **Conversion tools** translate rules to IDE-specific formats (Cursor, Windsurf, Copilot, Claude Code) +2. **Conversion tools** translate rules to IDE-specific formats (Cursor, Windsurf, Copilot, Claude Code,antigravity) 3. **Release automation** packages rules into downloadable ZIP files 4. **AI assistants** reference these rules when generating or reviewing code 5. **Secure code** is produced automatically without developer intervention diff --git a/docs/getting-started.md b/docs/getting-started.md index 81add1a..8de909a 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -31,6 +31,11 @@ Before you begin, familiarize yourself with how rules work in your IDE: :material-book-open-page-variant: [GitHub Copilot Instructions](https://docs.github.com/en/copilot/how-tos/configure-custom-instructions/add-repository-instructions) +=== "Google AntiGravity" + Google AntiGravity uses `.agent/rules` for rule configuration. + + :material-book-open-page-variant: [Google AntiGravity Instructions](https://codelabs.developers.google.com/getting-started-google-antigravity#6) + ## Installation ### Option 1: Download Pre-built Rules (Recommended) @@ -95,6 +100,7 @@ uv run python src/convert_to_ide_formats.py --source core owasp cp -r dist/.cursor/ /path/to/your/project/ cp -r dist/.windsurf/ /path/to/your/project/ cp -r dist/.github/ /path/to/your/project/ +cp -r dist/.agent/ /path/to/your/project/ ``` ## Verify Installation @@ -103,6 +109,8 @@ After installation, your project structure should include: ``` your-project/ +├── .agent/ +│ └── rules/ ├── .cursor/ │ └── rules/ ├── .windsurf/ diff --git a/src/convert_to_ide_formats.py b/src/convert_to_ide_formats.py index 3c3cab3..1657366 100644 --- a/src/convert_to_ide_formats.py +++ b/src/convert_to_ide_formats.py @@ -16,7 +16,7 @@ from collections import defaultdict from converter import RuleConverter -from formats import CursorFormat, WindsurfFormat, CopilotFormat, ClaudeCodeFormat +from formats import CursorFormat, WindsurfFormat, CopilotFormat, ClaudeCodeFormat, AntigravityFormat from utils import get_version_from_pyproject from validate_versions import set_plugin_version, set_marketplace_version @@ -128,6 +128,7 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: CursorFormat(version), WindsurfFormat(version), CopilotFormat(version), + AntigravityFormat(version), ] # Only include Claude Code for core rules (committed plugin) diff --git a/src/formats/__init__.py b/src/formats/__init__.py index 4a2a7db..55cc0d0 100644 --- a/src/formats/__init__.py +++ b/src/formats/__init__.py @@ -30,6 +30,7 @@ from formats.windsurf import WindsurfFormat from formats.copilot import CopilotFormat from formats.claudecode import ClaudeCodeFormat +from formats.antigravity import AntigravityFormat __all__ = [ "BaseFormat", @@ -38,4 +39,5 @@ "WindsurfFormat", "CopilotFormat", "ClaudeCodeFormat", + "AntigravityFormat", ] diff --git a/src/formats/antigravity.py b/src/formats/antigravity.py new file mode 100644 index 0000000..fb83fe1 --- /dev/null +++ b/src/formats/antigravity.py @@ -0,0 +1,66 @@ +# Copyright 2025 Cisco Systems, Inc. and its affiliates +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Antigravity Format Implementation + +Generates .md workflow files for Google Antigravity with YAML frontmatter. +""" + +from formats.base import BaseFormat, ProcessedRule + + +class AntigravityFormat(BaseFormat): + """ + Google Antigravity format implementation (.md workflow files). + + Antigravity uses .md files with YAML frontmatter containing: + - description: Rule description (required by Antigravity spec) + + Workflows are stored in .agent/workflows/ and can be triggered + on-demand with /workflow-name in the Antigravity interface. + """ + + def get_format_name(self) -> str: + """Return Antigravity format identifier.""" + return "antigravity" + + def get_file_extension(self) -> str: + """Return Antigravity format file extension.""" + return ".md" + + def get_output_subpath(self) -> str: + """Return Antigravity output subdirectory.""" + return ".agent/rules" + + def generate(self, rule: ProcessedRule, globs: str) -> str: + """ + Generate Antigravity .md format with YAML frontmatter. + + Args: + rule: The processed rule to format + globs: Glob patterns for file matching (not used by Antigravity) + + Returns: + Formatted .md content with minimal frontmatter + + Note: + Antigravity workflows use simple markdown with description-only + frontmatter. Language/glob information is not needed as workflows + are triggered manually by the user. + """ + yaml_lines = [] + + # Add description (required by Antigravity spec) + desc = self._format_yaml_field("description", rule.description) + if desc: + yaml_lines.append(desc) + + # Optional: Add tags for categorization (if Antigravity supports it) + if rule.tags: + yaml_lines.append("tags:") + for tag in rule.tags: + yaml_lines.append(f"- {tag}") + + return self._build_yaml_frontmatter(yaml_lines, rule.content) \ No newline at end of file From 624ffb38aa96e762b89f06527ee4da18e1b75548 Mon Sep 17 00:00:00 2001 From: Omar Santos Date: Tue, 9 Dec 2025 22:22:05 -0500 Subject: [PATCH 19/40] Update src/formats/antigravity.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/formats/antigravity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/formats/antigravity.py b/src/formats/antigravity.py index fb83fe1..8d5071a 100644 --- a/src/formats/antigravity.py +++ b/src/formats/antigravity.py @@ -5,7 +5,7 @@ """ Antigravity Format Implementation -Generates .md workflow files for Google Antigravity with YAML frontmatter. +Generates .md rule files for Google Antigravity with YAML frontmatter. """ from formats.base import BaseFormat, ProcessedRule From d9b6956bacbb00245d202455ef41cf995dbd7dd4 Mon Sep 17 00:00:00 2001 From: Omar Santos Date: Tue, 9 Dec 2025 22:23:03 -0500 Subject: [PATCH 20/40] Update src/formats/antigravity.py This is true. We should be talking about `rule `files not `workflow` files here... Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/formats/antigravity.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/formats/antigravity.py b/src/formats/antigravity.py index 8d5071a..e29aa30 100644 --- a/src/formats/antigravity.py +++ b/src/formats/antigravity.py @@ -13,13 +13,13 @@ class AntigravityFormat(BaseFormat): """ - Google Antigravity format implementation (.md workflow files). + Google Antigravity format implementation (.md rule files). Antigravity uses .md files with YAML frontmatter containing: - description: Rule description (required by Antigravity spec) - Workflows are stored in .agent/workflows/ and can be triggered - on-demand with /workflow-name in the Antigravity interface. + Rules are stored in .agent/rules/ and can be triggered + on-demand with /rule-name in the Antigravity interface. """ def get_format_name(self) -> str: From 2897df044a6cb63e4ee8b9c3da73d30cfd64833a Mon Sep 17 00:00:00 2001 From: Parveen Birthaliya Date: Fri, 2 Jan 2026 20:39:22 +0530 Subject: [PATCH 21/40] Enhance Antigravity YAML frontmatter structure Updated Antigravity format implementation to include trigger types, glob patterns, and version in YAML frontmatter. --- src/formats/antigravity.py | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/formats/antigravity.py b/src/formats/antigravity.py index e29aa30..229181b 100644 --- a/src/formats/antigravity.py +++ b/src/formats/antigravity.py @@ -16,10 +16,14 @@ class AntigravityFormat(BaseFormat): Google Antigravity format implementation (.md rule files). Antigravity uses .md files with YAML frontmatter containing: - - description: Rule description (required by Antigravity spec) + - trigger: 'always_on' or 'glob' (activation type) + - globs: (if trigger is 'glob') File matching patterns + - description: Rule description + - version: Rule version - Rules are stored in .agent/rules/ and can be triggered - on-demand with /rule-name in the Antigravity interface. + Rules use activation types (Always On or Glob) to determine when + they apply, similar to Windsurf's implementation. + See: https://antigravity.google/docs/rules-workflows """ def get_format_name(self) -> str: @@ -40,27 +44,31 @@ def generate(self, rule: ProcessedRule, globs: str) -> str: Args: rule: The processed rule to format - globs: Glob patterns for file matching (not used by Antigravity) + globs: Glob patterns for file matching Returns: - Formatted .md content with minimal frontmatter + Formatted .md content with trigger, globs, description, and version Note: - Antigravity workflows use simple markdown with description-only - frontmatter. Language/glob information is not needed as workflows - are triggered manually by the user. + Antigravity rules use activation types: + - 'always_on': Rule applies to all files (when alwaysApply is true) + - 'glob': Rule applies to files matching glob patterns (language-specific) """ yaml_lines = [] + # Use trigger: always_on for rules that should always apply + if rule.always_apply: + yaml_lines.append("trigger: always_on") + else: + yaml_lines.append("trigger: glob") + yaml_lines.append(f"globs: {globs}") + # Add description (required by Antigravity spec) desc = self._format_yaml_field("description", rule.description) if desc: yaml_lines.append(desc) - - # Optional: Add tags for categorization (if Antigravity supports it) - if rule.tags: - yaml_lines.append("tags:") - for tag in rule.tags: - yaml_lines.append(f"- {tag}") - return self._build_yaml_frontmatter(yaml_lines, rule.content) \ No newline at end of file + # Add version + yaml_lines.append(f"version: {self.version}") + + return self._build_yaml_frontmatter(yaml_lines, rule.content) From 5d53cdd7332d3bbd757b17845d09fb831900ae4d Mon Sep 17 00:00:00 2001 From: Parveen Birthaliya Date: Tue, 6 Jan 2026 08:52:05 +0530 Subject: [PATCH 22/40] Fix typo in README.md for Antigravity --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 088cdb8..ceb5d6f 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Get started in minutes: ## How It Works 1. **Security rules** are written in unified markdown format (`sources/` directory) -2. **Conversion tools** translate rules to IDE-specific formats (Cursor, Windsurf, Copilot, Claude Code,antigravity) +2. **Conversion tools** translate rules to IDE-specific formats (Cursor, Windsurf, Copilot, Claude Code, Antigravity) 3. **Release automation** packages rules into downloadable ZIP files 4. **AI assistants** reference these rules when generating or reviewing code 5. **Secure code** is produced automatically without developer intervention From 95c24cbe341941d46ec935d586bdd1c88368f32e Mon Sep 17 00:00:00 2001 From: Parveen Birthaliya Date: Tue, 6 Jan 2026 08:55:51 +0530 Subject: [PATCH 23/40] Fix capitalization in Google Antigravity section --- docs/getting-started.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 8de909a..5588e58 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -31,10 +31,10 @@ Before you begin, familiarize yourself with how rules work in your IDE: :material-book-open-page-variant: [GitHub Copilot Instructions](https://docs.github.com/en/copilot/how-tos/configure-custom-instructions/add-repository-instructions) -=== "Google AntiGravity" - Google AntiGravity uses `.agent/rules` for rule configuration. +=== "Google Antigravity" + Google Antigravity uses `.agent/rules` for rule configuration. - :material-book-open-page-variant: [Google AntiGravity Instructions](https://codelabs.developers.google.com/getting-started-google-antigravity#6) + :material-book-open-page-variant: [Google Antigravity Instructions](https://codelabs.developers.google.com/getting-started-google-antigravity#6) ## Installation From 0d6c1d56f545ac973c9e0b6c887a6433690c67b1 Mon Sep 17 00:00:00 2001 From: Parveen Birthaliya Date: Tue, 6 Jan 2026 09:01:03 +0530 Subject: [PATCH 24/40] Fix link to Google Antigravity Instructions Updated link to Google Antigravity instructions. --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 5588e58..9609a28 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -34,7 +34,7 @@ Before you begin, familiarize yourself with how rules work in your IDE: === "Google Antigravity" Google Antigravity uses `.agent/rules` for rule configuration. - :material-book-open-page-variant: [Google Antigravity Instructions](https://codelabs.developers.google.com/getting-started-google-antigravity#6) + :material-book-open-page-variant: [Google Antigravity Instructions](https://codelabs.developers.google.com/getting-started-google-antigravity#8) ## Installation From b901ecc17c1ac5c77ca828a945419e3ed2f0e46d Mon Sep 17 00:00:00 2001 From: Parveen Birthaliya Date: Tue, 6 Jan 2026 09:18:41 +0530 Subject: [PATCH 25/40] Refine Antigravity format documentation Updated documentation to remove 'Google' from Antigravity references. --- src/formats/antigravity.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/formats/antigravity.py b/src/formats/antigravity.py index 229181b..a77ecbd 100644 --- a/src/formats/antigravity.py +++ b/src/formats/antigravity.py @@ -5,7 +5,7 @@ """ Antigravity Format Implementation -Generates .md rule files for Google Antigravity with YAML frontmatter. +Generates .md rule files for Antigravity with YAML frontmatter. """ from formats.base import BaseFormat, ProcessedRule @@ -13,7 +13,7 @@ class AntigravityFormat(BaseFormat): """ - Google Antigravity format implementation (.md rule files). + Antigravity format implementation (.md rule files). Antigravity uses .md files with YAML frontmatter containing: - trigger: 'always_on' or 'glob' (activation type) From d9d3e82683b3f048c34784ff2b8e45ae1a48db21 Mon Sep 17 00:00:00 2001 From: Parveen Birthaliya Date: Tue, 6 Jan 2026 09:21:56 +0530 Subject: [PATCH 26/40] Update Google Antigravity to Antigravity in docs --- docs/getting-started.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 9609a28..adcb1d0 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -31,10 +31,10 @@ Before you begin, familiarize yourself with how rules work in your IDE: :material-book-open-page-variant: [GitHub Copilot Instructions](https://docs.github.com/en/copilot/how-tos/configure-custom-instructions/add-repository-instructions) -=== "Google Antigravity" - Google Antigravity uses `.agent/rules` for rule configuration. +=== "Antigravity" + Antigravity uses `.agent/rules` for rule configuration. - :material-book-open-page-variant: [Google Antigravity Instructions](https://codelabs.developers.google.com/getting-started-google-antigravity#8) + :material-book-open-page-variant: [Antigravity Instructions](https://codelabs.developers.google.com/getting-started-google-antigravity#8) ## Installation From 0a01853d33050e7d6661f595b3d04684adc4fc1c Mon Sep 17 00:00:00 2001 From: Thomas Bartlett <67928676+thomas-bartlett@users.noreply.github.com> Date: Tue, 6 Jan 2026 17:03:18 -0500 Subject: [PATCH 27/40] Add Antigravity to CI workflows, docs, and fix CONTRIBUTING.md link --- .github/ISSUE_TEMPLATE/new-rule.yml | 2 +- .github/ISSUE_TEMPLATE/rule-feedback.yml | 1 + .github/workflows/build-ide-bundles.yml | 2 ++ .github/workflows/validate-rules.yml | 5 +++++ CONTRIBUTING.md | 4 ++-- docs/claude-code-skill-plugin.md | 2 +- docs/faq.md | 8 ++++---- docs/getting-started.md | 4 +++- 8 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/new-rule.yml b/.github/ISSUE_TEMPLATE/new-rule.yml index c9ed8bc..939eee6 100644 --- a/.github/ISSUE_TEMPLATE/new-rule.yml +++ b/.github/ISSUE_TEMPLATE/new-rule.yml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Thank you for your new rule request! Please provide as much detail as possible. Use any of the existing rules as a reference. You only have to provide the rule contents (markdown), not the rule metadata. We will handle the rest. In other words, convert your rule into all the formats (Cursor, Windsurf, Copilot). + Thank you for your new rule request! Please provide as much detail as possible. Use any of the existing rules as a reference. You only have to provide the rule contents (markdown), not the rule metadata. We will handle the rest. In other words, convert your rule into all the formats (Cursor, Windsurf, Copilot, Antigravity). - type: textarea id: description diff --git a/.github/ISSUE_TEMPLATE/rule-feedback.yml b/.github/ISSUE_TEMPLATE/rule-feedback.yml index 7abbc2c..ad24a97 100644 --- a/.github/ISSUE_TEMPLATE/rule-feedback.yml +++ b/.github/ISSUE_TEMPLATE/rule-feedback.yml @@ -26,6 +26,7 @@ body: - Cursor - GitHub Copilot - Windsurf + - Antigravity - Codex - Augment Code - Sourcegraph diff --git a/.github/workflows/build-ide-bundles.yml b/.github/workflows/build-ide-bundles.yml index f832a55..610bebe 100644 --- a/.github/workflows/build-ide-bundles.yml +++ b/.github/workflows/build-ide-bundles.yml @@ -54,6 +54,7 @@ jobs: zip -r ../ide-rules-cursor.zip .cursor/ zip -r ../ide-rules-windsurf.zip .windsurf/ zip -r ../ide-rules-copilot.zip .github/ + zip -r ../ide-rules-antigravity.zip .agent/ cd .. zip -r ide-rules-all.zip dist/ ls -lh ide-rules-*.zip @@ -67,5 +68,6 @@ jobs: ide-rules-cursor.zip \ ide-rules-windsurf.zip \ ide-rules-copilot.zip \ + ide-rules-antigravity.zip \ --clobber diff --git a/.github/workflows/validate-rules.yml b/.github/workflows/validate-rules.yml index 10d83f3..1ddfefe 100644 --- a/.github/workflows/validate-rules.yml +++ b/.github/workflows/validate-rules.yml @@ -89,6 +89,11 @@ jobs: exit 1 fi + if [ ! -d "test-output/.agent" ]; then + echo "❌ Antigravity rules not generated" + exit 1 + fi + echo "✅ All IDE formats generated successfully" - name: Check skills/ directory is up-to-date diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 73d9568..155a453 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -160,7 +160,7 @@ git push origin main **Note**: The conversion script automatically syncs the version from `pyproject.toml` to: - `.claude-plugin/plugin.json` and `marketplace.json` (Claude Code plugin metadata) -- All generated IDE rule files (Cursor `.mdc`, Windsurf `.md`, Copilot `.instructions.md`, Claude Code `.md`) +- All generated IDE rule files (Cursor `.mdc`, Windsurf `.md`, Copilot `.instructions.md`, Claude Code `.md`, Antigravity `.md`) This ensures version consistency across all artifacts. @@ -174,7 +174,7 @@ This ensures version consistency across all artifacts. GitHub Actions will automatically: - ✅ Validate versions match the tag -- ✅ Build IDE bundles (Cursor, Windsurf, Copilot) +- ✅ Build IDE bundles (Cursor, Windsurf, Copilot, Antigravity) - ✅ Upload ZIP artifacts to the release ## Testing Your Changes diff --git a/docs/claude-code-skill-plugin.md b/docs/claude-code-skill-plugin.md index c6caca7..e739cbb 100644 --- a/docs/claude-code-skill-plugin.md +++ b/docs/claude-code-skill-plugin.md @@ -290,7 +290,7 @@ uv run python src/convert_to_ide_formats.py This command: - Converts unified rules from `sources/` to IDE-specific formats - Generates `skills/` directory with the 22 core security rules (Claude Code plugin) -- Creates `dist/` with IDE-specific formats (Cursor, Windsurf, Copilot) +- Creates `dist/` with IDE-specific formats (Cursor, Windsurf, Copilot, Antigravity) **Note:** The Claude Code plugin (`skills/`) always contains only the 22 curated core rules. To build bundles with OWASP supplementary rules for other IDEs, use `--source core owasp`, but this only affects `dist/`, not `skills/`. diff --git a/docs/faq.md b/docs/faq.md index fc4bc2c..21a57d6 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -50,19 +50,19 @@ This FAQ document provides clear, concise answers to help developers seamlessly ## Q: How can I use the rules in my own AI agent? -**A:** You can use the rules in your own AI agent by creating a custom ruleset. You can create a custom ruleset by creating a new file in the `.cursor/rules`, `.windsurf/rules`, or `.github/instructions` directories and adding the rules you want to apply. You can also use the `project-codeguard/rules` repository as a template to create your own ruleset. +**A:** You can use the rules in your own AI agent by creating a custom ruleset. You can create a custom ruleset by creating a new file in the `.cursor/rules`, `.windsurf/rules`, `.github/instructions`, or `.agent/rules` directories and adding the rules you want to apply. You can also use the `project-codeguard/rules` repository as a template to create your own ruleset. --- ## Q: Why does the downloaded release folder appear empty? -**A:** After downloading and extracting the release, the folders may appear empty because the rule directories (`.cursor/`, `.windsurf/`, `.github/`) start with a dot (`.`) and are hidden by default on most operating systems. +**A:** After downloading and extracting the release, the folders may appear empty because the rule directories (`.cursor/`, `.windsurf/`, `.github/`, `.agent/`) start with a dot (`.`) and are hidden by default on most operating systems. **To show hidden files:** === "macOS" - In Finder, navigate to the extracted folder and press ++cmd+shift+period++ to toggle the visibility of hidden files. You should now see the `.cursor/`, `.windsurf/`, and `.github/` directories. + In Finder, navigate to the extracted folder and press ++cmd+shift+period++ to toggle the visibility of hidden files. You should now see the `.cursor/`, `.windsurf/`, `.github/`, and `.agent/` directories. === "Windows" @@ -76,7 +76,7 @@ This FAQ document provides clear, concise answers to help developers seamlessly In your file manager, press ++ctrl+h++ to toggle hidden files, or use `ls -la` in the terminal to view all files including hidden ones. -Once hidden files are visible, you can copy the appropriate directory (`.cursor/`, `.windsurf/`, or `.github/`) to your project root. +Once hidden files are visible, you can copy the appropriate directory (`.cursor/`, `.windsurf/`, `.github/`, or `.agent/`) to your project root. --- diff --git a/docs/getting-started.md b/docs/getting-started.md index adcb1d0..86c2ec7 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -45,11 +45,13 @@ Before you begin, familiarize yourself with how rules work in your IDE: - `ide-rules-cursor.zip` - Cursor only - `ide-rules-windsurf.zip` - Windsurf only - `ide-rules-copilot.zip` - GitHub Copilot only + - `ide-rules-antigravity.zip` - Antigravity only 2. **Extract**: Unzip the downloaded file 3. **Install**: Copy the relevant IDE-specific rules to your project root: - For **Cursor**: Copy `.cursor/` directory to your project - For **Windsurf**: Copy `.windsurf/` directory to your project - For **GitHub Copilot**: Copy `.github/` directory to your project + - For **Antigravity**: Copy `.agent/` directory to your project !!! tip "Repository Level Installation" @@ -165,7 +167,7 @@ To verify the rules are working: - **Review Rules**: Explore the security rules in your IDE's rules directory - **Test Integration**: Generate some code and see the security guidance in action - **Share Feedback**: Help us improve by [opening an issue](https://github.com/project-codeguard/rules/issues) -- **Contribute**: See [CONTRIBUTING.md](https://github.com/project-codeguard/rules/CONTRIBUTING.md) to contribute new rules or improvements +- **Contribute**: See [CONTRIBUTING.md](https://github.com/project-codeguard/rules/blob/main/CONTRIBUTING.md) to contribute new rules or improvements !!! success "You're Ready!" Project CodeGuard is now protecting your development workflow. The security rules will automatically guide AI assistants to generate more secure code. From 9e4551f545cb53bdd929cb8d0aeea03141187e52 Mon Sep 17 00:00:00 2001 From: Ramraj Bishnoie Date: Fri, 16 Jan 2026 12:49:19 -0500 Subject: [PATCH 28/40] docs: added instructions for adding codeguard to codex --- docs/getting-started.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/getting-started.md b/docs/getting-started.md index 86c2ec7..8dc3ac7 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -77,6 +77,37 @@ Claude Code uses a plugin system instead of manual file installation: The plugin will be automatically loaded and apply security rules to your code. See the [Claude Code Plugin documentation](claude-code-skill-plugin.md) for more details. +### OpenAI Codex Skills + +OpenAI Codex uses an agent skills system to extend capabilities with task-specific instructions. + +!!! warning "Prerequisites" + Make sure you're running the latest version of Codex before installing skills. + +To install Project CodeGuard as a Codex skill, open Codex and use the built-in skill installer: + +``` +$skill-installer install from https://github.com/project-codeguard/rules/tree/main/skills/software-security +``` + +Alternatively, you can manually clone the skill to your project: + +```bash +# Clone to your project's .codex/skills directory +mkdir -p .codex/skills +cd .codex/skills +git clone https://github.com/project-codeguard/rules.git temp +mv temp/skills/software-security ./ +rm -rf temp + +# Restart Codex to load the new skill +``` + +Once installed, you can invoke the skill explicitly in your prompts using `$software-security` or Codex will automatically use it when you're trying to write, review, or modify code. + +!!! info "Codex Skills Documentation" + For more information about Codex skills, skill locations, and configuration, see the [OpenAI Codex Skills documentation](https://developers.openai.com/codex/skills/). + ### Option 2: Build from Source If you want to customize or contribute to the rules: From b677e621cc1df209d6cd6ac16c4eca4bf41640d7 Mon Sep 17 00:00:00 2001 From: Ramraj Bishnoie Date: Fri, 16 Jan 2026 12:52:04 -0500 Subject: [PATCH 29/40] chore: add agent skills link --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 8dc3ac7..01efe69 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -79,7 +79,7 @@ The plugin will be automatically loaded and apply security rules to your code. S ### OpenAI Codex Skills -OpenAI Codex uses an agent skills system to extend capabilities with task-specific instructions. +OpenAI Codex uses [agent skills](https://agentskills.io/) to extend capabilities with task-specific instructions. !!! warning "Prerequisites" Make sure you're running the latest version of Codex before installing skills. From 704d9f276a1e8daade86711853e2b4391edcea25 Mon Sep 17 00:00:00 2001 From: Vinny Parla Date: Wed, 19 Nov 2025 09:46:17 -0500 Subject: [PATCH 30/40] Add core PQC rule with hybrid ML-KEM-768 guidance, ML-KEM selection/packet-size notes, ECDHE pairing examples, and TPM/mTLS guidance; regenerate Claude Code skills --- .../codeguard-1-post-quantum-cryptography.md | 98 +++++++++++++++++++ .../codeguard-1-post-quantum-cryptography.md | 96 ++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 skills/software-security/rules/codeguard-1-post-quantum-cryptography.md create mode 100644 sources/core/codeguard-1-post-quantum-cryptography.md diff --git a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md new file mode 100644 index 0000000..2b82d94 --- /dev/null +++ b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md @@ -0,0 +1,98 @@ +--- +description: Post-Quantum Cryptography (PQC) guidance and migration best practices +alwaysApply: true +--- + +rule_id: codeguard-1-post-quantum-cryptography + +# Post-Quantum Cryptography (PQC) + +Post-quantum cryptography protects long-lived secrets and future-proof communications against adversaries with quantum capabilities. Adopt PQC via supported libraries and protocols, favoring algorithm agility and incremental, interoperable rollout. + +## Goals +- Prefer standardized primitives and vendor-supported APIs; never implement custom crypto. +- Maintain algorithm agility (configurable algorithms, decoupled from business logic). +- Use hybrid key establishment during transition to preserve interoperability and defense-in-depth. +- Validate interoperability and performance before broad rollout. + +## Algorithm Selection (Key Establishment and Signatures) +- Key establishment (KEM): Use NIST-standardized ML-KEM (FIPS 203). Select parameter sets per risk and performance. +- Digital signatures: Use standardized ML-DSA (FIPS 204) where supported. Consider SLH-DSA (FIPS 205) where required by policy; account for larger signatures. + - Continue to rely on vendor-provided, audited implementations. Do not hardcode experimental group names or OIDs; use library-provided options. + +## ML-KEM Parameter Set Selection and Packet Size Considerations +- Minimum baseline: ML-KEM-768 should be the minimum consideration for general-purpose and multi-tenant deployments. Avoid ML-KEM-512 for broad use; only consider in highly constrained environments after explicit risk acceptance and compensating controls. +- High-assurance option: ML-KEM-1024 for regulated or high-assurance segments where added overhead is acceptable; validate capacity and latency budgets. +- Packet/message size impacts: + - ML-KEM public keys and ciphertexts increase handshake sizes; expect larger ClientHello/ServerHello and key exchange messages. + - Plan for path MTU and fragmentation: tune TLS record sizing as supported; validate middlebox tolerance to larger handshakes; monitor for handshake failures/timeouts. + - Assess QUIC vs TCP/TLS behavior in your environment; verify congestion and packetization with your vendor’s stack. + - Measure peak and P95 handshake size and latency before/after enabling hybrids; adjust proxy/load balancer limits as needed (e.g., header/body/initial settings). +- Operational guidance: + - Prefer vendor-documented hybrid groups or ciphersuites that include ML-KEM-768; avoid experimental or draft identifiers. + - Document fallback behavior and client capability detection; surface algorithm choices in configuration, not code. + - Ensure telemetry captures negotiated groups and retry causes to support safe rollouts and rollbacks. + +## Recommendation for Multi-Tenant Ecosystems +- Use a hybrid key establishment that combines a classical ECDHE group with ML-KEM-768. +- Rationale: ML-KEM-768 provides a widely recommended balance of security and performance suitable for shared, multi-tenant environments where diverse client capabilities and high request volume demand efficient handshakes. +- Implementation guidance: + - Enable the library’s supported hybrid mode pairing ML-KEM-768 with a classical group such as X25519 or secp256r1, as documented by the vendor. + - For example, use X25519 + ML-KEM-768 as the default hybrid pairing; use secp256r1 + ML-KEM-768 when X25519 is unavailable due to policy or platform constraints. + - Avoid bespoke or ad-hoc hybrids. Do not assemble hybrid handshakes manually; use stable, supported configuration flags or cipher/group selections provided by the stack. + +## Deprecations and Disallowed Predecessors +- Discontinue pre-standard, draft, or experiment-only Kyber-based hybrid groups and ciphers (often labeled generically as “Hybrid-Kyber” or with draft names like X25519Kyber…/P256_Kyber/CECPQ2). These predecessors must be replaced with a hybrid ML-KEM alternative provided by the vendor. +- Do not introduce new dependencies on legacy Kyber draft identifiers or OIDs. Migrate configuration and policy to ML-KEM-based hybrids in accordance with FIPS 203 and vendor guidance. + +## Protocol Guidance (e.g., TLS, SSH, HPKE) +- Prefer TLS 1.3 where hybrid KEMs are supported by your stack. New solutions should mandate using TLS 1.3 only and avoid TLS 1.2 and earlier versions where possible. +- Use vendor-documented hybrid groups that include ML-KEM-768 during transition. Avoid TLS 1.2 and earlier versions where possible. +- Only enable pure-PQC key establishment when interoperability with required clients is verified. Otherwise, deploy hybrid modes to preserve compatibility while adding PQC assurances. +- For signatures, adopt ML-DSA when supported by the protocol and vendor stack; validate message sizes, handshake overhead, and client support. + +## Key Management and Operations +- Generate keys via FIPS-validated or vendor-audited modules (KMS/HSM where available). Use a CSPRNG suitable for the platform. +- Maintain separate keys by purpose (encryption, signatures). Rotate per policy, compromise, or cryptoperiod changes. +- Store keys in KMS/HSM or secure vaults. Never hardcode keys or parameters; avoid plain environment variables for long-lived secrets. Prefer HW-based key protections over Software-only solutions. +- Ensure algorithm identifiers, parameters, and certificates reflect ML-KEM/ML-DSA selections from supported libraries; avoid stale Kyber-era identifiers. +- TPM-backed keys typically do not support ML-DSA today; for mTLS client/server authentication, continue to use EC algorithms supported by your TPM (e.g., ECDSA with secp256r1) until suitable ML-DSA TPM implementations are available in hardware. +- You can deploy hybrid ML-KEM key establishment while continuing to authenticate with ECDSA mTLS certificates; plan migration to ML-DSA-signed certificates when vendor hardware and ecosystem support becomes available. + +## Authenticators and Credentials +- FIDO2/WebAuthn: Platform and roaming authenticators today primarily use ECC (e.g., ECDSA/EdDSA). PQC attestation is not broadly deployed; continue using supported authenticators and track vendor roadmaps for PQC or hybrid attestation. +- TPM-backed mTLS: Current TPMs typically do not support ML-DSA. For client/server mTLS certificates, continue to use ECDSA (e.g., secp256r1) until suitable ML-DSA TPM implementations are available in hardware. +- Tokens and credentials: Hybrid ML-KEM key establishment protects transport but does not change token formats. Keep token signing on widely supported algorithms today and design for algorithm agility to adopt ML-DSA when ecosystems support it. +- Policy and configurability: Surface algorithm choices in configuration/policy (not code), define safe defaults and explicit fallback behavior, and capture telemetry for negotiated groups and failures. + +## Digital Signatures and Code/Artifact Signing +- Current practice: Use vetted, widely supported signatures (e.g., ECDSA P-256 or Ed25519) for code, container, and SBOM signing while PQC HSM/TPM support is nascent. +- Migration path: Plan for algorithm agility and adopt ML-DSA (FIPS 204) when your toolchain, HSMs/TPMs, and verifiers support it. Expect larger signatures and potential format/verification updates. +- Avoid drafts: Do not adopt draft/proprietary PQC signatures. Prefer standardized ML-DSA when available from audited libraries and hardware. +- Operational checks: Verify verifier support across CI/CD, registries, update servers, and clients; measure artifact size impact; ensure revocation and audit logs remain effective. + +## Adoptable Today (Open Source Projects) +- TLS 1.3 hybrid KEM with ML-KEM-768 is available using OpenSSL 3 with the oqs-provider (Open Quantum Safe) or OQS-OpenSSL builds. Use in controlled environments where you manage both client and server. Keep classical fallback enabled; note these builds are not yet FIPS-validated. +- SSH: OpenSSH supports a hybrid key exchange (sntrup761x25519). While not ML-KEM, it provides a PQC KEX option. Adopt per your policy until ML-KEM hybrids are supported in your SSH stack. +- HPKE and libraries: Open Quantum Safe libraries (liboqs) can be used to develop ML-KEM-based HPKE and related constructs. +- FIDO/WebAuthn (production today): Continue to use platform/hardware authenticators with classical algorithms (COSE ES256/ECDSA P-256; allow EdDSA where supported). Keep accepted COSE algorithms configurable on the RP/server, log negotiated algorithms, and maintain attestation/trust metadata. PQC authenticators/attestations are not broadly available today; track standards and vendor roadmaps. + +## Migration Plan +- Inventory cryptography usage and endpoints. Identify where key establishment and signature algorithms are configured or negotiated. +- Prioritize externally facing, high-value, and long-lived data flows for hybrid deployment first. +- Roll out hybrid ML-KEM-768 in stages with monitoring, fallbacks, and explicit client compat testing. +- Decommission predecessor “Hybrid-Kyber” configurations and remove any policy allowances or feature flags that enable them. + +## Implementation Checklist +- Hybrid key establishment enabled with ML-KEM-768 alongside a classical ECDHE group. +- No usage of predecessor draft Kyber hybrids; configurations updated to ML-KEM hybrids. +- Algorithms are configurable (algorithm agility) and surfaced in policy/config, not compiled into business logic. +- Keys generated with a CSPRNG in validated modules; stored in KMS/HSM; separated by purpose; rotation documented. +- Protocol versions and cipher/group selections align with vendor-documented, supported PQC options. +- Monitoring in place for handshake success rates, latency, and error codes after enabling hybrids. + +## Test Plan +- Interoperability tests across representative clients/agents for hybrid ML-KEM-768 handshakes; verify negotiation and fallback behavior. +- Negative tests: reject configurations attempting legacy Hybrid-Kyber or draft-only group names. +- Performance/regression tests: measure handshake latency and server CPU for peak and P95 after enabling hybrids. +- Configuration validation: confirm algorithm identifiers and parameters map to ML-KEM/ML-DSA in logs and diagnostics; ensure no stale Kyber draft identifiers remain. diff --git a/sources/core/codeguard-1-post-quantum-cryptography.md b/sources/core/codeguard-1-post-quantum-cryptography.md new file mode 100644 index 0000000..a99e200 --- /dev/null +++ b/sources/core/codeguard-1-post-quantum-cryptography.md @@ -0,0 +1,96 @@ +--- +description: Post-Quantum Cryptography (PQC) guidance and migration best practices +alwaysApply: true +--- + +# Post-Quantum Cryptography (PQC) + +Post-quantum cryptography protects long-lived secrets and future-proof communications against adversaries with quantum capabilities. Adopt PQC via supported libraries and protocols, favoring algorithm agility and incremental, interoperable rollout. + +## Goals +- Prefer standardized primitives and vendor-supported APIs; never implement custom crypto. +- Maintain algorithm agility (configurable algorithms, decoupled from business logic). +- Use hybrid key establishment during transition to preserve interoperability and defense-in-depth. +- Validate interoperability and performance before broad rollout. + +## Algorithm Selection (Key Establishment and Signatures) +- Key establishment (KEM): Use NIST-standardized ML-KEM (FIPS 203). Select parameter sets per risk and performance. +- Digital signatures: Use standardized ML-DSA (FIPS 204) where supported. Consider SLH-DSA (FIPS 205) where required by policy; account for larger signatures. + - Continue to rely on vendor-provided, audited implementations. Do not hardcode experimental group names or OIDs; use library-provided options. + +## ML-KEM Parameter Set Selection and Packet Size Considerations +- Minimum baseline: ML-KEM-768 should be the minimum consideration for general-purpose and multi-tenant deployments. Avoid ML-KEM-512 for broad use; only consider in highly constrained environments after explicit risk acceptance and compensating controls. +- High-assurance option: ML-KEM-1024 for regulated or high-assurance segments where added overhead is acceptable; validate capacity and latency budgets. +- Packet/message size impacts: + - ML-KEM public keys and ciphertexts increase handshake sizes; expect larger ClientHello/ServerHello and key exchange messages. + - Plan for path MTU and fragmentation: tune TLS record sizing as supported; validate middlebox tolerance to larger handshakes; monitor for handshake failures/timeouts. + - Assess QUIC vs TCP/TLS behavior in your environment; verify congestion and packetization with your vendor’s stack. + - Measure peak and P95 handshake size and latency before/after enabling hybrids; adjust proxy/load balancer limits as needed (e.g., header/body/initial settings). +- Operational guidance: + - Prefer vendor-documented hybrid groups or ciphersuites that include ML-KEM-768; avoid experimental or draft identifiers. + - Document fallback behavior and client capability detection; surface algorithm choices in configuration, not code. + - Ensure telemetry captures negotiated groups and retry causes to support safe rollouts and rollbacks. + +## Recommendation for Multi-Tenant Ecosystems +- Use a hybrid key establishment that combines a classical ECDHE group with ML-KEM-768. +- Rationale: ML-KEM-768 provides a widely recommended balance of security and performance suitable for shared, multi-tenant environments where diverse client capabilities and high request volume demand efficient handshakes. +- Implementation guidance: + - Enable the library’s supported hybrid mode pairing ML-KEM-768 with a classical group such as X25519 or secp256r1, as documented by the vendor. + - For example, use X25519 + ML-KEM-768 as the default hybrid pairing; use secp256r1 + ML-KEM-768 when X25519 is unavailable due to policy or platform constraints. + - Avoid bespoke or ad-hoc hybrids. Do not assemble hybrid handshakes manually; use stable, supported configuration flags or cipher/group selections provided by the stack. + +## Deprecations and Disallowed Predecessors +- Discontinue pre-standard, draft, or experiment-only Kyber-based hybrid groups and ciphers (often labeled generically as “Hybrid-Kyber” or with draft names like X25519Kyber…/P256_Kyber/CECPQ2). These predecessors must be replaced with a hybrid ML-KEM alternative provided by the vendor. +- Do not introduce new dependencies on legacy Kyber draft identifiers or OIDs. Migrate configuration and policy to ML-KEM-based hybrids in accordance with FIPS 203 and vendor guidance. + +## Protocol Guidance (e.g., TLS, SSH, HPKE) +- Prefer TLS 1.3 where hybrid KEMs are supported by your stack. New solutions should mandate using TLS 1.3 only and avoid TLS 1.2 and earlier versions where possible. +- Use vendor-documented hybrid groups that include ML-KEM-768 during transition. Avoid TLS 1.2 and earlier versions where possible. +- Only enable pure-PQC key establishment when interoperability with required clients is verified. Otherwise, deploy hybrid modes to preserve compatibility while adding PQC assurances. +- For signatures, adopt ML-DSA when supported by the protocol and vendor stack; validate message sizes, handshake overhead, and client support. + +## Key Management and Operations +- Generate keys via FIPS-validated or vendor-audited modules (KMS/HSM where available). Use a CSPRNG suitable for the platform. +- Maintain separate keys by purpose (encryption, signatures). Rotate per policy, compromise, or cryptoperiod changes. +- Store keys in KMS/HSM or secure vaults. Never hardcode keys or parameters; avoid plain environment variables for long-lived secrets. Prefer HW-based key protections over Software-only solutions. +- Ensure algorithm identifiers, parameters, and certificates reflect ML-KEM/ML-DSA selections from supported libraries; avoid stale Kyber-era identifiers. +- TPM-backed keys typically do not support ML-DSA today; for mTLS client/server authentication, continue to use EC algorithms supported by your TPM (e.g., ECDSA with secp256r1) until suitable ML-DSA TPM implementations are available in hardware. +- You can deploy hybrid ML-KEM key establishment while continuing to authenticate with ECDSA mTLS certificates; plan migration to ML-DSA-signed certificates when vendor hardware and ecosystem support becomes available. + +## Authenticators and Credentials +- FIDO2/WebAuthn: Platform and roaming authenticators today primarily use ECC (e.g., ECDSA/EdDSA). PQC attestation is not broadly deployed; continue using supported authenticators and track vendor roadmaps for PQC or hybrid attestation. +- TPM-backed mTLS: Current TPMs typically do not support ML-DSA. For client/server mTLS certificates, continue to use ECDSA (e.g., secp256r1) until suitable ML-DSA TPM implementations are available in hardware. +- Tokens and credentials: Hybrid ML-KEM key establishment protects transport but does not change token formats. Keep token signing on widely supported algorithms today and design for algorithm agility to adopt ML-DSA when ecosystems support it. +- Policy and configurability: Surface algorithm choices in configuration/policy (not code), define safe defaults and explicit fallback behavior, and capture telemetry for negotiated groups and failures. + +## Digital Signatures and Code/Artifact Signing +- Current practice: Use vetted, widely supported signatures (e.g., ECDSA P-256 or Ed25519) for code, container, and SBOM signing while PQC HSM/TPM support is nascent. +- Migration path: Plan for algorithm agility and adopt ML-DSA (FIPS 204) when your toolchain, HSMs/TPMs, and verifiers support it. Expect larger signatures and potential format/verification updates. +- Avoid drafts: Do not adopt draft/proprietary PQC signatures. Prefer standardized ML-DSA when available from audited libraries and hardware. +- Operational checks: Verify verifier support across CI/CD, registries, update servers, and clients; measure artifact size impact; ensure revocation and audit logs remain effective. + +## Adoptable Today (Open Source Projects) +- TLS 1.3 hybrid KEM with ML-KEM-768 is available using OpenSSL 3 with the oqs-provider (Open Quantum Safe) or OQS-OpenSSL builds. Use in controlled environments where you manage both client and server. Keep classical fallback enabled; note these builds are not yet FIPS-validated. +- SSH: OpenSSH supports a hybrid key exchange (sntrup761x25519). While not ML-KEM, it provides a PQC KEX option. Adopt per your policy until ML-KEM hybrids are supported in your SSH stack. +- HPKE and libraries: Open Quantum Safe libraries (liboqs) can be used to develop ML-KEM-based HPKE and related constructs. +- FIDO/WebAuthn (production today): Continue to use platform/hardware authenticators with classical algorithms (COSE ES256/ECDSA P-256; allow EdDSA where supported). Keep accepted COSE algorithms configurable on the RP/server, log negotiated algorithms, and maintain attestation/trust metadata. PQC authenticators/attestations are not broadly available today; track standards and vendor roadmaps. + +## Migration Plan +- Inventory cryptography usage and endpoints. Identify where key establishment and signature algorithms are configured or negotiated. +- Prioritize externally facing, high-value, and long-lived data flows for hybrid deployment first. +- Roll out hybrid ML-KEM-768 in stages with monitoring, fallbacks, and explicit client compat testing. +- Decommission predecessor “Hybrid-Kyber” configurations and remove any policy allowances or feature flags that enable them. + +## Implementation Checklist +- Hybrid key establishment enabled with ML-KEM-768 alongside a classical ECDHE group. +- No usage of predecessor draft Kyber hybrids; configurations updated to ML-KEM hybrids. +- Algorithms are configurable (algorithm agility) and surfaced in policy/config, not compiled into business logic. +- Keys generated with a CSPRNG in validated modules; stored in KMS/HSM; separated by purpose; rotation documented. +- Protocol versions and cipher/group selections align with vendor-documented, supported PQC options. +- Monitoring in place for handshake success rates, latency, and error codes after enabling hybrids. + +## Test Plan +- Interoperability tests across representative clients/agents for hybrid ML-KEM-768 handshakes; verify negotiation and fallback behavior. +- Negative tests: reject configurations attempting legacy Hybrid-Kyber or draft-only group names. +- Performance/regression tests: measure handshake latency and server CPU for peak and P95 after enabling hybrids. +- Configuration validation: confirm algorithm identifiers and parameters map to ML-KEM/ML-DSA in logs and diagnostics; ensure no stale Kyber draft identifiers remain. From ef560cdf919f1dd7f5df8357fbf05b460bdad9ae Mon Sep 17 00:00:00 2001 From: Vinny Parla Date: Thu, 20 Nov 2025 21:36:03 -0500 Subject: [PATCH 31/40] docs(pqc): concise implementation/testing focus; prohibit draft Kyber; add ML-KEM-512 constrained-use guidance; regenerate skills --- .../codeguard-1-post-quantum-cryptography.md | 114 +++++------------- .../codeguard-1-post-quantum-cryptography.md | 114 +++++------------- 2 files changed, 58 insertions(+), 170 deletions(-) diff --git a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md index 2b82d94..b17a344 100644 --- a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md +++ b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md @@ -7,92 +7,36 @@ rule_id: codeguard-1-post-quantum-cryptography # Post-Quantum Cryptography (PQC) -Post-quantum cryptography protects long-lived secrets and future-proof communications against adversaries with quantum capabilities. Adopt PQC via supported libraries and protocols, favoring algorithm agility and incremental, interoperable rollout. - -## Goals -- Prefer standardized primitives and vendor-supported APIs; never implement custom crypto. -- Maintain algorithm agility (configurable algorithms, decoupled from business logic). -- Use hybrid key establishment during transition to preserve interoperability and defense-in-depth. -- Validate interoperability and performance before broad rollout. - -## Algorithm Selection (Key Establishment and Signatures) -- Key establishment (KEM): Use NIST-standardized ML-KEM (FIPS 203). Select parameter sets per risk and performance. -- Digital signatures: Use standardized ML-DSA (FIPS 204) where supported. Consider SLH-DSA (FIPS 205) where required by policy; account for larger signatures. - - Continue to rely on vendor-provided, audited implementations. Do not hardcode experimental group names or OIDs; use library-provided options. - -## ML-KEM Parameter Set Selection and Packet Size Considerations -- Minimum baseline: ML-KEM-768 should be the minimum consideration for general-purpose and multi-tenant deployments. Avoid ML-KEM-512 for broad use; only consider in highly constrained environments after explicit risk acceptance and compensating controls. -- High-assurance option: ML-KEM-1024 for regulated or high-assurance segments where added overhead is acceptable; validate capacity and latency budgets. -- Packet/message size impacts: - - ML-KEM public keys and ciphertexts increase handshake sizes; expect larger ClientHello/ServerHello and key exchange messages. - - Plan for path MTU and fragmentation: tune TLS record sizing as supported; validate middlebox tolerance to larger handshakes; monitor for handshake failures/timeouts. - - Assess QUIC vs TCP/TLS behavior in your environment; verify congestion and packetization with your vendor’s stack. - - Measure peak and P95 handshake size and latency before/after enabling hybrids; adjust proxy/load balancer limits as needed (e.g., header/body/initial settings). -- Operational guidance: - - Prefer vendor-documented hybrid groups or ciphersuites that include ML-KEM-768; avoid experimental or draft identifiers. - - Document fallback behavior and client capability detection; surface algorithm choices in configuration, not code. - - Ensure telemetry captures negotiated groups and retry causes to support safe rollouts and rollbacks. - -## Recommendation for Multi-Tenant Ecosystems -- Use a hybrid key establishment that combines a classical ECDHE group with ML-KEM-768. -- Rationale: ML-KEM-768 provides a widely recommended balance of security and performance suitable for shared, multi-tenant environments where diverse client capabilities and high request volume demand efficient handshakes. -- Implementation guidance: - - Enable the library’s supported hybrid mode pairing ML-KEM-768 with a classical group such as X25519 or secp256r1, as documented by the vendor. - - For example, use X25519 + ML-KEM-768 as the default hybrid pairing; use secp256r1 + ML-KEM-768 when X25519 is unavailable due to policy or platform constraints. - - Avoid bespoke or ad-hoc hybrids. Do not assemble hybrid handshakes manually; use stable, supported configuration flags or cipher/group selections provided by the stack. - -## Deprecations and Disallowed Predecessors -- Discontinue pre-standard, draft, or experiment-only Kyber-based hybrid groups and ciphers (often labeled generically as “Hybrid-Kyber” or with draft names like X25519Kyber…/P256_Kyber/CECPQ2). These predecessors must be replaced with a hybrid ML-KEM alternative provided by the vendor. -- Do not introduce new dependencies on legacy Kyber draft identifiers or OIDs. Migrate configuration and policy to ML-KEM-based hybrids in accordance with FIPS 203 and vendor guidance. - -## Protocol Guidance (e.g., TLS, SSH, HPKE) -- Prefer TLS 1.3 where hybrid KEMs are supported by your stack. New solutions should mandate using TLS 1.3 only and avoid TLS 1.2 and earlier versions where possible. -- Use vendor-documented hybrid groups that include ML-KEM-768 during transition. Avoid TLS 1.2 and earlier versions where possible. -- Only enable pure-PQC key establishment when interoperability with required clients is verified. Otherwise, deploy hybrid modes to preserve compatibility while adding PQC assurances. -- For signatures, adopt ML-DSA when supported by the protocol and vendor stack; validate message sizes, handshake overhead, and client support. - -## Key Management and Operations -- Generate keys via FIPS-validated or vendor-audited modules (KMS/HSM where available). Use a CSPRNG suitable for the platform. -- Maintain separate keys by purpose (encryption, signatures). Rotate per policy, compromise, or cryptoperiod changes. -- Store keys in KMS/HSM or secure vaults. Never hardcode keys or parameters; avoid plain environment variables for long-lived secrets. Prefer HW-based key protections over Software-only solutions. -- Ensure algorithm identifiers, parameters, and certificates reflect ML-KEM/ML-DSA selections from supported libraries; avoid stale Kyber-era identifiers. -- TPM-backed keys typically do not support ML-DSA today; for mTLS client/server authentication, continue to use EC algorithms supported by your TPM (e.g., ECDSA with secp256r1) until suitable ML-DSA TPM implementations are available in hardware. -- You can deploy hybrid ML-KEM key establishment while continuing to authenticate with ECDSA mTLS certificates; plan migration to ML-DSA-signed certificates when vendor hardware and ecosystem support becomes available. - -## Authenticators and Credentials -- FIDO2/WebAuthn: Platform and roaming authenticators today primarily use ECC (e.g., ECDSA/EdDSA). PQC attestation is not broadly deployed; continue using supported authenticators and track vendor roadmaps for PQC or hybrid attestation. -- TPM-backed mTLS: Current TPMs typically do not support ML-DSA. For client/server mTLS certificates, continue to use ECDSA (e.g., secp256r1) until suitable ML-DSA TPM implementations are available in hardware. -- Tokens and credentials: Hybrid ML-KEM key establishment protects transport but does not change token formats. Keep token signing on widely supported algorithms today and design for algorithm agility to adopt ML-DSA when ecosystems support it. -- Policy and configurability: Surface algorithm choices in configuration/policy (not code), define safe defaults and explicit fallback behavior, and capture telemetry for negotiated groups and failures. - -## Digital Signatures and Code/Artifact Signing -- Current practice: Use vetted, widely supported signatures (e.g., ECDSA P-256 or Ed25519) for code, container, and SBOM signing while PQC HSM/TPM support is nascent. -- Migration path: Plan for algorithm agility and adopt ML-DSA (FIPS 204) when your toolchain, HSMs/TPMs, and verifiers support it. Expect larger signatures and potential format/verification updates. -- Avoid drafts: Do not adopt draft/proprietary PQC signatures. Prefer standardized ML-DSA when available from audited libraries and hardware. -- Operational checks: Verify verifier support across CI/CD, registries, update servers, and clients; measure artifact size impact; ensure revocation and audit logs remain effective. - -## Adoptable Today (Open Source Projects) -- TLS 1.3 hybrid KEM with ML-KEM-768 is available using OpenSSL 3 with the oqs-provider (Open Quantum Safe) or OQS-OpenSSL builds. Use in controlled environments where you manage both client and server. Keep classical fallback enabled; note these builds are not yet FIPS-validated. -- SSH: OpenSSH supports a hybrid key exchange (sntrup761x25519). While not ML-KEM, it provides a PQC KEX option. Adopt per your policy until ML-KEM hybrids are supported in your SSH stack. -- HPKE and libraries: Open Quantum Safe libraries (liboqs) can be used to develop ML-KEM-based HPKE and related constructs. -- FIDO/WebAuthn (production today): Continue to use platform/hardware authenticators with classical algorithms (COSE ES256/ECDSA P-256; allow EdDSA where supported). Keep accepted COSE algorithms configurable on the RP/server, log negotiated algorithms, and maintain attestation/trust metadata. PQC authenticators/attestations are not broadly available today; track standards and vendor roadmaps. - -## Migration Plan -- Inventory cryptography usage and endpoints. Identify where key establishment and signature algorithms are configured or negotiated. -- Prioritize externally facing, high-value, and long-lived data flows for hybrid deployment first. -- Roll out hybrid ML-KEM-768 in stages with monitoring, fallbacks, and explicit client compat testing. -- Decommission predecessor “Hybrid-Kyber” configurations and remove any policy allowances or feature flags that enable them. +Concise Code Guard focused on what to implement and how to test it. + +## Implementation (Do this) +- Enforce TLS 1.3 only (or later when available). +- Use vendor‑supported crypto APIs only; never implement custom crypto. Do not hand‑roll hybrids or hardcode experimental group names/OIDs. +- Hybrid KEM: enable vendor‑documented hybrids that include ML‑KEM‑768 with a classical ECDHE group (X25519 or secp256r1). Use ML‑KEM‑1024 for high‑assurance segments after validating overhead. +- Multi-tenant systems that share crypto resources across tenants should select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. X25519 is recommended for ECDHE. +- Avoid predecessors: remove legacy/draft “Hybrid‑Kyber” groups (e.g., CECPQ2; X25519Kyber…, P256_Kyber) and draft OIDs. +- Configuration, not code: expose algorithm choices in config/policy; document fallback behavior; keep a classical‑only fallback for incompatible clients if you don't control both client and server. +- Key management: use KMS/HSM; generate keys with a CSPRNG; separate encryption vs signatures; rotate per policy; never hardcode keys/parameters; avoid plain env vars for long‑lived secrets. +- Certificates/signatures: continue ECDSA (P‑256) for mTLS and code signing until ML‑DSA is supported by your stack; plan migration to ML‑DSA. +- Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune TLS record sizes and proxy/LB limits to avoid fragmentation and timeouts. +- SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. + +## Migration +- Inventory endpoints and crypto usage. +- Prioritize external/high‑value/long‑lived flows. +- Roll out hybrids in stages with metrics and rollback; remove predecessor configs after success criteria are met. ## Implementation Checklist -- Hybrid key establishment enabled with ML-KEM-768 alongside a classical ECDHE group. -- No usage of predecessor draft Kyber hybrids; configurations updated to ML-KEM hybrids. -- Algorithms are configurable (algorithm agility) and surfaced in policy/config, not compiled into business logic. -- Keys generated with a CSPRNG in validated modules; stored in KMS/HSM; separated by purpose; rotation documented. -- Protocol versions and cipher/group selections align with vendor-documented, supported PQC options. -- Monitoring in place for handshake success rates, latency, and error codes after enabling hybrids. +- Hybrid key establishment with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. +- Avoid ML‑KEM‑512 except in explicitly risk‑accepted, highly constrained device or network environments, with compensating controls and tight scope. +- No draft Kyber groups; only vendor‑documented ML‑KEM hybrids. +- Algorithm agility via configuration (not code); explicit fallback behavior. +- Keys via validated modules; separated by purpose; rotation policy in place. +- TLS version and group selections align with supported PQC options. +- Monitoring in place for handshake success/latency/errors and negotiated groups. ## Test Plan -- Interoperability tests across representative clients/agents for hybrid ML-KEM-768 handshakes; verify negotiation and fallback behavior. -- Negative tests: reject configurations attempting legacy Hybrid-Kyber or draft-only group names. -- Performance/regression tests: measure handshake latency and server CPU for peak and P95 after enabling hybrids. -- Configuration validation: confirm algorithm identifiers and parameters map to ML-KEM/ML-DSA in logs and diagnostics; ensure no stale Kyber draft identifiers remain. +- Interoperability: verify hybrid ML‑KEM‑768 and ML‑KEM‑1024 handshakes across representative clients; validate negotiated groups and fallback paths. +- Negative: reject legacy/draft Hybrid‑Kyber identifiers and misconfigured groups. +- Performance: measure handshake size and latency (peak and P95) and server CPU after enabling hybrids; tune record sizes and limits as needed. +- Configuration validation: confirm groups/algorithm identifiers in logs and diagnostics; ensure no stale Kyber‑era identifiers remain. diff --git a/sources/core/codeguard-1-post-quantum-cryptography.md b/sources/core/codeguard-1-post-quantum-cryptography.md index a99e200..9305ba2 100644 --- a/sources/core/codeguard-1-post-quantum-cryptography.md +++ b/sources/core/codeguard-1-post-quantum-cryptography.md @@ -5,92 +5,36 @@ alwaysApply: true # Post-Quantum Cryptography (PQC) -Post-quantum cryptography protects long-lived secrets and future-proof communications against adversaries with quantum capabilities. Adopt PQC via supported libraries and protocols, favoring algorithm agility and incremental, interoperable rollout. - -## Goals -- Prefer standardized primitives and vendor-supported APIs; never implement custom crypto. -- Maintain algorithm agility (configurable algorithms, decoupled from business logic). -- Use hybrid key establishment during transition to preserve interoperability and defense-in-depth. -- Validate interoperability and performance before broad rollout. - -## Algorithm Selection (Key Establishment and Signatures) -- Key establishment (KEM): Use NIST-standardized ML-KEM (FIPS 203). Select parameter sets per risk and performance. -- Digital signatures: Use standardized ML-DSA (FIPS 204) where supported. Consider SLH-DSA (FIPS 205) where required by policy; account for larger signatures. - - Continue to rely on vendor-provided, audited implementations. Do not hardcode experimental group names or OIDs; use library-provided options. - -## ML-KEM Parameter Set Selection and Packet Size Considerations -- Minimum baseline: ML-KEM-768 should be the minimum consideration for general-purpose and multi-tenant deployments. Avoid ML-KEM-512 for broad use; only consider in highly constrained environments after explicit risk acceptance and compensating controls. -- High-assurance option: ML-KEM-1024 for regulated or high-assurance segments where added overhead is acceptable; validate capacity and latency budgets. -- Packet/message size impacts: - - ML-KEM public keys and ciphertexts increase handshake sizes; expect larger ClientHello/ServerHello and key exchange messages. - - Plan for path MTU and fragmentation: tune TLS record sizing as supported; validate middlebox tolerance to larger handshakes; monitor for handshake failures/timeouts. - - Assess QUIC vs TCP/TLS behavior in your environment; verify congestion and packetization with your vendor’s stack. - - Measure peak and P95 handshake size and latency before/after enabling hybrids; adjust proxy/load balancer limits as needed (e.g., header/body/initial settings). -- Operational guidance: - - Prefer vendor-documented hybrid groups or ciphersuites that include ML-KEM-768; avoid experimental or draft identifiers. - - Document fallback behavior and client capability detection; surface algorithm choices in configuration, not code. - - Ensure telemetry captures negotiated groups and retry causes to support safe rollouts and rollbacks. - -## Recommendation for Multi-Tenant Ecosystems -- Use a hybrid key establishment that combines a classical ECDHE group with ML-KEM-768. -- Rationale: ML-KEM-768 provides a widely recommended balance of security and performance suitable for shared, multi-tenant environments where diverse client capabilities and high request volume demand efficient handshakes. -- Implementation guidance: - - Enable the library’s supported hybrid mode pairing ML-KEM-768 with a classical group such as X25519 or secp256r1, as documented by the vendor. - - For example, use X25519 + ML-KEM-768 as the default hybrid pairing; use secp256r1 + ML-KEM-768 when X25519 is unavailable due to policy or platform constraints. - - Avoid bespoke or ad-hoc hybrids. Do not assemble hybrid handshakes manually; use stable, supported configuration flags or cipher/group selections provided by the stack. - -## Deprecations and Disallowed Predecessors -- Discontinue pre-standard, draft, or experiment-only Kyber-based hybrid groups and ciphers (often labeled generically as “Hybrid-Kyber” or with draft names like X25519Kyber…/P256_Kyber/CECPQ2). These predecessors must be replaced with a hybrid ML-KEM alternative provided by the vendor. -- Do not introduce new dependencies on legacy Kyber draft identifiers or OIDs. Migrate configuration and policy to ML-KEM-based hybrids in accordance with FIPS 203 and vendor guidance. - -## Protocol Guidance (e.g., TLS, SSH, HPKE) -- Prefer TLS 1.3 where hybrid KEMs are supported by your stack. New solutions should mandate using TLS 1.3 only and avoid TLS 1.2 and earlier versions where possible. -- Use vendor-documented hybrid groups that include ML-KEM-768 during transition. Avoid TLS 1.2 and earlier versions where possible. -- Only enable pure-PQC key establishment when interoperability with required clients is verified. Otherwise, deploy hybrid modes to preserve compatibility while adding PQC assurances. -- For signatures, adopt ML-DSA when supported by the protocol and vendor stack; validate message sizes, handshake overhead, and client support. - -## Key Management and Operations -- Generate keys via FIPS-validated or vendor-audited modules (KMS/HSM where available). Use a CSPRNG suitable for the platform. -- Maintain separate keys by purpose (encryption, signatures). Rotate per policy, compromise, or cryptoperiod changes. -- Store keys in KMS/HSM or secure vaults. Never hardcode keys or parameters; avoid plain environment variables for long-lived secrets. Prefer HW-based key protections over Software-only solutions. -- Ensure algorithm identifiers, parameters, and certificates reflect ML-KEM/ML-DSA selections from supported libraries; avoid stale Kyber-era identifiers. -- TPM-backed keys typically do not support ML-DSA today; for mTLS client/server authentication, continue to use EC algorithms supported by your TPM (e.g., ECDSA with secp256r1) until suitable ML-DSA TPM implementations are available in hardware. -- You can deploy hybrid ML-KEM key establishment while continuing to authenticate with ECDSA mTLS certificates; plan migration to ML-DSA-signed certificates when vendor hardware and ecosystem support becomes available. - -## Authenticators and Credentials -- FIDO2/WebAuthn: Platform and roaming authenticators today primarily use ECC (e.g., ECDSA/EdDSA). PQC attestation is not broadly deployed; continue using supported authenticators and track vendor roadmaps for PQC or hybrid attestation. -- TPM-backed mTLS: Current TPMs typically do not support ML-DSA. For client/server mTLS certificates, continue to use ECDSA (e.g., secp256r1) until suitable ML-DSA TPM implementations are available in hardware. -- Tokens and credentials: Hybrid ML-KEM key establishment protects transport but does not change token formats. Keep token signing on widely supported algorithms today and design for algorithm agility to adopt ML-DSA when ecosystems support it. -- Policy and configurability: Surface algorithm choices in configuration/policy (not code), define safe defaults and explicit fallback behavior, and capture telemetry for negotiated groups and failures. - -## Digital Signatures and Code/Artifact Signing -- Current practice: Use vetted, widely supported signatures (e.g., ECDSA P-256 or Ed25519) for code, container, and SBOM signing while PQC HSM/TPM support is nascent. -- Migration path: Plan for algorithm agility and adopt ML-DSA (FIPS 204) when your toolchain, HSMs/TPMs, and verifiers support it. Expect larger signatures and potential format/verification updates. -- Avoid drafts: Do not adopt draft/proprietary PQC signatures. Prefer standardized ML-DSA when available from audited libraries and hardware. -- Operational checks: Verify verifier support across CI/CD, registries, update servers, and clients; measure artifact size impact; ensure revocation and audit logs remain effective. - -## Adoptable Today (Open Source Projects) -- TLS 1.3 hybrid KEM with ML-KEM-768 is available using OpenSSL 3 with the oqs-provider (Open Quantum Safe) or OQS-OpenSSL builds. Use in controlled environments where you manage both client and server. Keep classical fallback enabled; note these builds are not yet FIPS-validated. -- SSH: OpenSSH supports a hybrid key exchange (sntrup761x25519). While not ML-KEM, it provides a PQC KEX option. Adopt per your policy until ML-KEM hybrids are supported in your SSH stack. -- HPKE and libraries: Open Quantum Safe libraries (liboqs) can be used to develop ML-KEM-based HPKE and related constructs. -- FIDO/WebAuthn (production today): Continue to use platform/hardware authenticators with classical algorithms (COSE ES256/ECDSA P-256; allow EdDSA where supported). Keep accepted COSE algorithms configurable on the RP/server, log negotiated algorithms, and maintain attestation/trust metadata. PQC authenticators/attestations are not broadly available today; track standards and vendor roadmaps. - -## Migration Plan -- Inventory cryptography usage and endpoints. Identify where key establishment and signature algorithms are configured or negotiated. -- Prioritize externally facing, high-value, and long-lived data flows for hybrid deployment first. -- Roll out hybrid ML-KEM-768 in stages with monitoring, fallbacks, and explicit client compat testing. -- Decommission predecessor “Hybrid-Kyber” configurations and remove any policy allowances or feature flags that enable them. +Concise Code Guard focused on what to implement and how to test it. + +## Implementation (Do this) +- Enforce TLS 1.3 only (or later when available). +- Use vendor‑supported crypto APIs only; never implement custom crypto. Do not hand‑roll hybrids or hardcode experimental group names/OIDs. +- Hybrid KEM: enable vendor‑documented hybrids that include ML‑KEM‑768 with a classical ECDHE group (X25519 or secp256r1). Use ML‑KEM‑1024 for high‑assurance segments after validating overhead. +- Multi-tenant systems that share crypto resources across tenants should select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. X25519 is recommended for ECDHE. +- Avoid predecessors: remove legacy/draft “Hybrid‑Kyber” groups (e.g., CECPQ2; X25519Kyber…, P256_Kyber) and draft OIDs. +- Configuration, not code: expose algorithm choices in config/policy; document fallback behavior; keep a classical‑only fallback for incompatible clients if you don't control both client and server. +- Key management: use KMS/HSM; generate keys with a CSPRNG; separate encryption vs signatures; rotate per policy; never hardcode keys/parameters; avoid plain env vars for long‑lived secrets. +- Certificates/signatures: continue ECDSA (P‑256) for mTLS and code signing until ML‑DSA is supported by your stack; plan migration to ML‑DSA. +- Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune TLS record sizes and proxy/LB limits to avoid fragmentation and timeouts. +- SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. + +## Migration +- Inventory endpoints and crypto usage. +- Prioritize external/high‑value/long‑lived flows. +- Roll out hybrids in stages with metrics and rollback; remove predecessor configs after success criteria are met. ## Implementation Checklist -- Hybrid key establishment enabled with ML-KEM-768 alongside a classical ECDHE group. -- No usage of predecessor draft Kyber hybrids; configurations updated to ML-KEM hybrids. -- Algorithms are configurable (algorithm agility) and surfaced in policy/config, not compiled into business logic. -- Keys generated with a CSPRNG in validated modules; stored in KMS/HSM; separated by purpose; rotation documented. -- Protocol versions and cipher/group selections align with vendor-documented, supported PQC options. -- Monitoring in place for handshake success rates, latency, and error codes after enabling hybrids. +- Hybrid key establishment with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. +- Avoid ML‑KEM‑512 except in explicitly risk‑accepted, highly constrained device or network environments, with compensating controls and tight scope. +- No draft Kyber groups; only vendor‑documented ML‑KEM hybrids. +- Algorithm agility via configuration (not code); explicit fallback behavior. +- Keys via validated modules; separated by purpose; rotation policy in place. +- TLS version and group selections align with supported PQC options. +- Monitoring in place for handshake success/latency/errors and negotiated groups. ## Test Plan -- Interoperability tests across representative clients/agents for hybrid ML-KEM-768 handshakes; verify negotiation and fallback behavior. -- Negative tests: reject configurations attempting legacy Hybrid-Kyber or draft-only group names. -- Performance/regression tests: measure handshake latency and server CPU for peak and P95 after enabling hybrids. -- Configuration validation: confirm algorithm identifiers and parameters map to ML-KEM/ML-DSA in logs and diagnostics; ensure no stale Kyber draft identifiers remain. +- Interoperability: verify hybrid ML‑KEM‑768 and ML‑KEM‑1024 handshakes across representative clients; validate negotiated groups and fallback paths. +- Negative: reject legacy/draft Hybrid‑Kyber identifiers and misconfigured groups. +- Performance: measure handshake size and latency (peak and P95) and server CPU after enabling hybrids; tune record sizes and limits as needed. +- Configuration validation: confirm groups/algorithm identifiers in logs and diagnostics; ensure no stale Kyber‑era identifiers remain. From a10c995814d6029269c26b73c22d3d5cfa51a689 Mon Sep 17 00:00:00 2001 From: Vinny Parla Date: Mon, 24 Nov 2025 12:40:42 -0500 Subject: [PATCH 32/40] docs(pqc): clarify AES unaffected by Shor; recommend AES-256; regenerate skills --- .../codeguard-1-post-quantum-cryptography.md | 26 ++++++++++--------- .../codeguard-1-post-quantum-cryptography.md | 26 ++++++++++--------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md index b17a344..bb4e54b 100644 --- a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md +++ b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md @@ -12,14 +12,16 @@ Concise Code Guard focused on what to implement and how to test it. ## Implementation (Do this) - Enforce TLS 1.3 only (or later when available). - Use vendor‑supported crypto APIs only; never implement custom crypto. Do not hand‑roll hybrids or hardcode experimental group names/OIDs. -- Hybrid KEM: enable vendor‑documented hybrids that include ML‑KEM‑768 with a classical ECDHE group (X25519 or secp256r1). Use ML‑KEM‑1024 for high‑assurance segments after validating overhead. + - Symmetric encryption: Shor's algorithm and quantum computers do not affect symmetric algorithms like AES; using AES‑256 keys (or stronger) is highly recommended. + - Hybrid KEM: enable vendor‑documented hybrids that include ML‑KEM‑768 with a classical ECDHE group (X25519 or secp256r1). Use ML‑KEM‑1024 for high‑assurance segments after validating overhead. - Multi-tenant systems that share crypto resources across tenants should select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. X25519 is recommended for ECDHE. -- Avoid predecessors: remove legacy/draft “Hybrid‑Kyber” groups (e.g., CECPQ2; X25519Kyber…, P256_Kyber) and draft OIDs. -- Configuration, not code: expose algorithm choices in config/policy; document fallback behavior; keep a classical‑only fallback for incompatible clients if you don't control both client and server. -- Key management: use KMS/HSM; generate keys with a CSPRNG; separate encryption vs signatures; rotate per policy; never hardcode keys/parameters; avoid plain env vars for long‑lived secrets. -- Certificates/signatures: continue ECDSA (P‑256) for mTLS and code signing until ML‑DSA is supported by your stack; plan migration to ML‑DSA. -- Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune TLS record sizes and proxy/LB limits to avoid fragmentation and timeouts. -- SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. + - Avoid predecessors: remove legacy/draft “Hybrid‑Kyber” groups (e.g., CECPQ2; X25519Kyber…, P256_Kyber) and draft OIDs. + - Configuration, not code: expose algorithm choices in config/policy; document fallback behavior; keep a classical‑only fallback for incompatible clients if you don't control both client and server. + - Key management: use KMS/HSM; generate keys with a CSPRNG; separate encryption vs signatures; rotate per policy; never hardcode keys/parameters; avoid plain env vars for long‑lived secrets; require hardware‑backed keys (HSM/TPM) for private key storage. + - Certificates/signatures: continue ECDSA (P‑256) for mTLS and code signing until hardware‑backed ML‑DSA is available in your stack (e.g., HSM or TPM); plan migration to ML‑DSA once supported. + - Hardware requirement for ML‑DSA: do not enable PQC ML‑DSA signatures using software‑only keys. Require HSM/TPM‑backed key storage and signing paths before migrating. + - Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune TLS record sizes and proxy/LB/concentrator limits to avoid fragmentation and timeouts. + - SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. ## Migration - Inventory endpoints and crypto usage. @@ -30,13 +32,13 @@ Concise Code Guard focused on what to implement and how to test it. - Hybrid key establishment with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. - Avoid ML‑KEM‑512 except in explicitly risk‑accepted, highly constrained device or network environments, with compensating controls and tight scope. - No draft Kyber groups; only vendor‑documented ML‑KEM hybrids. -- Algorithm agility via configuration (not code); explicit fallback behavior. -- Keys via validated modules; separated by purpose; rotation policy in place. -- TLS version and group selections align with supported PQC options. -- Monitoring in place for handshake success/latency/errors and negotiated groups. + - Algorithm agility via configuration (not code); explicit fallback behavior. + - Keys via validated modules; separated by purpose; rotation policy in place. + - Hardware‑backed key storage (HSM/TPM) required before enabling ML‑DSA signatures; continue ECDSA (P‑256) for mTLS/signing until hardware support exists. + - TLS version and group selections align with supported PQC options. + - Monitoring in place for handshake success/latency/errors and negotiated groups. ## Test Plan - Interoperability: verify hybrid ML‑KEM‑768 and ML‑KEM‑1024 handshakes across representative clients; validate negotiated groups and fallback paths. - Negative: reject legacy/draft Hybrid‑Kyber identifiers and misconfigured groups. - Performance: measure handshake size and latency (peak and P95) and server CPU after enabling hybrids; tune record sizes and limits as needed. -- Configuration validation: confirm groups/algorithm identifiers in logs and diagnostics; ensure no stale Kyber‑era identifiers remain. diff --git a/sources/core/codeguard-1-post-quantum-cryptography.md b/sources/core/codeguard-1-post-quantum-cryptography.md index 9305ba2..4280e07 100644 --- a/sources/core/codeguard-1-post-quantum-cryptography.md +++ b/sources/core/codeguard-1-post-quantum-cryptography.md @@ -10,14 +10,16 @@ Concise Code Guard focused on what to implement and how to test it. ## Implementation (Do this) - Enforce TLS 1.3 only (or later when available). - Use vendor‑supported crypto APIs only; never implement custom crypto. Do not hand‑roll hybrids or hardcode experimental group names/OIDs. -- Hybrid KEM: enable vendor‑documented hybrids that include ML‑KEM‑768 with a classical ECDHE group (X25519 or secp256r1). Use ML‑KEM‑1024 for high‑assurance segments after validating overhead. + - Symmetric encryption: Shor's algorithm and quantum computers do not affect symmetric algorithms like AES; using AES‑256 keys (or stronger) is highly recommended. + - Hybrid KEM: enable vendor‑documented hybrids that include ML‑KEM‑768 with a classical ECDHE group (X25519 or secp256r1). Use ML‑KEM‑1024 for high‑assurance segments after validating overhead. - Multi-tenant systems that share crypto resources across tenants should select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. X25519 is recommended for ECDHE. -- Avoid predecessors: remove legacy/draft “Hybrid‑Kyber” groups (e.g., CECPQ2; X25519Kyber…, P256_Kyber) and draft OIDs. -- Configuration, not code: expose algorithm choices in config/policy; document fallback behavior; keep a classical‑only fallback for incompatible clients if you don't control both client and server. -- Key management: use KMS/HSM; generate keys with a CSPRNG; separate encryption vs signatures; rotate per policy; never hardcode keys/parameters; avoid plain env vars for long‑lived secrets. -- Certificates/signatures: continue ECDSA (P‑256) for mTLS and code signing until ML‑DSA is supported by your stack; plan migration to ML‑DSA. -- Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune TLS record sizes and proxy/LB limits to avoid fragmentation and timeouts. -- SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. + - Avoid predecessors: remove legacy/draft “Hybrid‑Kyber” groups (e.g., CECPQ2; X25519Kyber…, P256_Kyber) and draft OIDs. + - Configuration, not code: expose algorithm choices in config/policy; document fallback behavior; keep a classical‑only fallback for incompatible clients if you don't control both client and server. + - Key management: use KMS/HSM; generate keys with a CSPRNG; separate encryption vs signatures; rotate per policy; never hardcode keys/parameters; avoid plain env vars for long‑lived secrets; require hardware‑backed keys (HSM/TPM) for private key storage. + - Certificates/signatures: continue ECDSA (P‑256) for mTLS and code signing until hardware‑backed ML‑DSA is available in your stack (e.g., HSM or TPM); plan migration to ML‑DSA once supported. + - Hardware requirement for ML‑DSA: do not enable PQC ML‑DSA signatures using software‑only keys. Require HSM/TPM‑backed key storage and signing paths before migrating. + - Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune TLS record sizes and proxy/LB/concentrator limits to avoid fragmentation and timeouts. + - SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. ## Migration - Inventory endpoints and crypto usage. @@ -28,13 +30,13 @@ Concise Code Guard focused on what to implement and how to test it. - Hybrid key establishment with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. - Avoid ML‑KEM‑512 except in explicitly risk‑accepted, highly constrained device or network environments, with compensating controls and tight scope. - No draft Kyber groups; only vendor‑documented ML‑KEM hybrids. -- Algorithm agility via configuration (not code); explicit fallback behavior. -- Keys via validated modules; separated by purpose; rotation policy in place. -- TLS version and group selections align with supported PQC options. -- Monitoring in place for handshake success/latency/errors and negotiated groups. + - Algorithm agility via configuration (not code); explicit fallback behavior. + - Keys via validated modules; separated by purpose; rotation policy in place. + - Hardware‑backed key storage (HSM/TPM) required before enabling ML‑DSA signatures; continue ECDSA (P‑256) for mTLS/signing until hardware support exists. + - TLS version and group selections align with supported PQC options. + - Monitoring in place for handshake success/latency/errors and negotiated groups. ## Test Plan - Interoperability: verify hybrid ML‑KEM‑768 and ML‑KEM‑1024 handshakes across representative clients; validate negotiated groups and fallback paths. - Negative: reject legacy/draft Hybrid‑Kyber identifiers and misconfigured groups. - Performance: measure handshake size and latency (peak and P95) and server CPU after enabling hybrids; tune record sizes and limits as needed. -- Configuration validation: confirm groups/algorithm identifiers in logs and diagnostics; ensure no stale Kyber‑era identifiers remain. From 4f41e61c324b3a5cfb9e2077f42546bc41edfa67 Mon Sep 17 00:00:00 2001 From: Vinny Parla Date: Tue, 25 Nov 2025 11:24:36 -0500 Subject: [PATCH 33/40] docs(pqc): add IPsec guidance; reference IKEv2 PQC RFCs 9242/9370; regenerate skills --- .../codeguard-1-post-quantum-cryptography.md | 20 ++++++++++--------- .../codeguard-1-post-quantum-cryptography.md | 20 ++++++++++--------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md index bb4e54b..8d2fa19 100644 --- a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md +++ b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md @@ -11,17 +11,19 @@ Concise Code Guard focused on what to implement and how to test it. ## Implementation (Do this) - Enforce TLS 1.3 only (or later when available). +- IPsec: Enforce IKEv2 only; use ESP with AEAD (e.g. AES‑256‑GCM or stronger); require PFS via ECDHE (X25519 or secp256r1); use SHA‑256+ for IKE PRF/auth; disable IKEv1 and legacy suites (3DES, DES, MD5, SHA‑1, AES‑CBC); +- IKEv2 PQC support: implement RFC 9242 (IKEv2 Intermediate Exchange) and RFC 9370 (Multiple Key Exchanges in IKEv2) to enable hybrid PQC + ECDHE and handle larger exchanges. Select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. - Use vendor‑supported crypto APIs only; never implement custom crypto. Do not hand‑roll hybrids or hardcode experimental group names/OIDs. - - Symmetric encryption: Shor's algorithm and quantum computers do not affect symmetric algorithms like AES; using AES‑256 keys (or stronger) is highly recommended. - - Hybrid KEM: enable vendor‑documented hybrids that include ML‑KEM‑768 with a classical ECDHE group (X25519 or secp256r1). Use ML‑KEM‑1024 for high‑assurance segments after validating overhead. +- Symmetric encryption: Shor's algorithm and quantum computers do not affect symmetric algorithms like AES; using AES‑256 keys (or stronger) is strongly recommended. +- Hybrid KEM: enable vendor‑documented hybrids that include ML‑KEM‑768 with a classical ECDHE group (X25519 or secp256r1). Use ML‑KEM‑1024 for high‑assurance segments after validating overhead. - Multi-tenant systems that share crypto resources across tenants should select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. X25519 is recommended for ECDHE. - - Avoid predecessors: remove legacy/draft “Hybrid‑Kyber” groups (e.g., CECPQ2; X25519Kyber…, P256_Kyber) and draft OIDs. - - Configuration, not code: expose algorithm choices in config/policy; document fallback behavior; keep a classical‑only fallback for incompatible clients if you don't control both client and server. - - Key management: use KMS/HSM; generate keys with a CSPRNG; separate encryption vs signatures; rotate per policy; never hardcode keys/parameters; avoid plain env vars for long‑lived secrets; require hardware‑backed keys (HSM/TPM) for private key storage. - - Certificates/signatures: continue ECDSA (P‑256) for mTLS and code signing until hardware‑backed ML‑DSA is available in your stack (e.g., HSM or TPM); plan migration to ML‑DSA once supported. - - Hardware requirement for ML‑DSA: do not enable PQC ML‑DSA signatures using software‑only keys. Require HSM/TPM‑backed key storage and signing paths before migrating. - - Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune TLS record sizes and proxy/LB/concentrator limits to avoid fragmentation and timeouts. - - SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. +- Avoid predecessors: remove legacy/draft “Hybrid‑Kyber” groups (e.g., CECPQ2; X25519Kyber…, P256_Kyber) and draft OIDs. +- Configuration, not code: expose algorithm choices in config/policy; document fallback behavior; keep a classical‑only fallback for incompatible clients if you don't control both client and server. +- Key management: use KMS/HSM; generate keys with a CSPRNG; separate encryption vs signatures; rotate per policy; never hardcode keys/parameters; avoid plain env vars for long‑lived secrets; require hardware‑backed keys (HSM/TPM) for private key storage. +- Certificates/signatures: continue ECDSA (P‑256) for mTLS and code signing until hardware‑backed ML‑DSA is available in your stack (e.g., HSM or TPM); plan migration to ML‑DSA once supported. +- Hardware requirement for ML‑DSA: do not enable PQC ML‑DSA signatures using software‑only keys. Require HSM/TPM‑backed key storage and signing paths before migrating. +- Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune TLS record sizes and proxy/LB/concentrator limits to avoid fragmentation and timeouts. +- SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. ## Migration - Inventory endpoints and crypto usage. diff --git a/sources/core/codeguard-1-post-quantum-cryptography.md b/sources/core/codeguard-1-post-quantum-cryptography.md index 4280e07..8c4c573 100644 --- a/sources/core/codeguard-1-post-quantum-cryptography.md +++ b/sources/core/codeguard-1-post-quantum-cryptography.md @@ -9,17 +9,19 @@ Concise Code Guard focused on what to implement and how to test it. ## Implementation (Do this) - Enforce TLS 1.3 only (or later when available). +- IPsec: Enforce IKEv2 only; use ESP with AEAD (e.g. AES‑256‑GCM or stronger); require PFS via ECDHE (X25519 or secp256r1); use SHA‑256+ for IKE PRF/auth; disable IKEv1 and legacy suites (3DES, DES, MD5, SHA‑1, AES‑CBC); +- IKEv2 PQC support: implement RFC 9242 (IKEv2 Intermediate Exchange) and RFC 9370 (Multiple Key Exchanges in IKEv2) to enable hybrid PQC + ECDHE and handle larger exchanges. Select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. - Use vendor‑supported crypto APIs only; never implement custom crypto. Do not hand‑roll hybrids or hardcode experimental group names/OIDs. - - Symmetric encryption: Shor's algorithm and quantum computers do not affect symmetric algorithms like AES; using AES‑256 keys (or stronger) is highly recommended. - - Hybrid KEM: enable vendor‑documented hybrids that include ML‑KEM‑768 with a classical ECDHE group (X25519 or secp256r1). Use ML‑KEM‑1024 for high‑assurance segments after validating overhead. +- Symmetric encryption: Shor's algorithm and quantum computers do not affect symmetric algorithms like AES; using AES‑256 keys (or stronger) is strongly recommended. +- Hybrid KEM: enable vendor‑documented hybrids that include ML‑KEM‑768 with a classical ECDHE group (X25519 or secp256r1). Use ML‑KEM‑1024 for high‑assurance segments after validating overhead. - Multi-tenant systems that share crypto resources across tenants should select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. X25519 is recommended for ECDHE. - - Avoid predecessors: remove legacy/draft “Hybrid‑Kyber” groups (e.g., CECPQ2; X25519Kyber…, P256_Kyber) and draft OIDs. - - Configuration, not code: expose algorithm choices in config/policy; document fallback behavior; keep a classical‑only fallback for incompatible clients if you don't control both client and server. - - Key management: use KMS/HSM; generate keys with a CSPRNG; separate encryption vs signatures; rotate per policy; never hardcode keys/parameters; avoid plain env vars for long‑lived secrets; require hardware‑backed keys (HSM/TPM) for private key storage. - - Certificates/signatures: continue ECDSA (P‑256) for mTLS and code signing until hardware‑backed ML‑DSA is available in your stack (e.g., HSM or TPM); plan migration to ML‑DSA once supported. - - Hardware requirement for ML‑DSA: do not enable PQC ML‑DSA signatures using software‑only keys. Require HSM/TPM‑backed key storage and signing paths before migrating. - - Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune TLS record sizes and proxy/LB/concentrator limits to avoid fragmentation and timeouts. - - SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. +- Avoid predecessors: remove legacy/draft “Hybrid‑Kyber” groups (e.g., CECPQ2; X25519Kyber…, P256_Kyber) and draft OIDs. +- Configuration, not code: expose algorithm choices in config/policy; document fallback behavior; keep a classical‑only fallback for incompatible clients if you don't control both client and server. +- Key management: use KMS/HSM; generate keys with a CSPRNG; separate encryption vs signatures; rotate per policy; never hardcode keys/parameters; avoid plain env vars for long‑lived secrets; require hardware‑backed keys (HSM/TPM) for private key storage. +- Certificates/signatures: continue ECDSA (P‑256) for mTLS and code signing until hardware‑backed ML‑DSA is available in your stack (e.g., HSM or TPM); plan migration to ML‑DSA once supported. +- Hardware requirement for ML‑DSA: do not enable PQC ML‑DSA signatures using software‑only keys. Require HSM/TPM‑backed key storage and signing paths before migrating. +- Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune TLS record sizes and proxy/LB/concentrator limits to avoid fragmentation and timeouts. +- SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. ## Migration - Inventory endpoints and crypto usage. From 93b70d33bef2f3932a46b07278dc16e0658b67d9 Mon Sep 17 00:00:00 2001 From: Vinny Parla Date: Tue, 25 Nov 2025 12:21:08 -0500 Subject: [PATCH 34/40] docs(pqc): clarify DTLS; enforce (D)TLS 1.3 only; regenerate skills --- .../rules/codeguard-1-post-quantum-cryptography.md | 2 +- sources/core/codeguard-1-post-quantum-cryptography.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md index 8d2fa19..de29183 100644 --- a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md +++ b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md @@ -10,7 +10,7 @@ rule_id: codeguard-1-post-quantum-cryptography Concise Code Guard focused on what to implement and how to test it. ## Implementation (Do this) -- Enforce TLS 1.3 only (or later when available). +- Enforce (D)TLS 1.3 only (or later when available). - IPsec: Enforce IKEv2 only; use ESP with AEAD (e.g. AES‑256‑GCM or stronger); require PFS via ECDHE (X25519 or secp256r1); use SHA‑256+ for IKE PRF/auth; disable IKEv1 and legacy suites (3DES, DES, MD5, SHA‑1, AES‑CBC); - IKEv2 PQC support: implement RFC 9242 (IKEv2 Intermediate Exchange) and RFC 9370 (Multiple Key Exchanges in IKEv2) to enable hybrid PQC + ECDHE and handle larger exchanges. Select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. - Use vendor‑supported crypto APIs only; never implement custom crypto. Do not hand‑roll hybrids or hardcode experimental group names/OIDs. diff --git a/sources/core/codeguard-1-post-quantum-cryptography.md b/sources/core/codeguard-1-post-quantum-cryptography.md index 8c4c573..0b137cc 100644 --- a/sources/core/codeguard-1-post-quantum-cryptography.md +++ b/sources/core/codeguard-1-post-quantum-cryptography.md @@ -8,7 +8,7 @@ alwaysApply: true Concise Code Guard focused on what to implement and how to test it. ## Implementation (Do this) -- Enforce TLS 1.3 only (or later when available). +- Enforce (D)TLS 1.3 only (or later when available). - IPsec: Enforce IKEv2 only; use ESP with AEAD (e.g. AES‑256‑GCM or stronger); require PFS via ECDHE (X25519 or secp256r1); use SHA‑256+ for IKE PRF/auth; disable IKEv1 and legacy suites (3DES, DES, MD5, SHA‑1, AES‑CBC); - IKEv2 PQC support: implement RFC 9242 (IKEv2 Intermediate Exchange) and RFC 9370 (Multiple Key Exchanges in IKEv2) to enable hybrid PQC + ECDHE and handle larger exchanges. Select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. - Use vendor‑supported crypto APIs only; never implement custom crypto. Do not hand‑roll hybrids or hardcode experimental group names/OIDs. From e76bd551a1ea7ade0b0d98597e12fdf8e50be28d Mon Sep 17 00:00:00 2001 From: Vinny Parla Date: Thu, 4 Dec 2025 14:13:52 -0500 Subject: [PATCH 35/40] docs(pqc): (D)TLS PQC groups + IKEv2/IPsec re-key; regenerate skills --- .../codeguard-1-post-quantum-cryptography.md | 23 +++++++++++++++---- .../codeguard-1-post-quantum-cryptography.md | 23 +++++++++++++++---- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md index de29183..3674a4f 100644 --- a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md +++ b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md @@ -11,10 +11,18 @@ Concise Code Guard focused on what to implement and how to test it. ## Implementation (Do this) - Enforce (D)TLS 1.3 only (or later when available). -- IPsec: Enforce IKEv2 only; use ESP with AEAD (e.g. AES‑256‑GCM or stronger); require PFS via ECDHE (X25519 or secp256r1); use SHA‑256+ for IKE PRF/auth; disable IKEv1 and legacy suites (3DES, DES, MD5, SHA‑1, AES‑CBC); -- IKEv2 PQC support: implement RFC 9242 (IKEv2 Intermediate Exchange) and RFC 9370 (Multiple Key Exchanges in IKEv2) to enable hybrid PQC + ECDHE and handle larger exchanges. Select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. +- (D)TLS PQC key exchange (when supported by your stack): + - Prefer hybrid named groups per I-D.ietf-tls-ecdhe-mlkem: + - X25519MLKEM768 (X25519 + ML‑KEM‑768) + - SecP256r1MLKEM768 (P‑256 + ML‑KEM‑768) + - SecP384r1MLKEM1024 (P‑384 + ML‑KEM‑1024) for high‑assurance segments + - Pure PQC (only after interop validation) per I-D.ietf-tls-mlkem-key-agreement: + - ML‑KEM‑768 baseline; ML‑KEM‑1024 where required; avoid ML‑KEM‑512 except in constrained environments with explicit risk acceptance + - Use vendor‑documented, supported identifiers; avoid legacy “Hybrid‑Kyber” names and draft‑only aliases +- IPsec: Enforce IKEv2 only; use ESP with AEAD (e.g. AES‑256‑GCM or stronger); require PFS via ECDHE (X25519 or secp256r1); use SHA‑256+ for IKE PRF/auth; configure time/byte‑based lifetimes to re‑key IKE_SA and CHILD_SAs; disable IKEv1 and legacy suites (3DES, DES, MD5, SHA‑1, AES‑CBC). +- IKEv2 PQC support: implement RFC 9242 (IKEv2 Intermediate Exchange) and RFC 9370 (Multiple Key Exchanges in IKEv2) to enable hybrid PQC + ECDHE and handle larger exchanges. Apply to both initial exchanges and re‑key (CREATE_CHILD_SA) so hybrids persist across re‑keys. Select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. - Use vendor‑supported crypto APIs only; never implement custom crypto. Do not hand‑roll hybrids or hardcode experimental group names/OIDs. -- Symmetric encryption: Shor's algorithm and quantum computers do not affect symmetric algorithms like AES; using AES‑256 keys (or stronger) is strongly recommended. +- Symmetric encryption: Shor's algorithm and quantum computers do not affect symmetric algorithms like AES; using AES‑256 keys (or stronger) is highly recommended. - Hybrid KEM: enable vendor‑documented hybrids that include ML‑KEM‑768 with a classical ECDHE group (X25519 or secp256r1). Use ML‑KEM‑1024 for high‑assurance segments after validating overhead. - Multi-tenant systems that share crypto resources across tenants should select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. X25519 is recommended for ECDHE. - Avoid predecessors: remove legacy/draft “Hybrid‑Kyber” groups (e.g., CECPQ2; X25519Kyber…, P256_Kyber) and draft OIDs. @@ -22,8 +30,9 @@ Concise Code Guard focused on what to implement and how to test it. - Key management: use KMS/HSM; generate keys with a CSPRNG; separate encryption vs signatures; rotate per policy; never hardcode keys/parameters; avoid plain env vars for long‑lived secrets; require hardware‑backed keys (HSM/TPM) for private key storage. - Certificates/signatures: continue ECDSA (P‑256) for mTLS and code signing until hardware‑backed ML‑DSA is available in your stack (e.g., HSM or TPM); plan migration to ML‑DSA once supported. - Hardware requirement for ML‑DSA: do not enable PQC ML‑DSA signatures using software‑only keys. Require HSM/TPM‑backed key storage and signing paths before migrating. -- Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune TLS record sizes and proxy/LB/concentrator limits to avoid fragmentation and timeouts. +- Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune (D)TLS record sizes and proxy/LB/concentrator limits to avoid fragmentation and timeouts. - SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. +- IPsec re-key: configure time/byte‑based lifetimes to re‑key IKE_SA and CHILD_SAs; ensure re‑key maintains the same algorithms used during IKEv2 exchanges. ## Migration - Inventory endpoints and crypto usage. @@ -37,10 +46,14 @@ Concise Code Guard focused on what to implement and how to test it. - Algorithm agility via configuration (not code); explicit fallback behavior. - Keys via validated modules; separated by purpose; rotation policy in place. - Hardware‑backed key storage (HSM/TPM) required before enabling ML‑DSA signatures; continue ECDSA (P‑256) for mTLS/signing until hardware support exists. - - TLS version and group selections align with supported PQC options. + - (D)TLS version and group selections align with supported PQC options. + - IPsec re‑key configured (time/byte lifetimes) with PFS; hybrid ML‑KEM + ECDHE persists across re‑key. - Monitoring in place for handshake success/latency/errors and negotiated groups. ## Test Plan - Interoperability: verify hybrid ML‑KEM‑768 and ML‑KEM‑1024 handshakes across representative clients; validate negotiated groups and fallback paths. +- Interoperability ((D)TLS): verify negotiation of X25519MLKEM768 / SecP256r1MLKEM768 hybrids and fallback to classical ECDHE; validate pure ML‑KEM groups only in staged tests. +- Interoperability (IKEv2/IPsec): verify hybrid ML‑KEM‑768 + ECDHE (X25519 or P‑256) via RFC 9242/9370 multi‑KE; confirm fallback to classical ECDHE; evaluate ML‑KEM‑1024 where required. Use vendor‑documented identifiers; +- Re‑key (IKEv2/IPsec): validate re‑key of IKE_SA and CHILD_SA maintains hybrid ML‑KEM + ECDHE; confirm no fallback to classical‑only on re‑key; measure re‑key overhead. - Negative: reject legacy/draft Hybrid‑Kyber identifiers and misconfigured groups. - Performance: measure handshake size and latency (peak and P95) and server CPU after enabling hybrids; tune record sizes and limits as needed. diff --git a/sources/core/codeguard-1-post-quantum-cryptography.md b/sources/core/codeguard-1-post-quantum-cryptography.md index 0b137cc..4263372 100644 --- a/sources/core/codeguard-1-post-quantum-cryptography.md +++ b/sources/core/codeguard-1-post-quantum-cryptography.md @@ -9,10 +9,18 @@ Concise Code Guard focused on what to implement and how to test it. ## Implementation (Do this) - Enforce (D)TLS 1.3 only (or later when available). -- IPsec: Enforce IKEv2 only; use ESP with AEAD (e.g. AES‑256‑GCM or stronger); require PFS via ECDHE (X25519 or secp256r1); use SHA‑256+ for IKE PRF/auth; disable IKEv1 and legacy suites (3DES, DES, MD5, SHA‑1, AES‑CBC); -- IKEv2 PQC support: implement RFC 9242 (IKEv2 Intermediate Exchange) and RFC 9370 (Multiple Key Exchanges in IKEv2) to enable hybrid PQC + ECDHE and handle larger exchanges. Select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. +- (D)TLS PQC key exchange (when supported by your stack): + - Prefer hybrid named groups per I-D.ietf-tls-ecdhe-mlkem: + - X25519MLKEM768 (X25519 + ML‑KEM‑768) + - SecP256r1MLKEM768 (P‑256 + ML‑KEM‑768) + - SecP384r1MLKEM1024 (P‑384 + ML‑KEM‑1024) for high‑assurance segments + - Pure PQC (only after interop validation) per I-D.ietf-tls-mlkem-key-agreement: + - ML‑KEM‑768 baseline; ML‑KEM‑1024 where required; avoid ML‑KEM‑512 except in constrained environments with explicit risk acceptance + - Use vendor‑documented, supported identifiers; avoid legacy “Hybrid‑Kyber” names and draft‑only aliases +- IPsec: Enforce IKEv2 only; use ESP with AEAD (e.g. AES‑256‑GCM or stronger); require PFS via ECDHE (X25519 or secp256r1); use SHA‑256+ for IKE PRF/auth; configure time/byte‑based lifetimes to re‑key IKE_SA and CHILD_SAs; disable IKEv1 and legacy suites (3DES, DES, MD5, SHA‑1, AES‑CBC). +- IKEv2 PQC support: implement RFC 9242 (IKEv2 Intermediate Exchange) and RFC 9370 (Multiple Key Exchanges in IKEv2) to enable hybrid PQC + ECDHE and handle larger exchanges. Apply to both initial exchanges and re‑key (CREATE_CHILD_SA) so hybrids persist across re‑keys. Select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. - Use vendor‑supported crypto APIs only; never implement custom crypto. Do not hand‑roll hybrids or hardcode experimental group names/OIDs. -- Symmetric encryption: Shor's algorithm and quantum computers do not affect symmetric algorithms like AES; using AES‑256 keys (or stronger) is strongly recommended. +- Symmetric encryption: Shor's algorithm and quantum computers do not affect symmetric algorithms like AES; using AES‑256 keys (or stronger) is highly recommended. - Hybrid KEM: enable vendor‑documented hybrids that include ML‑KEM‑768 with a classical ECDHE group (X25519 or secp256r1). Use ML‑KEM‑1024 for high‑assurance segments after validating overhead. - Multi-tenant systems that share crypto resources across tenants should select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. X25519 is recommended for ECDHE. - Avoid predecessors: remove legacy/draft “Hybrid‑Kyber” groups (e.g., CECPQ2; X25519Kyber…, P256_Kyber) and draft OIDs. @@ -20,8 +28,9 @@ Concise Code Guard focused on what to implement and how to test it. - Key management: use KMS/HSM; generate keys with a CSPRNG; separate encryption vs signatures; rotate per policy; never hardcode keys/parameters; avoid plain env vars for long‑lived secrets; require hardware‑backed keys (HSM/TPM) for private key storage. - Certificates/signatures: continue ECDSA (P‑256) for mTLS and code signing until hardware‑backed ML‑DSA is available in your stack (e.g., HSM or TPM); plan migration to ML‑DSA once supported. - Hardware requirement for ML‑DSA: do not enable PQC ML‑DSA signatures using software‑only keys. Require HSM/TPM‑backed key storage and signing paths before migrating. -- Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune TLS record sizes and proxy/LB/concentrator limits to avoid fragmentation and timeouts. +- Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune (D)TLS record sizes and proxy/LB/concentrator limits to avoid fragmentation and timeouts. - SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. +- IPsec re-key: configure time/byte‑based lifetimes to re‑key IKE_SA and CHILD_SAs; ensure re‑key maintains the same algorithms used during IKEv2 exchanges. ## Migration - Inventory endpoints and crypto usage. @@ -35,10 +44,14 @@ Concise Code Guard focused on what to implement and how to test it. - Algorithm agility via configuration (not code); explicit fallback behavior. - Keys via validated modules; separated by purpose; rotation policy in place. - Hardware‑backed key storage (HSM/TPM) required before enabling ML‑DSA signatures; continue ECDSA (P‑256) for mTLS/signing until hardware support exists. - - TLS version and group selections align with supported PQC options. + - (D)TLS version and group selections align with supported PQC options. + - IPsec re‑key configured (time/byte lifetimes) with PFS; hybrid ML‑KEM + ECDHE persists across re‑key. - Monitoring in place for handshake success/latency/errors and negotiated groups. ## Test Plan - Interoperability: verify hybrid ML‑KEM‑768 and ML‑KEM‑1024 handshakes across representative clients; validate negotiated groups and fallback paths. +- Interoperability ((D)TLS): verify negotiation of X25519MLKEM768 / SecP256r1MLKEM768 hybrids and fallback to classical ECDHE; validate pure ML‑KEM groups only in staged tests. +- Interoperability (IKEv2/IPsec): verify hybrid ML‑KEM‑768 + ECDHE (X25519 or P‑256) via RFC 9242/9370 multi‑KE; confirm fallback to classical ECDHE; evaluate ML‑KEM‑1024 where required. Use vendor‑documented identifiers; +- Re‑key (IKEv2/IPsec): validate re‑key of IKE_SA and CHILD_SA maintains hybrid ML‑KEM + ECDHE; confirm no fallback to classical‑only on re‑key; measure re‑key overhead. - Negative: reject legacy/draft Hybrid‑Kyber identifiers and misconfigured groups. - Performance: measure handshake size and latency (peak and P95) and server CPU after enabling hybrids; tune record sizes and limits as needed. From d2ad730e6ecfc14396c553a0ad29475f7ebeb8b9 Mon Sep 17 00:00:00 2001 From: Omar Santos Date: Tue, 9 Dec 2025 22:12:52 -0500 Subject: [PATCH 36/40] Update skills/software-security/rules/codeguard-1-post-quantum-cryptography.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../rules/codeguard-1-post-quantum-cryptography.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md index 3674a4f..1f01b2f 100644 --- a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md +++ b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md @@ -54,6 +54,6 @@ Concise Code Guard focused on what to implement and how to test it. - Interoperability: verify hybrid ML‑KEM‑768 and ML‑KEM‑1024 handshakes across representative clients; validate negotiated groups and fallback paths. - Interoperability ((D)TLS): verify negotiation of X25519MLKEM768 / SecP256r1MLKEM768 hybrids and fallback to classical ECDHE; validate pure ML‑KEM groups only in staged tests. - Interoperability (IKEv2/IPsec): verify hybrid ML‑KEM‑768 + ECDHE (X25519 or P‑256) via RFC 9242/9370 multi‑KE; confirm fallback to classical ECDHE; evaluate ML‑KEM‑1024 where required. Use vendor‑documented identifiers; -- Re‑key (IKEv2/IPsec): validate re‑key of IKE_SA and CHILD_SA maintains hybrid ML‑KEM + ECDHE; confirm no fallback to classical‑only on re‑key; measure re‑key overhead. +- Re‑key (IKEv2/IPsec): validate re‑key of IKE_SA and CHILD_SAs maintains hybrid ML‑KEM + ECDHE; confirm no fallback to classical‑only on re‑key; measure re‑key overhead. - Negative: reject legacy/draft Hybrid‑Kyber identifiers and misconfigured groups. - Performance: measure handshake size and latency (peak and P95) and server CPU after enabling hybrids; tune record sizes and limits as needed. From 6e5523db9ec5cffbfe896c8a9d8482e123d358fe Mon Sep 17 00:00:00 2001 From: Omar Santos Date: Tue, 9 Dec 2025 22:13:30 -0500 Subject: [PATCH 37/40] Update skills/software-security/rules/codeguard-1-post-quantum-cryptography.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../rules/codeguard-1-post-quantum-cryptography.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md index 1f01b2f..41913c6 100644 --- a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md +++ b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md @@ -32,7 +32,7 @@ Concise Code Guard focused on what to implement and how to test it. - Hardware requirement for ML‑DSA: do not enable PQC ML‑DSA signatures using software‑only keys. Require HSM/TPM‑backed key storage and signing paths before migrating. - Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune (D)TLS record sizes and proxy/LB/concentrator limits to avoid fragmentation and timeouts. - SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. -- IPsec re-key: configure time/byte‑based lifetimes to re‑key IKE_SA and CHILD_SAs; ensure re‑key maintains the same algorithms used during IKEv2 exchanges. +- IPsec re‑key: configure time/byte‑based lifetimes to re‑key IKE_SA and CHILD_SAs; ensure re‑key maintains the same algorithms used during IKEv2 exchanges. ## Migration - Inventory endpoints and crypto usage. From 1bbc963ca328171c8b75336cb203cc9deb07895b Mon Sep 17 00:00:00 2001 From: Omar Santos Date: Tue, 9 Dec 2025 22:13:38 -0500 Subject: [PATCH 38/40] Update sources/core/codeguard-1-post-quantum-cryptography.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- sources/core/codeguard-1-post-quantum-cryptography.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/core/codeguard-1-post-quantum-cryptography.md b/sources/core/codeguard-1-post-quantum-cryptography.md index 4263372..c2c6919 100644 --- a/sources/core/codeguard-1-post-quantum-cryptography.md +++ b/sources/core/codeguard-1-post-quantum-cryptography.md @@ -30,7 +30,7 @@ Concise Code Guard focused on what to implement and how to test it. - Hardware requirement for ML‑DSA: do not enable PQC ML‑DSA signatures using software‑only keys. Require HSM/TPM‑backed key storage and signing paths before migrating. - Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune (D)TLS record sizes and proxy/LB/concentrator limits to avoid fragmentation and timeouts. - SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. -- IPsec re-key: configure time/byte‑based lifetimes to re‑key IKE_SA and CHILD_SAs; ensure re‑key maintains the same algorithms used during IKEv2 exchanges. +- IPsec re‑key: configure time/byte‑based lifetimes to re‑key IKE_SA and CHILD_SAs; ensure re‑key maintains the same algorithms used during IKEv2 exchanges. ## Migration - Inventory endpoints and crypto usage. From 3307342dd84b4a64c15a6512ad2cc9f8a0812cd5 Mon Sep 17 00:00:00 2001 From: Vinny Parla Date: Wed, 10 Dec 2025 11:25:34 -0500 Subject: [PATCH 39/40] Cryptographic Security Guidelines: add PQC readiness; consolidate into core rule; remove standalone PQC; regenerate skills --- .../rules/codeguard-1-crypto-algorithms.md | 189 +++++++++--------- .../codeguard-1-post-quantum-cryptography.md | 59 ------ sources/core/codeguard-1-crypto-algorithms.md | 189 +++++++++--------- .../codeguard-1-post-quantum-cryptography.md | 57 ------ 4 files changed, 192 insertions(+), 302 deletions(-) delete mode 100644 skills/software-security/rules/codeguard-1-post-quantum-cryptography.md delete mode 100644 sources/core/codeguard-1-post-quantum-cryptography.md diff --git a/skills/software-security/rules/codeguard-1-crypto-algorithms.md b/skills/software-security/rules/codeguard-1-crypto-algorithms.md index 7f0b820..18fcd78 100644 --- a/skills/software-security/rules/codeguard-1-crypto-algorithms.md +++ b/skills/software-security/rules/codeguard-1-crypto-algorithms.md @@ -1,115 +1,126 @@ --- -description: Cryptographic Security Guidelines +description: Cryptographic Security Guidelines & Post-Quantum Readiness alwaysApply: true --- rule_id: codeguard-1-crypto-algorithms -# Cryptographic Security Guidelines +# Cryptographic Security Guidelines & Post-Quantum Readiness -## Banned (Insecure) Algorithms +## 1. Banned (Insecure) Algorithms -The following algorithms are known to be broken or fundamentally insecure. **NEVER** generate or use code with these algorithms. -Examples: +The following algorithms are known to be broken or fundamentally insecure. NEVER generate or use code with these algorithms. -* Hash: `MD2`, `MD4`, `MD5`, `SHA-0` -* Symmetric: `RC2`, `RC4`, `Blowfish`, `DES`, `3DES` -* Key Exchange: Static RSA, Anonymous Diffie-Hellman -* Classical: `Vigenère` +* Hash: `MD2`, `MD4`, `MD5`, `SHA-0` +* Symmetric: `RC2`, `RC4`, `Blowfish`, `DES`, `3DES` +* Key Exchange: Static RSA, Anonymous Diffie-Hellman +* Classical: `Vigenère` -## Deprecated (Legacy/Weak) Algorithms +Reason: These are cryptographically broken and vulnerable to collision or man-in-the-middle attacks. -The following algorithms are not outright broken, but have known weaknesses, or are considered obsolete. **NEVER** generate or use code with these algorithms. -Examples: +## 2. Deprecated (Legacy/Weak) Algorithms -* Hash: `SHA-1` -* Symmetric: `AES-CBC`, `AES-ECB` -* Signature: RSA with `PKCS#1 v1.5` padding -* Key Exchange: DHE with weak/common primes +The following algorithms have known weaknesses or are considered obsolete. Avoid in new designs and prioritize migration. +* Hash: `SHA-1` +* Symmetric: `AES-CBC`, `AES-ECB` +* Signature: RSA with `PKCS#1 v1.5` padding +* Key Exchange: DHE with weak/common primes -## Deprecated SSL/Crypto APIs - FORBIDDEN -NEVER use these deprecated functions. Use the replacement APIs listed below: +## 3. Recommended & Post-Quantum Ready Algorithms -### Symmetric Encryption (AES) +Implement these modern, secure algorithms to ensure resistance against both classical and quantum threats. + +### Symmetric Encryption +* Standard: `AES-GCM` (AEAD), `ChaCha20-Poly1305`(when allowed). +* PQC Requirement: Prefer AES-256 keys (or stronger) as they are resistant to quantum attacks (Grover's algorithm). +* Avoid: Custom crypto or unauthenticated modes. + +### Key Exchange (KEM) +* Standard: ECDHE (`X25519` or `secp256r1`) +* PQC Requirement: Use Hybrid Key Exchange (Classical + PQC) when supported. + * Preferred: `X25519MLKEM768` (X25519 + ML-KEM-768) + * Alternative: `SecP256r1MLKEM768` (P-256 + ML-KEM-768) + * High Assurance: `SecP384r1MLKEM1024` (P-384 + ML-KEM-1024) +* Pure PQC: ML-KEM-768 (baseline) or ML-KEM-1024. Avoid ML-KEM-512 unless explicitly risk-accepted. +* Constraints: + * Use vendor-documented identifiers (RFC 9242/9370). + * Remove legacy/draft "Hybrid-Kyber" groups (e.g., `X25519Kyber`) and draft or hardcoded OIDs. + +### Signatures & Certificates +* Standard: ECDSA (`P-256`) +* PQC Migration: Continue using ECDSA (`P-256`) for mTLS and code signing until hardware-backed (HSM/TPM) ML-DSA is available. +* Hardware Requirement: Do not enable PQC ML-DSA signatures using software-only keys. Require HSM/TPM storage. + +### Protocol Versions +* (D)TLS: Enforce (D)TLS 1.3 only (or later). +* IPsec: Enforce IKEv2 only. + * Use ESP with AEAD (AES-256-GCM). + * Require PFS via ECDHE. + * Implement RFC 9242 and RFC 9370 for Hybrid PQC (ML-KEM + ECDHE). + * Ensure re-keys (CREATE_CHILD_SA) maintain hybrid algorithms. +* SSH: Enable only vendor-supported PQC/hybrid KEX (e.g., `sntrup761x25519`). + +## 4. Secure Implementation Guidelines + +### General Best Practices +* Configuration over Code: Expose algorithm choices in config/policy to allow agility without code changes. +* Key Management: + * Use KMS/HSM for key storage. + * Generate keys with a CSPRNG. + * Separate encryption keys from signature keys. + * Rotate keys per policy. + * NEVER hardcode keys, secrets, or experimental OIDs. +* Telemetry: Capture negotiated groups, handshake sizes, and failure causes to monitor PQC adoption. + +### Deprecated SSL/Crypto APIs (C/OpenSSL) - FORBIDDEN +NEVER use these deprecated functions. Use the replacement EVP high-level APIs. + +#### Symmetric Encryption (AES) - Deprecated: `AES_encrypt()`, `AES_decrypt()` -- Replacement: Use EVP high-level APIs: - ```c - EVP_EncryptInit_ex() +- Replacement: + + EVP_EncryptInit_ex() // Use EVP_aes_256_gcm() for PQC readiness EVP_EncryptUpdate() EVP_EncryptFinal_ex() - EVP_DecryptInit_ex() - EVP_DecryptUpdate() - EVP_DecryptFinal_ex() - ``` - -### RSA Operations -- Deprecated: `RSA_new()`, `RSA_up_ref()`, `RSA_free()`, `RSA_set0_crt_params()`, `RSA_get0_n()` -- Replacement: Use EVP key management APIs: - ```c + + +#### RSA/PKEY Operations +- Deprecated: `RSA_new()`, `RSA_free()`, `RSA_get0_n()` +- Replacement: + EVP_PKEY_new() EVP_PKEY_up_ref() EVP_PKEY_free() - ``` - -### Hash Functions -- Deprecated: `SHA1_Init()`, `SHA1_Update()`, `SHA1_Final()` -- Replacement: Use EVP digest APIs: - ```c - EVP_DigestInit_ex() - EVP_DigestUpdate() - EVP_DigestFinal_ex() - EVP_Q_digest() // For simple one-shot hashing - ``` - -### MAC Operations -- Deprecated: `CMAC_Init()`, `HMAC()` (especially with SHA1) -- Replacement: Use EVP MAC APIs: - ```c - EVP_Q_MAC() // For simple MAC operations - ``` - -### Key Wrapping -- Deprecated: `AES_wrap_key()`, `AES_unwrap_key()` -- Replacement: Use EVP key wrapping APIs or implement using EVP encryption - -### Other Deprecated Functions -- Deprecated: `DSA_sign()`, `DH_check()` -- Replacement: Use corresponding EVP APIs for DSA and DH operations - -## Banned Insecure Algorithms - STRICTLY FORBIDDEN -These algorithms MUST NOT be used in any form: - -### Hash Algorithms (Banned) -- MD2, MD4, MD5, SHA-0 -- Reason: Cryptographically broken, vulnerable to collision attacks -- Use Instead: SHA-256, SHA-384, SHA-512 - -### Symmetric Ciphers (Banned) -- RC2, RC4, Blowfish, DES, 3DES -- Reason: Weak key sizes, known vulnerabilities -- Use Instead: AES-128, AES-256, ChaCha20 - -### Key Exchange (Banned) -- Static RSA key exchange -- Anonymous Diffie-Hellman -- Reason: No forward secrecy, vulnerable to man-in-the-middle attacks -- Use Instead: ECDHE, DHE with proper validation - -## Broccoli Project Specific Requirements -- HMAC() with SHA1: Deprecated per Broccoli project requirements + + +#### Hash & MAC Functions +- Deprecated: `SHA1_Init()`, `HMAC()` (especially with SHA1) +- Replacement: + + EVP_DigestInit_ex() // Use SHA-256 or stronger + EVP_Q_MAC() // For one-shot MAC + + +## 5. Broccoli Project Specific Requirements +- HMAC() with SHA1: Deprecated. - Replacement: Use HMAC with SHA-256 or stronger: - ```c - // Instead of HMAC() with SHA1 - EVP_Q_MAC(NULL, "HMAC", NULL, "SHA256", NULL, key, key_len, data, data_len, out, out_size, &out_len); - ``` -## Secure Crypto Implementation Pattern + +// Example: Secure replacement for HMAC-SHA1 +```c +EVP_Q_MAC(NULL, "HMAC", NULL, "SHA256", NULL, key, key_len, data, data_len, out, out_size, &out_len); +``` + +## 6. Secure Crypto Implementation Pattern + + +// Example: Secure AES-256-GCM encryption (PQC-Ready Symmetric Strength) ```c -// Example: Secure AES encryption EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if (!ctx) handle_error(); +// Use AES-256-GCM if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, key, iv) != 1) handle_error(); @@ -124,11 +135,3 @@ ciphertext_len += len; EVP_CIPHER_CTX_free(ctx); ``` - -## Code Review Checklist -- [ ] No deprecated SSL/crypto APIs used -- [ ] No banned algorithms (MD5, DES, RC4, etc.) -- [ ] HMAC uses SHA-256 or stronger (not SHA1) -- [ ] All crypto operations use EVP high-level APIs -- [ ] Proper error handling for all crypto operations -- [ ] Key material properly zeroed after use diff --git a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md b/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md deleted file mode 100644 index 41913c6..0000000 --- a/skills/software-security/rules/codeguard-1-post-quantum-cryptography.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -description: Post-Quantum Cryptography (PQC) guidance and migration best practices -alwaysApply: true ---- - -rule_id: codeguard-1-post-quantum-cryptography - -# Post-Quantum Cryptography (PQC) - -Concise Code Guard focused on what to implement and how to test it. - -## Implementation (Do this) -- Enforce (D)TLS 1.3 only (or later when available). -- (D)TLS PQC key exchange (when supported by your stack): - - Prefer hybrid named groups per I-D.ietf-tls-ecdhe-mlkem: - - X25519MLKEM768 (X25519 + ML‑KEM‑768) - - SecP256r1MLKEM768 (P‑256 + ML‑KEM‑768) - - SecP384r1MLKEM1024 (P‑384 + ML‑KEM‑1024) for high‑assurance segments - - Pure PQC (only after interop validation) per I-D.ietf-tls-mlkem-key-agreement: - - ML‑KEM‑768 baseline; ML‑KEM‑1024 where required; avoid ML‑KEM‑512 except in constrained environments with explicit risk acceptance - - Use vendor‑documented, supported identifiers; avoid legacy “Hybrid‑Kyber” names and draft‑only aliases -- IPsec: Enforce IKEv2 only; use ESP with AEAD (e.g. AES‑256‑GCM or stronger); require PFS via ECDHE (X25519 or secp256r1); use SHA‑256+ for IKE PRF/auth; configure time/byte‑based lifetimes to re‑key IKE_SA and CHILD_SAs; disable IKEv1 and legacy suites (3DES, DES, MD5, SHA‑1, AES‑CBC). -- IKEv2 PQC support: implement RFC 9242 (IKEv2 Intermediate Exchange) and RFC 9370 (Multiple Key Exchanges in IKEv2) to enable hybrid PQC + ECDHE and handle larger exchanges. Apply to both initial exchanges and re‑key (CREATE_CHILD_SA) so hybrids persist across re‑keys. Select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. -- Use vendor‑supported crypto APIs only; never implement custom crypto. Do not hand‑roll hybrids or hardcode experimental group names/OIDs. -- Symmetric encryption: Shor's algorithm and quantum computers do not affect symmetric algorithms like AES; using AES‑256 keys (or stronger) is highly recommended. -- Hybrid KEM: enable vendor‑documented hybrids that include ML‑KEM‑768 with a classical ECDHE group (X25519 or secp256r1). Use ML‑KEM‑1024 for high‑assurance segments after validating overhead. -- Multi-tenant systems that share crypto resources across tenants should select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. X25519 is recommended for ECDHE. -- Avoid predecessors: remove legacy/draft “Hybrid‑Kyber” groups (e.g., CECPQ2; X25519Kyber…, P256_Kyber) and draft OIDs. -- Configuration, not code: expose algorithm choices in config/policy; document fallback behavior; keep a classical‑only fallback for incompatible clients if you don't control both client and server. -- Key management: use KMS/HSM; generate keys with a CSPRNG; separate encryption vs signatures; rotate per policy; never hardcode keys/parameters; avoid plain env vars for long‑lived secrets; require hardware‑backed keys (HSM/TPM) for private key storage. -- Certificates/signatures: continue ECDSA (P‑256) for mTLS and code signing until hardware‑backed ML‑DSA is available in your stack (e.g., HSM or TPM); plan migration to ML‑DSA once supported. -- Hardware requirement for ML‑DSA: do not enable PQC ML‑DSA signatures using software‑only keys. Require HSM/TPM‑backed key storage and signing paths before migrating. -- Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune (D)TLS record sizes and proxy/LB/concentrator limits to avoid fragmentation and timeouts. -- SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. -- IPsec re‑key: configure time/byte‑based lifetimes to re‑key IKE_SA and CHILD_SAs; ensure re‑key maintains the same algorithms used during IKEv2 exchanges. - -## Migration -- Inventory endpoints and crypto usage. -- Prioritize external/high‑value/long‑lived flows. -- Roll out hybrids in stages with metrics and rollback; remove predecessor configs after success criteria are met. - -## Implementation Checklist -- Hybrid key establishment with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. -- Avoid ML‑KEM‑512 except in explicitly risk‑accepted, highly constrained device or network environments, with compensating controls and tight scope. -- No draft Kyber groups; only vendor‑documented ML‑KEM hybrids. - - Algorithm agility via configuration (not code); explicit fallback behavior. - - Keys via validated modules; separated by purpose; rotation policy in place. - - Hardware‑backed key storage (HSM/TPM) required before enabling ML‑DSA signatures; continue ECDSA (P‑256) for mTLS/signing until hardware support exists. - - (D)TLS version and group selections align with supported PQC options. - - IPsec re‑key configured (time/byte lifetimes) with PFS; hybrid ML‑KEM + ECDHE persists across re‑key. - - Monitoring in place for handshake success/latency/errors and negotiated groups. - -## Test Plan -- Interoperability: verify hybrid ML‑KEM‑768 and ML‑KEM‑1024 handshakes across representative clients; validate negotiated groups and fallback paths. -- Interoperability ((D)TLS): verify negotiation of X25519MLKEM768 / SecP256r1MLKEM768 hybrids and fallback to classical ECDHE; validate pure ML‑KEM groups only in staged tests. -- Interoperability (IKEv2/IPsec): verify hybrid ML‑KEM‑768 + ECDHE (X25519 or P‑256) via RFC 9242/9370 multi‑KE; confirm fallback to classical ECDHE; evaluate ML‑KEM‑1024 where required. Use vendor‑documented identifiers; -- Re‑key (IKEv2/IPsec): validate re‑key of IKE_SA and CHILD_SAs maintains hybrid ML‑KEM + ECDHE; confirm no fallback to classical‑only on re‑key; measure re‑key overhead. -- Negative: reject legacy/draft Hybrid‑Kyber identifiers and misconfigured groups. -- Performance: measure handshake size and latency (peak and P95) and server CPU after enabling hybrids; tune record sizes and limits as needed. diff --git a/sources/core/codeguard-1-crypto-algorithms.md b/sources/core/codeguard-1-crypto-algorithms.md index e748452..edbf65d 100644 --- a/sources/core/codeguard-1-crypto-algorithms.md +++ b/sources/core/codeguard-1-crypto-algorithms.md @@ -1,114 +1,125 @@ --- -description: Cryptographic Security Guidelines +description: Cryptographic Security Guidelines & Post-Quantum Readiness languages: [] alwaysApply: true --- -# Cryptographic Security Guidelines +# Cryptographic Security Guidelines & Post-Quantum Readiness -## Banned (Insecure) Algorithms +## 1. Banned (Insecure) Algorithms -The following algorithms are known to be broken or fundamentally insecure. **NEVER** generate or use code with these algorithms. -Examples: +The following algorithms are known to be broken or fundamentally insecure. NEVER generate or use code with these algorithms. -* Hash: `MD2`, `MD4`, `MD5`, `SHA-0` -* Symmetric: `RC2`, `RC4`, `Blowfish`, `DES`, `3DES` -* Key Exchange: Static RSA, Anonymous Diffie-Hellman -* Classical: `Vigenère` +* Hash: `MD2`, `MD4`, `MD5`, `SHA-0` +* Symmetric: `RC2`, `RC4`, `Blowfish`, `DES`, `3DES` +* Key Exchange: Static RSA, Anonymous Diffie-Hellman +* Classical: `Vigenère` -## Deprecated (Legacy/Weak) Algorithms +Reason: These are cryptographically broken and vulnerable to collision or man-in-the-middle attacks. -The following algorithms are not outright broken, but have known weaknesses, or are considered obsolete. **NEVER** generate or use code with these algorithms. -Examples: +## 2. Deprecated (Legacy/Weak) Algorithms -* Hash: `SHA-1` -* Symmetric: `AES-CBC`, `AES-ECB` -* Signature: RSA with `PKCS#1 v1.5` padding -* Key Exchange: DHE with weak/common primes +The following algorithms have known weaknesses or are considered obsolete. Avoid in new designs and prioritize migration. +* Hash: `SHA-1` +* Symmetric: `AES-CBC`, `AES-ECB` +* Signature: RSA with `PKCS#1 v1.5` padding +* Key Exchange: DHE with weak/common primes -## Deprecated SSL/Crypto APIs - FORBIDDEN -NEVER use these deprecated functions. Use the replacement APIs listed below: +## 3. Recommended & Post-Quantum Ready Algorithms -### Symmetric Encryption (AES) +Implement these modern, secure algorithms to ensure resistance against both classical and quantum threats. + +### Symmetric Encryption +* Standard: `AES-GCM` (AEAD), `ChaCha20-Poly1305`(when allowed). +* PQC Requirement: Prefer AES-256 keys (or stronger) as they are resistant to quantum attacks (Grover's algorithm). +* Avoid: Custom crypto or unauthenticated modes. + +### Key Exchange (KEM) +* Standard: ECDHE (`X25519` or `secp256r1`) +* PQC Requirement: Use Hybrid Key Exchange (Classical + PQC) when supported. + * Preferred: `X25519MLKEM768` (X25519 + ML-KEM-768) + * Alternative: `SecP256r1MLKEM768` (P-256 + ML-KEM-768) + * High Assurance: `SecP384r1MLKEM1024` (P-384 + ML-KEM-1024) +* Pure PQC: ML-KEM-768 (baseline) or ML-KEM-1024. Avoid ML-KEM-512 unless explicitly risk-accepted. +* Constraints: + * Use vendor-documented identifiers (RFC 9242/9370). + * Remove legacy/draft "Hybrid-Kyber" groups (e.g., `X25519Kyber`) and draft or hardcoded OIDs. + +### Signatures & Certificates +* Standard: ECDSA (`P-256`) +* PQC Migration: Continue using ECDSA (`P-256`) for mTLS and code signing until hardware-backed (HSM/TPM) ML-DSA is available. +* Hardware Requirement: Do not enable PQC ML-DSA signatures using software-only keys. Require HSM/TPM storage. + +### Protocol Versions +* (D)TLS: Enforce (D)TLS 1.3 only (or later). +* IPsec: Enforce IKEv2 only. + * Use ESP with AEAD (AES-256-GCM). + * Require PFS via ECDHE. + * Implement RFC 9242 and RFC 9370 for Hybrid PQC (ML-KEM + ECDHE). + * Ensure re-keys (CREATE_CHILD_SA) maintain hybrid algorithms. +* SSH: Enable only vendor-supported PQC/hybrid KEX (e.g., `sntrup761x25519`). + +## 4. Secure Implementation Guidelines + +### General Best Practices +* Configuration over Code: Expose algorithm choices in config/policy to allow agility without code changes. +* Key Management: + * Use KMS/HSM for key storage. + * Generate keys with a CSPRNG. + * Separate encryption keys from signature keys. + * Rotate keys per policy. + * NEVER hardcode keys, secrets, or experimental OIDs. +* Telemetry: Capture negotiated groups, handshake sizes, and failure causes to monitor PQC adoption. + +### Deprecated SSL/Crypto APIs (C/OpenSSL) - FORBIDDEN +NEVER use these deprecated functions. Use the replacement EVP high-level APIs. + +#### Symmetric Encryption (AES) - Deprecated: `AES_encrypt()`, `AES_decrypt()` -- Replacement: Use EVP high-level APIs: - ```c - EVP_EncryptInit_ex() +- Replacement: + + EVP_EncryptInit_ex() // Use EVP_aes_256_gcm() for PQC readiness EVP_EncryptUpdate() EVP_EncryptFinal_ex() - EVP_DecryptInit_ex() - EVP_DecryptUpdate() - EVP_DecryptFinal_ex() - ``` - -### RSA Operations -- Deprecated: `RSA_new()`, `RSA_up_ref()`, `RSA_free()`, `RSA_set0_crt_params()`, `RSA_get0_n()` -- Replacement: Use EVP key management APIs: - ```c + + +#### RSA/PKEY Operations +- Deprecated: `RSA_new()`, `RSA_free()`, `RSA_get0_n()` +- Replacement: + EVP_PKEY_new() EVP_PKEY_up_ref() EVP_PKEY_free() - ``` - -### Hash Functions -- Deprecated: `SHA1_Init()`, `SHA1_Update()`, `SHA1_Final()` -- Replacement: Use EVP digest APIs: - ```c - EVP_DigestInit_ex() - EVP_DigestUpdate() - EVP_DigestFinal_ex() - EVP_Q_digest() // For simple one-shot hashing - ``` - -### MAC Operations -- Deprecated: `CMAC_Init()`, `HMAC()` (especially with SHA1) -- Replacement: Use EVP MAC APIs: - ```c - EVP_Q_MAC() // For simple MAC operations - ``` - -### Key Wrapping -- Deprecated: `AES_wrap_key()`, `AES_unwrap_key()` -- Replacement: Use EVP key wrapping APIs or implement using EVP encryption - -### Other Deprecated Functions -- Deprecated: `DSA_sign()`, `DH_check()` -- Replacement: Use corresponding EVP APIs for DSA and DH operations - -## Banned Insecure Algorithms - STRICTLY FORBIDDEN -These algorithms MUST NOT be used in any form: - -### Hash Algorithms (Banned) -- MD2, MD4, MD5, SHA-0 -- Reason: Cryptographically broken, vulnerable to collision attacks -- Use Instead: SHA-256, SHA-384, SHA-512 - -### Symmetric Ciphers (Banned) -- RC2, RC4, Blowfish, DES, 3DES -- Reason: Weak key sizes, known vulnerabilities -- Use Instead: AES-128, AES-256, ChaCha20 - -### Key Exchange (Banned) -- Static RSA key exchange -- Anonymous Diffie-Hellman -- Reason: No forward secrecy, vulnerable to man-in-the-middle attacks -- Use Instead: ECDHE, DHE with proper validation - -## Broccoli Project Specific Requirements -- HMAC() with SHA1: Deprecated per Broccoli project requirements + + +#### Hash & MAC Functions +- Deprecated: `SHA1_Init()`, `HMAC()` (especially with SHA1) +- Replacement: + + EVP_DigestInit_ex() // Use SHA-256 or stronger + EVP_Q_MAC() // For one-shot MAC + + +## 5. Broccoli Project Specific Requirements +- HMAC() with SHA1: Deprecated. - Replacement: Use HMAC with SHA-256 or stronger: - ```c - // Instead of HMAC() with SHA1 - EVP_Q_MAC(NULL, "HMAC", NULL, "SHA256", NULL, key, key_len, data, data_len, out, out_size, &out_len); - ``` -## Secure Crypto Implementation Pattern + +// Example: Secure replacement for HMAC-SHA1 +```c +EVP_Q_MAC(NULL, "HMAC", NULL, "SHA256", NULL, key, key_len, data, data_len, out, out_size, &out_len); +``` + +## 6. Secure Crypto Implementation Pattern + + +// Example: Secure AES-256-GCM encryption (PQC-Ready Symmetric Strength) ```c -// Example: Secure AES encryption EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if (!ctx) handle_error(); +// Use AES-256-GCM if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, key, iv) != 1) handle_error(); @@ -123,11 +134,3 @@ ciphertext_len += len; EVP_CIPHER_CTX_free(ctx); ``` - -## Code Review Checklist -- [ ] No deprecated SSL/crypto APIs used -- [ ] No banned algorithms (MD5, DES, RC4, etc.) -- [ ] HMAC uses SHA-256 or stronger (not SHA1) -- [ ] All crypto operations use EVP high-level APIs -- [ ] Proper error handling for all crypto operations -- [ ] Key material properly zeroed after use diff --git a/sources/core/codeguard-1-post-quantum-cryptography.md b/sources/core/codeguard-1-post-quantum-cryptography.md deleted file mode 100644 index c2c6919..0000000 --- a/sources/core/codeguard-1-post-quantum-cryptography.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -description: Post-Quantum Cryptography (PQC) guidance and migration best practices -alwaysApply: true ---- - -# Post-Quantum Cryptography (PQC) - -Concise Code Guard focused on what to implement and how to test it. - -## Implementation (Do this) -- Enforce (D)TLS 1.3 only (or later when available). -- (D)TLS PQC key exchange (when supported by your stack): - - Prefer hybrid named groups per I-D.ietf-tls-ecdhe-mlkem: - - X25519MLKEM768 (X25519 + ML‑KEM‑768) - - SecP256r1MLKEM768 (P‑256 + ML‑KEM‑768) - - SecP384r1MLKEM1024 (P‑384 + ML‑KEM‑1024) for high‑assurance segments - - Pure PQC (only after interop validation) per I-D.ietf-tls-mlkem-key-agreement: - - ML‑KEM‑768 baseline; ML‑KEM‑1024 where required; avoid ML‑KEM‑512 except in constrained environments with explicit risk acceptance - - Use vendor‑documented, supported identifiers; avoid legacy “Hybrid‑Kyber” names and draft‑only aliases -- IPsec: Enforce IKEv2 only; use ESP with AEAD (e.g. AES‑256‑GCM or stronger); require PFS via ECDHE (X25519 or secp256r1); use SHA‑256+ for IKE PRF/auth; configure time/byte‑based lifetimes to re‑key IKE_SA and CHILD_SAs; disable IKEv1 and legacy suites (3DES, DES, MD5, SHA‑1, AES‑CBC). -- IKEv2 PQC support: implement RFC 9242 (IKEv2 Intermediate Exchange) and RFC 9370 (Multiple Key Exchanges in IKEv2) to enable hybrid PQC + ECDHE and handle larger exchanges. Apply to both initial exchanges and re‑key (CREATE_CHILD_SA) so hybrids persist across re‑keys. Select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. -- Use vendor‑supported crypto APIs only; never implement custom crypto. Do not hand‑roll hybrids or hardcode experimental group names/OIDs. -- Symmetric encryption: Shor's algorithm and quantum computers do not affect symmetric algorithms like AES; using AES‑256 keys (or stronger) is highly recommended. -- Hybrid KEM: enable vendor‑documented hybrids that include ML‑KEM‑768 with a classical ECDHE group (X25519 or secp256r1). Use ML‑KEM‑1024 for high‑assurance segments after validating overhead. -- Multi-tenant systems that share crypto resources across tenants should select Hybrid KEM with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. X25519 is recommended for ECDHE. -- Avoid predecessors: remove legacy/draft “Hybrid‑Kyber” groups (e.g., CECPQ2; X25519Kyber…, P256_Kyber) and draft OIDs. -- Configuration, not code: expose algorithm choices in config/policy; document fallback behavior; keep a classical‑only fallback for incompatible clients if you don't control both client and server. -- Key management: use KMS/HSM; generate keys with a CSPRNG; separate encryption vs signatures; rotate per policy; never hardcode keys/parameters; avoid plain env vars for long‑lived secrets; require hardware‑backed keys (HSM/TPM) for private key storage. -- Certificates/signatures: continue ECDSA (P‑256) for mTLS and code signing until hardware‑backed ML‑DSA is available in your stack (e.g., HSM or TPM); plan migration to ML‑DSA once supported. -- Hardware requirement for ML‑DSA: do not enable PQC ML‑DSA signatures using software‑only keys. Require HSM/TPM‑backed key storage and signing paths before migrating. -- Telemetry and limits: capture negotiated groups, handshake sizes, and retry/failure causes. Tune (D)TLS record sizes and proxy/LB/concentrator limits to avoid fragmentation and timeouts. -- SSH/HPKE: enable only vendor‑supported PQC/hybrid KEX (e.g., sntrup761x25519 in OpenSSH if allowed). For HPKE, rely on native language runtime/vendor/audited libraries that support ML‑KEM. -- IPsec re‑key: configure time/byte‑based lifetimes to re‑key IKE_SA and CHILD_SAs; ensure re‑key maintains the same algorithms used during IKEv2 exchanges. - -## Migration -- Inventory endpoints and crypto usage. -- Prioritize external/high‑value/long‑lived flows. -- Roll out hybrids in stages with metrics and rollback; remove predecessor configs after success criteria are met. - -## Implementation Checklist -- Hybrid key establishment with ML‑KEM‑768 + ECDHE; ML‑KEM‑1024 where required. -- Avoid ML‑KEM‑512 except in explicitly risk‑accepted, highly constrained device or network environments, with compensating controls and tight scope. -- No draft Kyber groups; only vendor‑documented ML‑KEM hybrids. - - Algorithm agility via configuration (not code); explicit fallback behavior. - - Keys via validated modules; separated by purpose; rotation policy in place. - - Hardware‑backed key storage (HSM/TPM) required before enabling ML‑DSA signatures; continue ECDSA (P‑256) for mTLS/signing until hardware support exists. - - (D)TLS version and group selections align with supported PQC options. - - IPsec re‑key configured (time/byte lifetimes) with PFS; hybrid ML‑KEM + ECDHE persists across re‑key. - - Monitoring in place for handshake success/latency/errors and negotiated groups. - -## Test Plan -- Interoperability: verify hybrid ML‑KEM‑768 and ML‑KEM‑1024 handshakes across representative clients; validate negotiated groups and fallback paths. -- Interoperability ((D)TLS): verify negotiation of X25519MLKEM768 / SecP256r1MLKEM768 hybrids and fallback to classical ECDHE; validate pure ML‑KEM groups only in staged tests. -- Interoperability (IKEv2/IPsec): verify hybrid ML‑KEM‑768 + ECDHE (X25519 or P‑256) via RFC 9242/9370 multi‑KE; confirm fallback to classical ECDHE; evaluate ML‑KEM‑1024 where required. Use vendor‑documented identifiers; -- Re‑key (IKEv2/IPsec): validate re‑key of IKE_SA and CHILD_SA maintains hybrid ML‑KEM + ECDHE; confirm no fallback to classical‑only on re‑key; measure re‑key overhead. -- Negative: reject legacy/draft Hybrid‑Kyber identifiers and misconfigured groups. -- Performance: measure handshake size and latency (peak and P95) and server CPU after enabling hybrids; tune record sizes and limits as needed. From e0cde0ddbcef7fde7767192722df92acd1930b84 Mon Sep 17 00:00:00 2001 From: Ramraj Bishnoie Date: Fri, 16 Jan 2026 16:20:03 -0500 Subject: [PATCH 40/40] chore: converting claude code to agent skills --- README.md | 4 +- src/convert_to_ide_formats.py | 132 ++++++++++-------- src/formats/__init__.py | 10 +- src/formats/{claudecode.py => agentskills.py} | 61 ++++---- 4 files changed, 116 insertions(+), 91 deletions(-) rename src/formats/{claudecode.py => agentskills.py} (52%) diff --git a/README.md b/README.md index ceb5d6f..e30bc60 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Get started in minutes: ## How It Works 1. **Security rules** are written in unified markdown format (`sources/` directory) -2. **Conversion tools** translate rules to IDE-specific formats (Cursor, Windsurf, Copilot, Claude Code, Antigravity) +2. **Conversion tools** translate rules to IDE-specific formats (Cursor, Windsurf, Copilot, Agent Skills, Antigravity) 3. **Release automation** packages rules into downloadable ZIP files 4. **AI assistants** reference these rules when generating or reviewing code 5. **Secure code** is produced automatically without developer intervention @@ -63,7 +63,7 @@ Get started in minutes: ``` sources/ # Source rules -skills/ # Claude Code plugin (generated, committed) +skills/ # Agent Skills format (generated, committed) src/ # Conversion and validation tools dist/ # Other IDE bundles (generated, not committed) ``` diff --git a/src/convert_to_ide_formats.py b/src/convert_to_ide_formats.py index 1657366..0a1e2d0 100644 --- a/src/convert_to_ide_formats.py +++ b/src/convert_to_ide_formats.py @@ -6,8 +6,8 @@ Convert Unified Rules to IDE Formats Transforms the unified markdown sources into IDE-specific bundles (Cursor, -Windsurf, Copilot, Claude Code). This script is the main entry point for producing -distributable rule packs from the sources/ directory. +Windsurf, Copilot, Agent Skills, Antigravity). This script is the main entry point +for producing distributable rule packs from the sources/ directory. """ import re @@ -16,7 +16,13 @@ from collections import defaultdict from converter import RuleConverter -from formats import CursorFormat, WindsurfFormat, CopilotFormat, ClaudeCodeFormat, AntigravityFormat +from formats import ( + CursorFormat, + WindsurfFormat, + CopilotFormat, + AgentSkillsFormat, + AntigravityFormat, +) from utils import get_version_from_pyproject from validate_versions import set_plugin_version, set_marketplace_version @@ -26,7 +32,7 @@ def sync_plugin_metadata(version: str) -> None: """ - Sync version from pyproject.toml to Claude Code plugin metadata files. + Sync version from pyproject.toml to Agent Skills metadata files. Args: version: Version string from pyproject.toml @@ -39,17 +45,17 @@ def sync_plugin_metadata(version: str) -> None: def matches_tag_filter(rule_tags: list[str], filter_tags: list[str]) -> bool: """ Check if rule has all required tags (AND logic). - + Args: rule_tags: List of tags from the rule (already normalized to lowercase) filter_tags: List of tags to filter by (already normalized to lowercase) - + Returns: True if rule has all filter tags (or no filter), False otherwise """ if not filter_tags: return True # No filter means all pass - + return all(tag in rule_tags for tag in filter_tags) @@ -98,14 +104,20 @@ def update_skill_md(language_to_rules: dict[str, list[str]], skill_path: str) -> print(f"Updated SKILL.md with language mappings") -def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: bool = True, version: str = None, filter_tags: list[str] = None) -> dict[str, list[str]]: +def convert_rules( + input_path: str, + output_dir: str = "dist", + include_agentskills: bool = True, + version: str = None, + filter_tags: list[str] = None, +) -> dict[str, list[str]]: """ Convert rule file(s) to all supported IDE formats using RuleConverter. Args: input_path: Path to a single .md file or folder containing .md files output_dir: Output directory (default: 'dist/') - include_claudecode: Whether to generate Claude Code plugin (default: True, only for core rules) + include_agentskills: Whether to generate Agent Skills format (default: True, only for core rules) version: Version string to use (default: read from pyproject.toml) filter_tags: Optional list of tags to filter by (AND logic, case-insensitive) @@ -117,7 +129,7 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: } Example: - results = convert_rules("sources/core", "dist", include_claudecode=True) + results = convert_rules("sources/core", "dist", include_agentskills=True) print(f"Converted {len(results['success'])} rules") """ if version is None: @@ -130,10 +142,10 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: CopilotFormat(version), AntigravityFormat(version), ] - - # Only include Claude Code for core rules (committed plugin) - if include_claudecode: - all_formats.append(ClaudeCodeFormat(version)) + + # Only include Agent Skills format for core rules (committed as skills) + if include_agentskills: + all_formats.append(AgentSkillsFormat(version)) converter = RuleConverter(formats=all_formats) path = Path(input_path) @@ -151,7 +163,7 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: md_files = sorted(list(path.rglob("*.md"))) if not md_files: raise ValueError(f"No .md files found in {input_path}") - + print(f"Converting {len(md_files)} files from: {path}") # Setup output directory @@ -165,7 +177,7 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: try: # Convert the file (raises exceptions on error) result = converter.convert(md_file) - + # Apply tag filter if specified if filter_tags and not matches_tag_filter(result.tags, filter_tags): results["skipped"].append(result.filename) @@ -175,17 +187,15 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: output_files = [] for format_name, output in result.outputs.items(): # Construct output path - # Claude Code goes to project root ./skills/ + # Agent Skills goes to project root ./skills/ # Other formats go to dist/ (or specified output_dir) - if format_name == "claudecode": + if format_name == "agentskills": base_dir = PROJECT_ROOT else: base_dir = output_base - + output_file = ( - base_dir - / output.subpath - / f"{result.basename}{output.extension}" + base_dir / output.subpath / f"{result.basename}{output.extension}" ) # Create directory if it doesn't exist and write file @@ -225,30 +235,32 @@ def convert_rules(input_path: str, output_dir: str = "dist", include_claudecode: f"\nResults: {len(results['success'])} success, {len(results['errors'])} errors" ) - # Generate SKILL.md with language mappings (only if Claude Code is included) - if include_claudecode and language_to_rules: - template_path = PROJECT_ROOT / "sources" / "core" / "codeguard-SKILLS.md.template" - + # Generate SKILL.md with language mappings (only if Agent Skills is included) + if include_agentskills and language_to_rules: + template_path = ( + PROJECT_ROOT / "sources" / "core" / "codeguard-SKILLS.md.template" + ) + if not template_path.exists(): raise FileNotFoundError( f"SKILL.md template not found at {template_path}. " - "This file is required for Claude Code plugin generation." + "This file is required for Agent Skills generation." ) - + output_skill_dir = PROJECT_ROOT / "skills" / "software-security" output_skill_dir.mkdir(parents=True, exist_ok=True) output_skill_path = output_skill_dir / "SKILL.md" - + # Read template and inject current version from pyproject.toml template_content = template_path.read_text(encoding="utf-8") # Replace the hardcoded version with actual version template_content = re.sub( r'codeguard-version:\s*"[^"]*"', f'codeguard-version: "{version}"', - template_content + template_content, ) output_skill_path.write_text(template_content, encoding="utf-8") - + update_skill_md(language_to_rules, str(output_skill_path)) return results @@ -262,7 +274,7 @@ def _resolve_source_paths(args) -> list[Path]: # If --source flags provided, resolve under sources/ if args.source: return [Path("sources") / src for src in args.source] - + # Default: core rules only return [Path("sources/core")] @@ -270,7 +282,7 @@ def _resolve_source_paths(args) -> list[Path]: if __name__ == "__main__": import sys from argparse import ArgumentParser - + parser = ArgumentParser( description="Convert unified rule markdown into IDE-specific bundles." ) @@ -291,7 +303,7 @@ def _resolve_source_paths(args) -> list[Path]: dest="tags", help="Filter rules by tags (comma-separated, case-insensitive, AND logic). Example: --tag api,web-security", ) - + cli_args = parser.parse_args() source_paths = _resolve_source_paths(cli_args) @@ -307,27 +319,31 @@ def _resolve_source_paths(args) -> list[Path]: for source_path in source_paths: for md_file in source_path.rglob("*.md"): filename_to_sources[md_file.name].append(source_path.name) - - duplicates = {name: srcs for name, srcs in filename_to_sources.items() if len(srcs) > 1} + + duplicates = { + name: srcs for name, srcs in filename_to_sources.items() if len(srcs) > 1 + } if duplicates: print(f"❌ Found {len(duplicates)} duplicate filename(s) across sources:") for filename, sources in duplicates.items(): print(f" - {filename} in: {', '.join(sources)}") print("\nPlease rename files to have unique names across all sources.") sys.exit(1) - + # Get version once and sync to metadata files version = get_version_from_pyproject() sync_plugin_metadata(version) - # Check if core is in the sources for Claude Code plugin generation + # Check if core is in the sources for Agent Skills generation has_core = Path("sources/core") in source_paths if has_core: # Validate template exists early - template_path = PROJECT_ROOT / "sources" / "core" / "codeguard-SKILLS.md.template" + template_path = ( + PROJECT_ROOT / "sources" / "core" / "codeguard-SKILLS.md.template" + ) if not template_path.exists(): print(f"❌ SKILL.md template not found at {template_path}") - print("This file is required for Claude Code plugin generation.") + print("This file is required for Agent Skills generation.") sys.exit(1) # Clean output directories once before processing @@ -341,46 +357,50 @@ def _resolve_source_paths(args) -> list[Path]: if skills_rules_dir.exists(): shutil.rmtree(skills_rules_dir) print(f"✅ Cleaned skills/ directory") - + # Print processing summary if len(source_paths) > 1: - sources_list = ', '.join(p.name for p in source_paths) + sources_list = ", ".join(p.name for p in source_paths) print(f"\nConverting {len(source_paths)} sources: {sources_list}") if has_core: - print("(Claude Code plugin will include only core rules)") + print("(Agent Skills will include only core rules)") print() - + # Convert all sources aggregated = {"success": [], "errors": [], "skipped": []} # Parse comma-separated tags and normalize to lowercase filter_tags = None if cli_args.tags: - filter_tags = [tag.strip().lower() for tag in cli_args.tags.split(",") if tag.strip()] - + filter_tags = [ + tag.strip().lower() for tag in cli_args.tags.split(",") if tag.strip() + ] + # Print tag filter info if active if filter_tags: - print(f"Tag filter active: {', '.join(filter_tags)} (AND logic - rules must have all tags)\n") - + print( + f"Tag filter active: {', '.join(filter_tags)} (AND logic - rules must have all tags)\n" + ) + for source_path in source_paths: is_core = source_path == Path("sources/core") - + print(f"Processing: {source_path}") results = convert_rules( - str(source_path), - cli_args.output_dir, - include_claudecode=is_core, + str(source_path), + cli_args.output_dir, + include_agentskills=is_core, version=version, - filter_tags=filter_tags + filter_tags=filter_tags, ) - + aggregated["success"].extend(results["success"]) aggregated["errors"].extend(results["errors"]) if "skipped" in results: aggregated["skipped"].extend(results["skipped"]) print("") - + if aggregated["errors"]: print("❌ Some conversions failed") sys.exit(1) - + print("✅ All conversions successful") diff --git a/src/formats/__init__.py b/src/formats/__init__.py index 55cc0d0..e1a48a3 100644 --- a/src/formats/__init__.py +++ b/src/formats/__init__.py @@ -11,7 +11,8 @@ - CursorFormat: Generates .mdc files for Cursor IDE - WindsurfFormat: Generates .md files for Windsurf IDE - CopilotFormat: Generates .instructions.md files for GitHub Copilot -- ClaudeCodeFormat: Generates .md files for Claude Code plugins +- AgentSkillsFormat: Generates .md files for Agent Skills (OpenAI Codex, Claude Code, other AI coding tools) +- AntigravityFormat: Generates .md files for Google Antigravity Usage: from formats import BaseFormat, ProcessedRule, CursorFormat, WindsurfFormat, CopilotFormat, ClaudeCodeFormat @@ -21,7 +22,8 @@ CursorFormat(version), WindsurfFormat(version), CopilotFormat(version), - ClaudeCodeFormat(version), + AgentSkillsFormat(version), + AntigravityFormat(version), ] """ @@ -29,7 +31,7 @@ from formats.cursor import CursorFormat from formats.windsurf import WindsurfFormat from formats.copilot import CopilotFormat -from formats.claudecode import ClaudeCodeFormat +from formats.agentskills import AgentSkillsFormat from formats.antigravity import AntigravityFormat __all__ = [ @@ -38,6 +40,6 @@ "CursorFormat", "WindsurfFormat", "CopilotFormat", - "ClaudeCodeFormat", + "AgentSkillsFormat", "AntigravityFormat", ] diff --git a/src/formats/claudecode.py b/src/formats/agentskills.py similarity index 52% rename from src/formats/claudecode.py rename to src/formats/agentskills.py index 4c3b152..798b2db 100644 --- a/src/formats/claudecode.py +++ b/src/formats/agentskills.py @@ -3,72 +3,75 @@ # SPDX-License-Identifier: Apache-2.0 """ -Claude Code Format Implementation +Agent Skills Format Implementation -Generates .md files for Claude Code Skills/Plugins. +Generates .md files for the Agent Skills standard (agentskills.io). +This format is used by OpenAI Codex, Claude Code, and other AI coding tools. """ from formats.base import BaseFormat, ProcessedRule -class ClaudeCodeFormat(BaseFormat): +class AgentSkillsFormat(BaseFormat): """ - Claude Code plugin format implementation (.md files). - - Claude Code Skills use standard markdown files without - special frontmatter. The original rule content is preserved - and placed in the skills/software-security/rules/ directory - for plugin distribution. - - Unlike other IDE formats, Claude Code doesn't require special - frontmatter transformations - it uses the rules as-is for - plugin-based Skills. + Agent Skills format implementation (.md files). + + Agent Skills (https://agentskills.io/) is an open standard for extending + AI coding agents with task-specific capabilities. It uses standard markdown + files with YAML frontmatter to define rules and instructions. + + This format is adopted by: + - OpenAI Codex (skills) + - Claude Code (plugins) + - Other AI coding tools + + The original rule content is preserved and placed in the + skills/software-security/rules/ directory for distribution. """ def get_format_name(self) -> str: - """Return Claude Code format identifier.""" - return "claudecode" + """Return Agent Skills format identifier.""" + return "agentskills" def get_file_extension(self) -> str: - """Return Claude Code format file extension.""" + """Return Agent Skills format file extension.""" return ".md" def get_output_subpath(self) -> str: - """Return Claude Code output subdirectory.""" + """Return Agent Skills output subdirectory.""" return "skills/software-security/rules" def generate(self, rule: ProcessedRule, globs: str) -> str: """ - Generate Claude Code .md format. - - Claude Code Skills should preserve the original YAML frontmatter + Generate Agent Skills .md format. + + Agent Skills should preserve the original YAML frontmatter (description, languages, alwaysApply) so the rules remain complete - and can be referenced properly. - + and can be referenced properly by AI coding agents. + Args: rule: The processed rule to format - globs: Glob patterns (not used for Claude Code format) - + globs: Glob patterns (not used for Agent Skills format) + Returns: Complete markdown with original YAML frontmatter preserved """ # Build YAML frontmatter yaml_lines = [] - + # Add description desc = self._format_yaml_field("description", rule.description) if desc: yaml_lines.append(desc) - + # Add languages if present if rule.languages: # Format as YAML list yaml_lines.append("languages:") for lang in rule.languages: yaml_lines.append(f"- {lang}") - + # Add alwaysApply yaml_lines.append(f"alwaysApply: {str(rule.always_apply).lower()}") - - return self._build_yaml_frontmatter(yaml_lines, rule.content) + return self._build_yaml_frontmatter(yaml_lines, rule.content)