Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions python/prompd/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ def _run_impl(
actual_content = actual_file.read_text(encoding="utf-8")
if not actual_content.startswith("---"):
import re

stem = actual_file.stem
kebab_name = re.sub(r"[^a-z0-9]+", "-", stem.lower()).strip("-") or "prompt"
frontmatter = f"---\nname: {kebab_name}\nversion: 1.0.0\n---\n\n"
Expand Down
3 changes: 1 addition & 2 deletions python/prompd/section_override_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,7 @@ def _load_file_with_encoding(self, file_path: Path) -> str:
resolved_path = file_path.resolve(strict=True)
except (OSError, ValueError) as exc:
raise ValidationError(
"Unable to resolve override file path. "
"Please verify the file exists and is accessible."
"Unable to resolve override file path. " "Please verify the file exists and is accessible."
) from exc

# Security Control 2: Verify resolved path is within the allowed base directory
Expand Down
82 changes: 60 additions & 22 deletions python/prompd/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ def validate_version_string(version: str) -> str:

# --- Secrets Detection ---


@dataclass
class SecretMatch:
"""Represents a detected secret in content or a file."""
Expand All @@ -198,46 +199,76 @@ class SecretMatch:
"OpenAI API Key": re.compile(r"sk-[a-zA-Z0-9]{20,}"),
"Anthropic API Key": re.compile(r"sk-ant-[a-zA-Z0-9_-]{20,}"),
"AWS Access Key": re.compile(r"AKIA[0-9A-Z]{16}"),
"AWS Secret Key": re.compile(
r"(?i)aws[_-]?secret[_-]?access[_-]?key[=:\s]+['\"]?[a-zA-Z0-9/+=]{40}['\"]?"
),
"AWS Secret Key": re.compile(r"(?i)aws[_-]?secret[_-]?access[_-]?key[=:\s]+['\"]?[a-zA-Z0-9/+=]{40}['\"]?"),
"GitHub Token": re.compile(r"gh[ps]_[a-zA-Z0-9]{36}"),
"GitHub Fine-Grained": re.compile(r"github_pat_[a-zA-Z0-9_]{22,}"),
"Prompd Registry Token": re.compile(r"prompd_[a-zA-Z0-9]{32,}"),
"Private Key": re.compile(
r"-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----"
),
"Private Key": re.compile(r"-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----"),
"Generic API Key": re.compile(
r"(?i)(?:api[_-]?key|apikey|api_secret|apisecret)[_-]?[=:]\s*['\"]?([a-zA-Z0-9_\-]{20,})['\"]?"
),
"Generic Secret": re.compile(
r"(?i)(?:secret|password|passwd|token)[_-]?[=:]\s*['\"]?([a-zA-Z0-9_\-!@#$%^&*]{16,})['\"]?"
),
"Bearer Token": re.compile(r"[Bb]earer\s+[a-zA-Z0-9_\-.]{32,256}"),
"JWT Token": re.compile(
r"eyJ[a-zA-Z0-9_-]{10,}\.eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}"
),
"URL-Embedded Credentials": re.compile(
r"https?://[^:\s]+:[^@\s]+@[a-zA-Z0-9.-]+"
),
"JWT Token": re.compile(r"eyJ[a-zA-Z0-9_-]{10,}\.eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}"),
"URL-Embedded Credentials": re.compile(r"https?://[^:\s]+:[^@\s]+@[a-zA-Z0-9.-]+"),
"Slack Token": re.compile(r"xox[bpors]-[a-zA-Z0-9-]{10,}"),
"Google API Key": re.compile(r"AIza[0-9A-Za-z_-]{35}"),
"Stripe Key": re.compile(r"(?:sk|pk)_(?:test|live)_[a-zA-Z0-9]{20,}"),
}

# File extensions considered as text files for secrets scanning
_TEXT_EXTENSIONS = {
".prmd", ".pdproj", ".yaml", ".yml", ".json", ".md", ".txt",
".py", ".go", ".js", ".ts", ".tsx", ".jsx", ".sh", ".bat",
".env", ".html", ".css", ".xml", ".toml", ".ini", ".conf",
".c", ".cpp", ".h", ".hpp", ".java", ".cs", ".rb", ".php",
".sql", ".r", ".lua", ".pl", ".swift", ".kt", ".rs",
".prmd",
".pdproj",
".yaml",
".yml",
".json",
".md",
".txt",
".py",
".go",
".js",
".ts",
".tsx",
".jsx",
".sh",
".bat",
".env",
".html",
".css",
".xml",
".toml",
".ini",
".conf",
".c",
".cpp",
".h",
".hpp",
".java",
".cs",
".rb",
".php",
".sql",
".r",
".lua",
".pl",
".swift",
".kt",
".rs",
}

# Files that commonly contain secrets and should be excluded from packaging
_SECRET_FILE_NAMES = {
".env", ".env.local", ".env.production", ".env.development",
".env.test", "credentials.json", "secrets.yaml", "secrets.yml",
".env",
".env.local",
".env.production",
".env.development",
".env.test",
"credentials.json",
"secrets.yaml",
"secrets.yml",
"private.key",
}

Expand All @@ -261,8 +292,15 @@ def _is_text_file(file_path: str) -> bool:

base_name = os.path.basename(file_path).lower()
no_ext_patterns = [
"readme", "license", "makefile", "dockerfile", "vagrantfile",
".env", ".gitignore", ".dockerignore", ".npmignore",
"readme",
"license",
"makefile",
"dockerfile",
"vagrantfile",
".env",
".gitignore",
".dockerignore",
".npmignore",
]
for pattern in no_ext_patterns:
if base_name == pattern or base_name.startswith(pattern + "."):
Expand Down Expand Up @@ -314,7 +352,7 @@ def scan_file_for_secrets(file_path: str) -> List[SecretMatch]:
try:
with open(file_path, encoding="utf-8", errors="replace") as f:
content = f.read()
except (OSError, IOError):
except OSError:
return []

matches = detect_secrets_in_content(content)
Expand Down
4 changes: 1 addition & 3 deletions typescript/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@prompd/cli",
"version": "0.5.0-beta.3",
"version": "0.5.0-beta.10",
"description": "Node.js CLI for structured prompt definitions with universal LLM provider support. Also usable as a library in TypeScript/React apps.",
"main": "dist/lib/index.js",
"types": "dist/lib/index.d.ts",
Expand Down Expand Up @@ -68,7 +68,6 @@
"adm-zip": "^0.5.16",
"archiver": "^6.0.1",
"axios": "^1.6.2",
"bcrypt": "^6.0.0",
"chalk": "^4.1.2",
"commander": "^11.1.0",
"compression": "^1.7.4",
Expand All @@ -93,7 +92,6 @@
"devDependencies": {
"@types/adm-zip": "^0.5.7",
"@types/archiver": "^6.0.2",
"@types/bcrypt": "^5.0.2",
"@types/compression": "^1.7.5",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
Expand Down
5 changes: 5 additions & 0 deletions typescript/prompd.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"dependencies": {
"@prompd/core": "0.0.2"
}
}
Loading
Loading