mirror of
https://github.com/github/spec-kit.git
synced 2026-07-06 05:53:12 +08:00
2fab3660fece7743234da43f1525ae8ab6a327c9
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6fb7e77b3e |
fix: allow prerelease spec-kit versions in compatibility checks (#2695)
* docs: generate integrations reference from catalog * refactor: integrate table rendering into specify integration search --markdown - Remove standalone scripts/generate_integrations_reference.py - Strip doc injection machinery from catalog_docs.py; keep only table rendering - Wire render_integrations_table() into existing --markdown flag of integration search - Remove old simple markdown table block from integration_search (was Name|ID|Version|Description|Author) - Simplify tests: drop subprocess/doc-path tests, keep table rendering and metadata tests - Clean up docs/reference/integrations.md: remove generated markers, update note * fix: address Copilot review feedback on catalog_docs and integration_search - Warn when --markdown is combined with filters (query/--tag/--author) which are silently ignored; catch ValueError/FileNotFoundError and surface clean error via console instead of raw traceback (r3244821516) - Add coverage enforcement in list_integrations_for_docs(): raises ValueError with actionable message if any registry key is missing from INTEGRATION_DOC_URLS, preventing silently incomplete doc tables (r3244821589) - Rename test to accurately reflect sources: label derives from registry config, URL comes from INTEGRATION_DOC_URLS doc map — not solely from registry (r3244821607) - Simplify test dict construction to idiomatic dict comprehension (r3244821619) * fix: add sync test, INTEGRATIONS_REFERENCE_PATH constant, and fix naming * revert: restore docs/reference/integrations.md to upstream/main; remove sync test (GH Actions job will handle) * fix: remove dead INTEGRATIONS_REFERENCE_PATH, drop URL-length padding, fix docstring, drop FileNotFoundError * fix: send --markdown warnings/errors to stderr, rename test for clarity * fix: detect stale doc-map keys, test _render_cell escaping, strengthen header assertion * refactor: promote _render_cell to public render_cell function * test: mock registry and doc maps to avoid brittle live registry coupling * refactor: flatten patches, remove unused imports, fix trailing whitespace, optimize missing calculation * refactor: make validation non-fatal, fix context manager syntax, add CLI tests * fix: improve docstring clarity, test robustness, and exception handling * fix: improve test assertions, disable warnings by default, enhance exception handling * fix: make CLI tests deterministic and improve config access resilience * fix: remove extra blank line, add stale keys validation, add regression test for docs sync * Fix 5 remaining feedback items: - Rename _get_mocked_cli_runner() to _get_catalog_docs_patches() for clarity - Use ExitStack context manager for guaranteed patch cleanup - Add explicit UTF-8 encoding to file reads - Skip doc sync test gracefully when docs aren't present - Remove exception chaining from typer.Exit to avoid noisy tracebacks * address all outstanding copilot review feedback on PR 2563 * Address Copilot feedback: escape URLs in markdown links, deduplicate cell rendering, fix table parser for escaped pipes * Address 3 new Copilot feedback: add URL escaping test, fix parse_first_markdown_table for escaped pipes, guard community tests with skip * Address 3 new Copilot feedback: escape id field, remove unused alias, escape integration URLs * Address 3 new Copilot feedback: fix comment name, include all integrations in list * Fix architectural issue: escape raw fields before composing Markdown to prevent double-escaping * Deduplicate _escape_url_for_markdown_link and add URL escaping test * Address 4 new Copilot feedback: add trailing newline, fix test helper ExitStack, update warning message * Address 4 new Copilot feedback: make escape function public, fix error message, validate test rows, prevent double newline * Update error message in test_missing_catalog_file for clarity * Remove obsolete integrations sync test * keep integrations docs in sync * fix: allow prerelease spec-kit versions in compatibility checks Allow prerelease/dev builds to satisfy extension and preset compatibility checks when their version number falls within the required specifier range. Also harden the integrations docs rendering helpers and add regression coverage for the markdown table parsing and version gating paths. Tests: pytest -q; python3 -m compileall -q .; black/flake8 unavailable Reference: branch 002-generate-integrations-docs; source patch /tmp/spec-kit-changes.patch * fix: isolate prerelease compatibility gate changes Keep the prerelease/version compatibility fix on its own branch and remove the unrelated integrations docs updates that belong with PR 2563. Tests: full suite passed on the prerelease branch before splitting; docs branch covered by targeted docs tests Reference: upstream/main; source patch /tmp/spec-kit-changes.patch * Address PR 2695 feedback: Centralize prerelease policy and add boundary test * Address remaining Copilot PR feedback: revert docs and add preset prerelease tests * Remove unreachable raise CompatibilityError * Fix PEP8 E302 and E303 formatting issues |
||
|
|
8e76ff3d5c |
harden: reject shell=True in run_command (#3132)
run_command() forwarded shell= straight to subprocess.run, so a caller passing shell=True would invoke a shell. Reject shell=True with ValueError (keeping the parameter for signature compatibility) and drop shell= from both subprocess.run calls. Enable ruff S602/S604/S605 to flag any future shell=True reintroduction, annotate the one intentional workflow shell sink with # noqa: S602, and document the shell-step execution risk in workflows/PUBLISHING.md. |
||
|
|
902f5431f9 |
Harden command registration path handling (#3088)
* fix: validate command 'file' field against path traversal in registrar CommandRegistrar.register_commands() read each command body from source_dir / cmd_file without validating the manifest 'file' field, unlike the parallel skill and preset readers which already reject absolute paths and '..' traversal. A malicious extension/preset/bundle manifest with file: ../../../etc/passwd (or an absolute path) could read arbitrary host files verbatim into a generated agent command at a predictable path (GHSA-w5fv-7w9x-7fc5, CWE-22). Add the same containment guard at the command read site and reject a traversal/absolute 'file' at manifest-load time in ExtensionManifest._validate() for defense-in-depth, plus regression tests for both the read path and the manifest validator. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test/fix: address review — robust absolute-path test and tolerant reads - register_commands(): use is_file() instead of exists() and skip the command if read_text() raises (directory or non-UTF8 file), aligning with the other command/skill readers. - Traversal tests: point the absolute-path payload at the real temp secret.txt (guaranteed to exist on all platforms) instead of /etc/passwd, so the absolute-path guard is genuinely exercised and the test fails if it regresses, rather than passing because the target happens not to exist (e.g. on Windows runners). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: rename traversal fixtures to avoid CodeQL secret-storage false positive The regression fixtures named an out-of-tree file secret.txt with TOP-SECRET-CREDENTIAL content. CodeQL's clear-text-storage heuristic treated that read content as sensitive and followed the static path into the pre-existing write_text sinks in _write_registered_output, raising false 'clear-text storage of sensitive information' alerts on PR 3088. Rename the fixtures to neutral outside.txt / OUTSIDE-FILE-MARKER and drop /etc/passwd payloads; the test semantics (a file outside source_dir must never be read into a generated command) are unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: reject Windows drive-relative 'file' values in traversal guards is_absolute() is False for Windows drive-relative paths like C:outside.txt, which contain no '..' yet resolve against the process CWD on that drive — bypassing the containment guard on Windows. Evaluate the 'file' value under PureWindowsPath as well so both the registrar runtime guard and the manifest-load validator reject drive letters (and backslash '..' segments) cross-platform. Extend the regression tests with drive-relative cases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: use anchor under both path flavors so POSIX-absolute is rejected on Windows On a Windows runner WindowsPath('/abs/outside.md').is_absolute() is False (no drive), so the prior native-Path check let a leading-slash 'file' value through and the manifest validator did not raise. Evaluate the value under both PurePosixPath and PureWindowsPath and reject any non-empty anchor — covering POSIX-absolute, Windows drive-relative, Windows absolute, and rooted-without-drive — in both the registrar guard and the manifest validator. The registrar join now uses the raw 'file' string so native separators are handled by the resolve()/relative_to() containment check. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: validate command 'file' field against path traversal in registrar CommandRegistrar.register_commands() read each command body from source_dir / cmd_file without validating the manifest 'file' field, unlike the parallel skill and preset readers which already reject absolute paths and '..' traversal. A malicious extension/preset/bundle manifest with file: ../../../etc/passwd (or an absolute path) could read arbitrary host files verbatim into a generated agent command at a predictable path (GHSA-w5fv-7w9x-7fc5, CWE-22). Add the same containment guard at the command read site and reject a traversal/absolute 'file' at manifest-load time in ExtensionManifest._validate() for defense-in-depth, plus regression tests for both the read path and the manifest validator. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test/fix: address review — robust absolute-path test and tolerant reads - register_commands(): use is_file() instead of exists() and skip the command if read_text() raises (directory or non-UTF8 file), aligning with the other command/skill readers. - Traversal tests: point the absolute-path payload at the real temp secret.txt (guaranteed to exist on all platforms) instead of /etc/passwd, so the absolute-path guard is genuinely exercised and the test fails if it regresses, rather than passing because the target happens not to exist (e.g. on Windows runners). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: rename traversal fixtures to avoid CodeQL secret-storage false positive The regression fixtures named an out-of-tree file secret.txt with TOP-SECRET-CREDENTIAL content. CodeQL's clear-text-storage heuristic treated that read content as sensitive and followed the static path into the pre-existing write_text sinks in _write_registered_output, raising false 'clear-text storage of sensitive information' alerts on PR 3088. Rename the fixtures to neutral outside.txt / OUTSIDE-FILE-MARKER and drop /etc/passwd payloads; the test semantics (a file outside source_dir must never be read into a generated command) are unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: reject Windows drive-relative 'file' values in traversal guards is_absolute() is False for Windows drive-relative paths like C:outside.txt, which contain no '..' yet resolve against the process CWD on that drive — bypassing the containment guard on Windows. Evaluate the 'file' value under PureWindowsPath as well so both the registrar runtime guard and the manifest-load validator reject drive letters (and backslash '..' segments) cross-platform. Extend the regression tests with drive-relative cases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: use anchor under both path flavors so POSIX-absolute is rejected on Windows On a Windows runner WindowsPath('/abs/outside.md').is_absolute() is False (no drive), so the prior native-Path check let a leading-slash 'file' value through and the manifest validator did not raise. Evaluate the value under both PurePosixPath and PureWindowsPath and reject any non-empty anchor — covering POSIX-absolute, Windows drive-relative, Windows absolute, and rooted-without-drive — in both the registrar guard and the manifest validator. The registrar join now uses the raw 'file' string so native separators are handled by the resolve()/relative_to() containment check. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: harden register_commands inputs and tighten manifest 'file' validation Address review feedback on #3088: - register_commands(): skip non-string/empty 'file' values instead of raising TypeError, and hoist source_dir.resolve() out of the per-command loop. - ExtensionManifest._validate(): reject 'file' values with leading/trailing whitespace with a clear ValidationError instead of a confusing missing-file failure later. - tests: add non-string 'file' and whitespace cases; use yaml.safe_dump with explicit utf-8 encoding in the manifest validation test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: align runtime '..' policy, correct comment, dedupe test helper Address review feedback on #3088: - register_commands(): also reject '..' segments under both POSIX and Windows semantics, keeping runtime policy consistent with ExtensionManifest._validate() and the skill/preset readers (not just relying on the resolve()/relative_to() containment backstop). - Replace the version-dependent is_absolute() claim in the extensions.py comment with the actual portability rationale (native Path is OS- dependent; C:foo is anchored but not absolute). - Extract the duplicated leak-detection assertion into _assert_no_marker_leak() and add an in-bounds '..' payload that exercises the new runtime '..' rejection. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Extract shared path-safety policy and warn on unreadable command files Introduce relative_extension_path_violation() in _utils.py as the single source of truth for the extension-relative `file` path-safety policy, and use it from both the runtime registrar guard (agents.py) and the manifest-load validator (extensions.py) so the two cannot drift. Warn (instead of silently skipping) when an in-bounds command file exists but cannot be read/decoded, surfacing misconfigured extensions. Add unit tests for the shared helper, a read-skip warning test, and make the in-bounds `..` test create its target file so the skip is attributable to the `..` rejection rather than file absence. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Retrigger CI Empty commit to re-trigger code scanning / CodeQL analysis on the PR merge ref. Assisted-by: GitHub Copilot CLI (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
811a3aa447 |
fix(skills): preserve non-ASCII characters in skill frontmatter (#2917)
* fix(skills): preserve non-ASCII chars in skill frontmatter Skill SKILL.md frontmatter descriptions containing non-ASCII characters were escaped to \uXXXX / \xXX sequences because yaml.safe_dump() was called without allow_unicode=True. - Add allow_unicode=True to the 7 skill/command frontmatter safe_dump sites (extensions, presets, claude integration) - Add regression tests for the render and extension-install paths Follows the approach of #1936; encoding="utf-8" is already set on the affected write paths, so no encoding change is needed here. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(_utils): add dump_frontmatter helper Centralize skill/command frontmatter YAML serialization into a single _utils.dump_frontmatter helper so no call site can drop allow_unicode or diverge on formatting. Route the 7 existing sites through it and drop a now-unused local yaml import. Switch the extension test fixtures to yaml.safe_dump for parity with the production safe-dump/safe-load codepaths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
927f54feea |
feat: make git extension opt-in and remove --no-git at v0.10.0 (#2873)
* feat(init)!: make git extension opt-in and remove --no-git at v0.10.0 - Remove --no-git parameter from specify init command - Remove git extension auto-installation from init flow - Git repository initialization (git init) still runs when git is available - Remove --no-git from all test invocations across the test suite - Update docs to reflect opt-in git extension behavior - Replace TestGitExtensionAutoInstall with TestGitExtensionOptIn tests BREAKING CHANGE: specify init no longer auto-installs the git extension. Use `specify extension add git` to install it explicitly. The --no-git flag has been removed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor(scripts): remove git operations from core scripts Git functionality is now entirely managed by the git extension. Core scripts only handle directory-based feature creation and numbering. - Remove has_git(), check_feature_branch(), git branch creation from core - Simplify number detection to use only spec directory scanning - Remove HAS_GIT output from get_feature_paths() - Remove git remote fetching and branch querying - Keep BRANCH_NAME output key for backward compatibility Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: remove all git operations from core - Remove is_git_repo() and init_git_repo() dead code from _utils.py - Remove --branch-numbering from init command - Remove git from 'specify check' (now extension-only) - Update docs: git is optional prerequisite, check command description - Fix tests to reflect no-git-in-core reality (fallback to main) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor(scripts): remove directory scanning and branch fallback from core Core scripts now resolve feature context exclusively from: 1. SPECIFY_FEATURE env var (set by git extension) 2. .specify/feature.json (persisted by specify command) Removed find_feature_dir_by_prefix() and directory scanning heuristics — these are the git extension's responsibility. Scripts error clearly when no feature context is available. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: introduce feature_numbering, deprecate branch_numbering in init-options - specify command template now reads feature_numbering (preferred) with fallback to branch_numbering (deprecated) from init-options.json - Git extension reads git-config.yml > feature_numbering > branch_numbering - init now writes feature_numbering: sequential to init-options.json - Deprecation warning emitted when branch_numbering is used as fallback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: remove trailing whitespace in common.ps1 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(scripts): persist SPECIFY_FEATURE_DIRECTORY env var to feature.json When SPECIFY_FEATURE_DIRECTORY is set, get_feature_paths() now writes the value to .specify/feature.json so future sessions without the env var can still resolve the feature directory. The write is idempotent — it skips when the file already contains the same value. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: address review feedback — error messages and docs - Update error messages in common.sh and common.ps1 to reference SPECIFY_FEATURE_DIRECTORY instead of SPECIFY_FEATURE (which no longer resolves feature directories) - Fix get_current_branch comment (returns empty string, not error) - Update upgrade.md to reference SPECIFY_FEATURE_DIRECTORY with correct example paths - Update local-development.md troubleshooting: replace stale 'Git step skipped' row with actionable git extension guidance Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(scripts): harden feature.json persistence - Use json_escape in printf fallback when jq is unavailable (common.sh) - Replace utf8NoBOM encoding with UTF8Encoding($false) for PowerShell 5.1 compatibility (common.ps1) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor(scripts): remove dead feature_json_matches_feature_dir functions These guards are no longer needed since the branch-name validation they protected against has been removed from check-prerequisites. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor(git-ext): rename create-new-feature to create-new-feature-branch The git extension's script only creates the git branch — rename it to reflect that responsibility. The core create-new-feature.sh/.ps1 handles feature directory creation and feature.json persistence. Also includes fixes from review feedback: - common.sh: _persist_feature_json uses json_escape fallback - common.ps1: Save-FeatureJson uses UTF8Encoding for PS 5.1 compat - common.ps1: case-sensitive path stripping on non-Windows - create-new-feature.sh/ps1: output both SPECIFY_FEATURE and SPECIFY_FEATURE_DIRECTORY - setup-tasks.sh: fix stale 'Validate branch' comment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(tests): update references to renamed git extension scripts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(tests): remove duplicate EXT_CREATE_FEATURE assignments Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Manfred Riem <mnriem@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
34ce66139e |
feat: add support for rovodev (#2539)
* feat: add support for rovodev * fixup! feat: add support for rovodev * fixup! feat: add support for rovodev * fixup! feat: add support for rovodev * fixup! feat: add support for rovodev * fixup! feat: add support for rovodev * fixup! feat: add support for rovodev * fixup! feat: add support for rovodev * fixup! feat: add support for rovodev * fixup! feat: add support for rovodev * fixup! feat: add support for rovodev * fixup! feat: add support for rovodev |
||
|
|
2fb9d3bb4b |
refactor: extract _assets.py and _utils.py from __init__.py (PR-2/8) (#2543)
* refactor: extract _assets.py and _utils.py from __init__.py Move bundle path resolution and version lookup into _assets.py (stdlib only, zero internal imports), and system utilities (subprocess, tool detection, file operations) into _utils.py (imports only from ._console). Re-export all moved symbols from __init__.py for backward compatibility. Update test_check_tool.py to patch both specify_cli and specify_cli._utils namespaces since constants are now defined in _utils. * style: apply PR-1 review patterns to _assets.py and _utils.py - Add module docstring to _assets.py (stdlib-only, zero internal imports) - Add blank line after `from __future__ import annotations` in both files - Replace `Optional[X]` with `X | None` throughout _utils.py (PEP 604) - Remove unused `Optional` import from _utils.py - Use explicit re-export form (`X as X`) for public symbols in __init__.py - Remove unused `subprocess` and `tempfile` imports from __init__.py (moved to _utils.py) |