Merge branch 'codex/security-robustness' into codex/merge-security-main
This commit is contained in:
@@ -3,7 +3,6 @@ name: alpha-vantage
|
||||
description: Access real-time and historical stock market data, forex rates, cryptocurrency prices, commodities, economic indicators, and 50+ technical indicators via the Alpha Vantage API. Use when fetching stock prices (OHLCV), company fundamentals (income statement, balance sheet, cash...
|
||||
risk: unknown
|
||||
source: community
|
||||
--- Unknown
|
||||
metadata:
|
||||
skill-author: K-Dense Inc.
|
||||
---
|
||||
|
||||
@@ -27,8 +27,10 @@
|
||||
# - Transitioning from research/exploration to implementation
|
||||
# - Plan has been finalized
|
||||
|
||||
# Track tool call count (increment in a temp file)
|
||||
COUNTER_FILE="/tmp/claude-tool-count-$$"
|
||||
# Track tool call count in a user-owned state directory
|
||||
COUNTER_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/strategic-compact"
|
||||
mkdir -p "$COUNTER_DIR"
|
||||
COUNTER_FILE="$COUNTER_DIR/tool-count"
|
||||
THRESHOLD=${COMPACT_THRESHOLD:-50}
|
||||
|
||||
# Initialize or increment counter
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -56,7 +56,10 @@ def parse_args():
|
||||
output_dir = arg
|
||||
|
||||
if output_dir is None:
|
||||
output_dir = os.environ.get("VIDEODB_EVENTS_DIR", "/tmp")
|
||||
output_dir = os.environ.get("VIDEODB_EVENTS_DIR")
|
||||
if output_dir is None:
|
||||
state_root = Path(os.environ.get("XDG_STATE_HOME", Path.home() / ".local" / "state"))
|
||||
output_dir = str(state_root / "videodb-events")
|
||||
|
||||
return clear, Path(output_dir)
|
||||
|
||||
|
||||
19
tools/scripts/tests/local_temp_safety.test.js
Normal file
19
tools/scripts/tests/local_temp_safety.test.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const assert = require("assert");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const repoRoot = path.resolve(__dirname, "../..", "..");
|
||||
|
||||
const compactScript = fs.readFileSync(
|
||||
path.join(repoRoot, "skills", "cc-skill-strategic-compact", "suggest-compact.sh"),
|
||||
"utf8",
|
||||
);
|
||||
const wsListener = fs.readFileSync(
|
||||
path.join(repoRoot, "skills", "videodb", "scripts", "ws_listener.py"),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
assert.match(compactScript, /XDG_STATE_HOME/, "strategic compact counter should use a user-owned state directory");
|
||||
assert.doesNotMatch(compactScript, /\/tmp\/claude-tool-count/, "strategic compact counter must not use predictable /tmp files");
|
||||
assert.match(wsListener, /XDG_STATE_HOME/, "videodb listener should default to a user-owned state directory");
|
||||
assert.doesNotMatch(wsListener, /VIDEODB_EVENTS_DIR", "\/tmp"/, "videodb listener must not default to /tmp");
|
||||
22
tools/scripts/tests/repo_hygiene_security.test.js
Normal file
22
tools/scripts/tests/repo_hygiene_security.test.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const assert = require("assert");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const repoRoot = path.resolve(__dirname, "../..", "..");
|
||||
const pycacheDir = path.join(repoRoot, "skills", "ui-ux-pro-max", "scripts", "__pycache__");
|
||||
const syncRecommended = fs.readFileSync(
|
||||
path.join(repoRoot, "tools", "scripts", "sync_recommended_skills.sh"),
|
||||
"utf8",
|
||||
);
|
||||
const alphaVantage = fs.readFileSync(
|
||||
path.join(repoRoot, "skills", "alpha-vantage", "SKILL.md"),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
fs.existsSync(pycacheDir),
|
||||
false,
|
||||
"tracked Python bytecode should not ship in skill directories",
|
||||
);
|
||||
assert.match(syncRecommended, /cp -RP/, "recommended skills sync should preserve symlinks instead of dereferencing them");
|
||||
assert.doesNotMatch(alphaVantage, /--- Unknown/, "alpha-vantage frontmatter should not contain malformed delimiters");
|
||||
65
tools/scripts/tests/test_frontmatter_parsing_security.py
Normal file
65
tools/scripts/tests/test_frontmatter_parsing_security.py
Normal file
@@ -0,0 +1,65 @@
|
||||
import importlib.util
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[3]
|
||||
TOOLS_SCRIPTS_DIR = REPO_ROOT / "tools" / "scripts"
|
||||
if str(TOOLS_SCRIPTS_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(TOOLS_SCRIPTS_DIR))
|
||||
|
||||
|
||||
def load_module(relative_path: str, module_name: str):
|
||||
module_path = REPO_ROOT / relative_path
|
||||
spec = importlib.util.spec_from_file_location(module_name, module_path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
assert spec.loader is not None
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
generate_index = load_module("tools/scripts/generate_index.py", "generate_index")
|
||||
validate_skills = load_module("tools/scripts/validate_skills.py", "validate_skills")
|
||||
|
||||
|
||||
class FrontmatterParsingSecurityTests(unittest.TestCase):
|
||||
def test_generate_index_frontmatter_rejects_non_mapping(self):
|
||||
content = "---\njust-a-string\n---\nbody\n"
|
||||
metadata = generate_index.parse_frontmatter(content)
|
||||
|
||||
self.assertEqual(metadata, {})
|
||||
|
||||
def test_validate_skills_frontmatter_rejects_non_mapping(self):
|
||||
content = "---\njust-a-string\n---\nbody\n"
|
||||
metadata, errors = validate_skills.parse_frontmatter(content)
|
||||
|
||||
self.assertIsNone(metadata)
|
||||
self.assertTrue(any("mapping" in error.lower() for error in errors))
|
||||
|
||||
def test_generate_index_ignores_symlinked_skill_markdown(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = Path(temp_dir)
|
||||
skills_dir = root / "skills"
|
||||
safe_skill = skills_dir / "safe-skill"
|
||||
linked_skill = skills_dir / "linked-skill"
|
||||
outside_dir = root / "outside"
|
||||
output_file = root / "skills_index.json"
|
||||
|
||||
safe_skill.mkdir(parents=True)
|
||||
linked_skill.mkdir(parents=True)
|
||||
outside_dir.mkdir()
|
||||
|
||||
(safe_skill / "SKILL.md").write_text("---\nname: safe-skill\ndescription: safe\n---\n", encoding="utf-8")
|
||||
target = outside_dir / "SKILL.md"
|
||||
target.write_text("---\nname: outside\ndescription: outside\n---\n", encoding="utf-8")
|
||||
(linked_skill / "SKILL.md").symlink_to(target)
|
||||
|
||||
skills = generate_index.generate_index(str(skills_dir), str(output_file))
|
||||
|
||||
self.assertEqual([skill["id"] for skill in skills], ["safe-skill"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -4,6 +4,7 @@ import argparse
|
||||
import sys
|
||||
import io
|
||||
import yaml
|
||||
from collections.abc import Mapping
|
||||
from _project_paths import find_repo_root
|
||||
|
||||
|
||||
@@ -50,6 +51,8 @@ def parse_frontmatter(content, rel_path=None):
|
||||
fm_errors = []
|
||||
try:
|
||||
metadata = yaml.safe_load(fm_text) or {}
|
||||
if not isinstance(metadata, Mapping):
|
||||
return None, ["Frontmatter must be a YAML mapping/object."]
|
||||
|
||||
# Identification of the specific regression issue for better reporting
|
||||
if "description" in metadata:
|
||||
@@ -59,7 +62,7 @@ def parse_frontmatter(content, rel_path=None):
|
||||
elif desc == "|":
|
||||
fm_errors.append("description contains only the YAML block indicator '|', likely due to a parsing regression.")
|
||||
|
||||
return metadata, fm_errors
|
||||
return dict(metadata), fm_errors
|
||||
except yaml.YAMLError as e:
|
||||
return None, [f"YAML Syntax Error: {e}"]
|
||||
|
||||
@@ -86,6 +89,9 @@ def validate_skills(skills_dir, strict_mode=False):
|
||||
if "SKILL.md" in files:
|
||||
skill_count += 1
|
||||
skill_path = os.path.join(root, "SKILL.md")
|
||||
if os.path.islink(skill_path):
|
||||
warnings.append(f"⚠️ {os.path.relpath(skill_path, skills_dir)}: Skipping symlinked SKILL.md")
|
||||
continue
|
||||
rel_path = os.path.relpath(skill_path, skills_dir)
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user