tweek 0.3.1__py3-none-any.whl → 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tweek/__init__.py +2 -2
- tweek/audit.py +2 -2
- tweek/cli.py +78 -6605
- tweek/cli_config.py +643 -0
- tweek/cli_configure.py +413 -0
- tweek/cli_core.py +718 -0
- tweek/cli_dry_run.py +390 -0
- tweek/cli_helpers.py +316 -0
- tweek/cli_install.py +1666 -0
- tweek/cli_logs.py +301 -0
- tweek/cli_mcp.py +148 -0
- tweek/cli_memory.py +343 -0
- tweek/cli_plugins.py +748 -0
- tweek/cli_protect.py +564 -0
- tweek/cli_proxy.py +405 -0
- tweek/cli_security.py +236 -0
- tweek/cli_skills.py +289 -0
- tweek/cli_uninstall.py +551 -0
- tweek/cli_vault.py +313 -0
- tweek/config/allowed_dirs.yaml +16 -17
- tweek/config/families.yaml +4 -1
- tweek/config/manager.py +17 -0
- tweek/config/patterns.yaml +29 -5
- tweek/config/templates/config.yaml.template +212 -0
- tweek/config/templates/env.template +45 -0
- tweek/config/templates/overrides.yaml.template +121 -0
- tweek/config/templates/tweek.yaml.template +20 -0
- tweek/config/templates.py +136 -0
- tweek/config/tiers.yaml +5 -4
- tweek/diagnostics.py +112 -32
- tweek/hooks/overrides.py +4 -0
- tweek/hooks/post_tool_use.py +46 -1
- tweek/hooks/pre_tool_use.py +149 -49
- tweek/integrations/openclaw.py +84 -0
- tweek/licensing.py +1 -1
- tweek/mcp/__init__.py +7 -9
- tweek/mcp/clients/chatgpt.py +2 -2
- tweek/mcp/clients/claude_desktop.py +2 -2
- tweek/mcp/clients/gemini.py +2 -2
- tweek/mcp/proxy.py +165 -1
- tweek/memory/provenance.py +438 -0
- tweek/memory/queries.py +2 -0
- tweek/memory/safety.py +23 -4
- tweek/memory/schemas.py +1 -0
- tweek/memory/store.py +101 -71
- tweek/plugins/screening/heuristic_scorer.py +1 -1
- tweek/security/integrity.py +77 -0
- tweek/security/llm_reviewer.py +170 -74
- tweek/security/local_reviewer.py +44 -2
- tweek/security/model_registry.py +73 -7
- tweek/skill_template/overrides-reference.md +1 -1
- tweek/skills/context.py +221 -0
- tweek/skills/scanner.py +2 -2
- {tweek-0.3.1.dist-info → tweek-0.4.1.dist-info}/METADATA +8 -7
- {tweek-0.3.1.dist-info → tweek-0.4.1.dist-info}/RECORD +60 -38
- tweek/mcp/server.py +0 -320
- {tweek-0.3.1.dist-info → tweek-0.4.1.dist-info}/WHEEL +0 -0
- {tweek-0.3.1.dist-info → tweek-0.4.1.dist-info}/entry_points.txt +0 -0
- {tweek-0.3.1.dist-info → tweek-0.4.1.dist-info}/licenses/LICENSE +0 -0
- {tweek-0.3.1.dist-info → tweek-0.4.1.dist-info}/licenses/NOTICE +0 -0
- {tweek-0.3.1.dist-info → tweek-0.4.1.dist-info}/top_level.txt +0 -0
tweek/cli_vault.py
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Tweek CLI — Vault and License command groups.
|
|
4
|
+
|
|
5
|
+
Extracted from cli.py to keep the main CLI module manageable.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
from rich.table import Table
|
|
15
|
+
|
|
16
|
+
from tweek.cli_helpers import console, TWEEK_BANNER
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ============================================================
|
|
20
|
+
# VAULT COMMANDS
|
|
21
|
+
# ============================================================
|
|
22
|
+
|
|
23
|
+
@click.group()
|
|
24
|
+
def vault():
|
|
25
|
+
"""Manage credentials in secure storage (Keychain on macOS, Secret Service on Linux)."""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@vault.command("store",
|
|
30
|
+
epilog="""\b
|
|
31
|
+
Examples:
|
|
32
|
+
tweek vault store myskill API_KEY Prompt for value securely
|
|
33
|
+
tweek vault store myskill API_KEY sk-abc123 Store an API key (visible in history!)
|
|
34
|
+
"""
|
|
35
|
+
)
|
|
36
|
+
@click.argument("skill")
|
|
37
|
+
@click.argument("key")
|
|
38
|
+
@click.argument("value", required=False, default=None)
|
|
39
|
+
def vault_store(skill: str, key: str, value: Optional[str]):
|
|
40
|
+
"""Store a credential securely for a skill."""
|
|
41
|
+
from tweek.vault import get_vault, VAULT_AVAILABLE
|
|
42
|
+
from tweek.platform import get_capabilities
|
|
43
|
+
|
|
44
|
+
if not VAULT_AVAILABLE:
|
|
45
|
+
console.print("[red]\u2717[/red] Vault not available.")
|
|
46
|
+
console.print(" [white]Hint: Install keyring support: pip install keyring[/white]")
|
|
47
|
+
console.print(" [white]On macOS, keyring uses Keychain. On Linux, install gnome-keyring or kwallet.[/white]")
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
caps = get_capabilities()
|
|
51
|
+
|
|
52
|
+
# If value not provided as argument, prompt securely (avoids shell history exposure)
|
|
53
|
+
if value is None:
|
|
54
|
+
value = click.prompt(f"Enter value for {key}", hide_input=True)
|
|
55
|
+
if not value:
|
|
56
|
+
console.print("[red]No value provided.[/red]")
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
vault_instance = get_vault()
|
|
61
|
+
if vault_instance.store(skill, key, value):
|
|
62
|
+
console.print(f"[green]\u2713[/green] Stored {key} for skill '{skill}'")
|
|
63
|
+
console.print(f"[white]Backend: {caps.vault_backend}[/white]")
|
|
64
|
+
else:
|
|
65
|
+
console.print(f"[red]\u2717[/red] Failed to store credential")
|
|
66
|
+
console.print(" [white]Hint: Check your keyring backend is unlocked and accessible[/white]")
|
|
67
|
+
except Exception as e:
|
|
68
|
+
console.print(f"[red]\u2717[/red] Failed to store credential: {e}")
|
|
69
|
+
console.print(" [white]Hint: Check your keyring backend is unlocked and accessible[/white]")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@vault.command("get",
|
|
73
|
+
epilog="""\b
|
|
74
|
+
Examples:
|
|
75
|
+
tweek vault get myskill API_KEY Retrieve a stored credential
|
|
76
|
+
tweek vault get deploy AWS_SECRET Retrieve a deployment secret
|
|
77
|
+
"""
|
|
78
|
+
)
|
|
79
|
+
@click.argument("skill")
|
|
80
|
+
@click.argument("key")
|
|
81
|
+
def vault_get(skill: str, key: str):
|
|
82
|
+
"""Retrieve a credential from secure storage."""
|
|
83
|
+
from tweek.vault import get_vault, VAULT_AVAILABLE
|
|
84
|
+
|
|
85
|
+
if not VAULT_AVAILABLE:
|
|
86
|
+
console.print("[red]\u2717[/red] Vault not available.")
|
|
87
|
+
console.print(" [white]Hint: Install keyring support: pip install keyring[/white]")
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
vault_instance = get_vault()
|
|
91
|
+
value = vault_instance.get(skill, key)
|
|
92
|
+
|
|
93
|
+
if value is not None:
|
|
94
|
+
console.print(f"[yellow]GAH![/yellow] Credential access logged")
|
|
95
|
+
import sys as _sys
|
|
96
|
+
if not _sys.stdout.isatty():
|
|
97
|
+
console.print("[yellow]WARNING: stdout is piped — credential may be logged.[/yellow]", err=True)
|
|
98
|
+
console.print(value)
|
|
99
|
+
else:
|
|
100
|
+
console.print(f"[red]\u2717[/red] Credential not found: {key} for skill '{skill}'")
|
|
101
|
+
console.print(" [white]Hint: Store it with: tweek vault store {skill} {key} <value>[/white]".format(skill=skill, key=key))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@vault.command("migrate-env",
|
|
105
|
+
epilog="""\b
|
|
106
|
+
Examples:
|
|
107
|
+
tweek vault migrate-env --skill myapp Migrate .env to vault
|
|
108
|
+
tweek vault migrate-env --skill myapp --dry-run Preview without changes
|
|
109
|
+
tweek vault migrate-env --skill deploy --env-file .env.production Migrate specific file
|
|
110
|
+
"""
|
|
111
|
+
)
|
|
112
|
+
@click.option("--dry-run", is_flag=True, help="Show what would be migrated without doing it")
|
|
113
|
+
@click.option("--env-file", default=".env", help="Path to .env file")
|
|
114
|
+
@click.option("--skill", required=True, help="Skill name to store credentials under")
|
|
115
|
+
def vault_migrate_env(dry_run: bool, env_file: str, skill: str):
|
|
116
|
+
"""Migrate credentials from .env file to secure storage."""
|
|
117
|
+
from tweek.vault import get_vault, migrate_env_to_vault, VAULT_AVAILABLE
|
|
118
|
+
|
|
119
|
+
if not VAULT_AVAILABLE:
|
|
120
|
+
console.print("[red]\u2717[/red] Vault not available. Install keyring: pip install keyring")
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
env_path = Path(env_file)
|
|
124
|
+
console.print(f"[cyan]Scanning {env_path} for credentials...[/cyan]")
|
|
125
|
+
|
|
126
|
+
if dry_run:
|
|
127
|
+
console.print("\n[yellow]DRY RUN - No changes will be made[/yellow]\n")
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
vault_instance = get_vault()
|
|
131
|
+
results = migrate_env_to_vault(env_path, skill, vault_instance, dry_run=dry_run)
|
|
132
|
+
|
|
133
|
+
if results:
|
|
134
|
+
console.print(f"\n[green]{'Would migrate' if dry_run else 'Migrated'}:[/green]")
|
|
135
|
+
for key, success in results:
|
|
136
|
+
status = "\u2713" if success else "\u2717"
|
|
137
|
+
console.print(f" {status} {key}")
|
|
138
|
+
successful = sum(1 for _, s in results if s)
|
|
139
|
+
total = len(results)
|
|
140
|
+
console.print(f"\n[green]\u2713[/green] {'Would migrate' if dry_run else 'Migrated'} {successful} credentials to skill '{skill}'")
|
|
141
|
+
|
|
142
|
+
if not dry_run and successful == total and env_path.exists():
|
|
143
|
+
console.print()
|
|
144
|
+
if click.confirm(f"Remove {env_path}? (credentials are now in the vault)"):
|
|
145
|
+
env_path.unlink()
|
|
146
|
+
console.print(f"[green]\u2713[/green] Removed {env_path}")
|
|
147
|
+
else:
|
|
148
|
+
console.print(f"[yellow]\u26a0[/yellow] {env_path} still contains plaintext credentials")
|
|
149
|
+
elif not dry_run and successful < total:
|
|
150
|
+
failed = total - successful
|
|
151
|
+
console.print(f"[yellow]\u26a0[/yellow] {failed} credential(s) failed to migrate \u2014 keeping {env_path}")
|
|
152
|
+
else:
|
|
153
|
+
console.print("[white]No credentials found to migrate[/white]")
|
|
154
|
+
|
|
155
|
+
except Exception as e:
|
|
156
|
+
console.print(f"[red]\u2717[/red] Migration failed: {e}")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@vault.command("delete",
|
|
160
|
+
epilog="""\b
|
|
161
|
+
Examples:
|
|
162
|
+
tweek vault delete myskill API_KEY Delete a stored credential
|
|
163
|
+
tweek vault delete deploy AWS_SECRET Remove a deployment secret
|
|
164
|
+
"""
|
|
165
|
+
)
|
|
166
|
+
@click.argument("skill")
|
|
167
|
+
@click.argument("key")
|
|
168
|
+
def vault_delete(skill: str, key: str):
|
|
169
|
+
"""Delete a credential from secure storage."""
|
|
170
|
+
from tweek.vault import get_vault, VAULT_AVAILABLE
|
|
171
|
+
|
|
172
|
+
if not VAULT_AVAILABLE:
|
|
173
|
+
console.print("[red]\u2717[/red] Vault not available. Install keyring: pip install keyring")
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
vault_instance = get_vault()
|
|
177
|
+
deleted = vault_instance.delete(skill, key)
|
|
178
|
+
|
|
179
|
+
if deleted:
|
|
180
|
+
console.print(f"[green]\u2713[/green] Deleted {key} from skill '{skill}'")
|
|
181
|
+
else:
|
|
182
|
+
console.print(f"[yellow]![/yellow] Credential not found: {key} for skill '{skill}'")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# ============================================================
|
|
186
|
+
# LICENSE COMMANDS [experimental]
|
|
187
|
+
# ============================================================
|
|
188
|
+
|
|
189
|
+
@click.group("license")
|
|
190
|
+
def license_group():
|
|
191
|
+
"""Manage Tweek license and features. [experimental]"""
|
|
192
|
+
pass
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@license_group.command("status",
|
|
196
|
+
epilog="""\b
|
|
197
|
+
Examples:
|
|
198
|
+
tweek license status Show license tier and features
|
|
199
|
+
"""
|
|
200
|
+
)
|
|
201
|
+
def license_status():
|
|
202
|
+
"""Show current license status and available features. [experimental]"""
|
|
203
|
+
console.print("[yellow]Note: License management is experimental. Pro/Enterprise tiers coming soon.[/yellow]")
|
|
204
|
+
|
|
205
|
+
from tweek.licensing import get_license, TIER_FEATURES, Tier
|
|
206
|
+
|
|
207
|
+
console.print(TWEEK_BANNER, style="cyan")
|
|
208
|
+
|
|
209
|
+
lic = get_license()
|
|
210
|
+
info = lic.info
|
|
211
|
+
|
|
212
|
+
# License info
|
|
213
|
+
tier_colors = {
|
|
214
|
+
Tier.FREE: "white",
|
|
215
|
+
Tier.PRO: "cyan",
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
tier_color = tier_colors.get(lic.tier, "white")
|
|
219
|
+
console.print(f"[bold]License Tier:[/bold] [{tier_color}]{lic.tier.value.upper()}[/{tier_color}]")
|
|
220
|
+
|
|
221
|
+
if info:
|
|
222
|
+
console.print(f"[white]Licensed to: {info.email}[/white]")
|
|
223
|
+
if info.expires_at:
|
|
224
|
+
from datetime import datetime
|
|
225
|
+
exp_date = datetime.fromtimestamp(info.expires_at).strftime("%Y-%m-%d")
|
|
226
|
+
if info.is_expired:
|
|
227
|
+
console.print(f"[red]Expired: {exp_date}[/red]")
|
|
228
|
+
else:
|
|
229
|
+
console.print(f"[white]Expires: {exp_date}[/white]")
|
|
230
|
+
else:
|
|
231
|
+
console.print("[white]Expires: Never[/white]")
|
|
232
|
+
console.print()
|
|
233
|
+
|
|
234
|
+
# Features table
|
|
235
|
+
table = Table(title="Feature Availability")
|
|
236
|
+
table.add_column("Feature", style="cyan")
|
|
237
|
+
table.add_column("Status")
|
|
238
|
+
table.add_column("Tier Required")
|
|
239
|
+
|
|
240
|
+
# Collect all features and their required tiers
|
|
241
|
+
feature_tiers = {}
|
|
242
|
+
for tier in [Tier.FREE, Tier.PRO]:
|
|
243
|
+
for feature in TIER_FEATURES.get(tier, []):
|
|
244
|
+
feature_tiers[feature] = tier
|
|
245
|
+
|
|
246
|
+
for feature, required_tier in feature_tiers.items():
|
|
247
|
+
has_it = lic.has_feature(feature)
|
|
248
|
+
status = "[green]\u2713[/green]" if has_it else "[white]\u25cb[/white]"
|
|
249
|
+
tier_display = required_tier.value.upper()
|
|
250
|
+
if required_tier == Tier.PRO:
|
|
251
|
+
tier_display = f"[cyan]{tier_display}[/cyan]"
|
|
252
|
+
|
|
253
|
+
table.add_row(feature, status, tier_display)
|
|
254
|
+
|
|
255
|
+
console.print(table)
|
|
256
|
+
|
|
257
|
+
if lic.tier == Tier.FREE:
|
|
258
|
+
console.print()
|
|
259
|
+
console.print("[green]All security features are included free and open source.[/green]")
|
|
260
|
+
console.print("[white]Pro (teams) and Enterprise (compliance) coming soon: gettweek.com[/white]")
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
@license_group.command("activate",
|
|
264
|
+
epilog="""\b
|
|
265
|
+
Examples:
|
|
266
|
+
tweek license activate YOUR_KEY Activate a license key (Pro/Enterprise coming soon)
|
|
267
|
+
"""
|
|
268
|
+
)
|
|
269
|
+
@click.argument("license_key")
|
|
270
|
+
def license_activate(license_key: str):
|
|
271
|
+
"""Activate a license key. [experimental]"""
|
|
272
|
+
console.print("[yellow]Note: License management is experimental. Pro/Enterprise tiers coming soon.[/yellow]")
|
|
273
|
+
|
|
274
|
+
from tweek.licensing import get_license
|
|
275
|
+
|
|
276
|
+
lic = get_license()
|
|
277
|
+
success, message = lic.activate(license_key)
|
|
278
|
+
|
|
279
|
+
if success:
|
|
280
|
+
console.print(f"[green]\u2713[/green] {message}")
|
|
281
|
+
console.print()
|
|
282
|
+
console.print("[white]Run 'tweek license status' to see available features[/white]")
|
|
283
|
+
else:
|
|
284
|
+
console.print(f"[red]\u2717[/red] {message}")
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@license_group.command("deactivate",
|
|
288
|
+
epilog="""\b
|
|
289
|
+
Examples:
|
|
290
|
+
tweek license deactivate Deactivate license (with prompt)
|
|
291
|
+
tweek license deactivate --confirm Deactivate without confirmation
|
|
292
|
+
"""
|
|
293
|
+
)
|
|
294
|
+
@click.option("--confirm", is_flag=True, help="Skip confirmation prompt")
|
|
295
|
+
def license_deactivate(confirm: bool):
|
|
296
|
+
"""Remove current license and revert to FREE tier. [experimental]"""
|
|
297
|
+
console.print("[yellow]Note: License management is experimental. Pro/Enterprise tiers coming soon.[/yellow]")
|
|
298
|
+
|
|
299
|
+
from tweek.licensing import get_license
|
|
300
|
+
|
|
301
|
+
if not confirm:
|
|
302
|
+
console.print("[yellow]Deactivate license and revert to FREE tier?[/yellow] ", end="")
|
|
303
|
+
if not click.confirm(""):
|
|
304
|
+
console.print("[white]Cancelled[/white]")
|
|
305
|
+
return
|
|
306
|
+
|
|
307
|
+
lic = get_license()
|
|
308
|
+
success, message = lic.deactivate()
|
|
309
|
+
|
|
310
|
+
if success:
|
|
311
|
+
console.print(f"[green]\u2713[/green] {message}")
|
|
312
|
+
else:
|
|
313
|
+
console.print(f"[red]\u2717[/red] {message}")
|
tweek/config/allowed_dirs.yaml
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
# Tweek Directory Safety Configuration
|
|
1
|
+
# Tweek Directory Safety Configuration (bundled default)
|
|
2
2
|
#
|
|
3
|
-
# This file
|
|
4
|
-
#
|
|
3
|
+
# This file ships with the Tweek package and provides the production default.
|
|
4
|
+
# To override, create ~/.tweek/allowed_dirs.yaml (user-level config takes
|
|
5
|
+
# full precedence when it exists).
|
|
5
6
|
#
|
|
6
7
|
# Options:
|
|
7
|
-
# global_enabled: true - Activate Tweek everywhere (
|
|
8
|
-
# allowed_directories: -
|
|
8
|
+
# global_enabled: true - Activate Tweek everywhere (default for end users)
|
|
9
|
+
# allowed_directories: - Restrict Tweek to specific directories only
|
|
9
10
|
#
|
|
10
|
-
#
|
|
11
|
+
# Lookup order:
|
|
12
|
+
# 1. ~/.tweek/allowed_dirs.yaml (user override — not in repo)
|
|
13
|
+
# 2. This file (bundled default)
|
|
11
14
|
|
|
12
|
-
#
|
|
13
|
-
global_enabled:
|
|
15
|
+
# Production default: Tweek is active everywhere after install
|
|
16
|
+
global_enabled: true
|
|
14
17
|
|
|
15
|
-
#
|
|
16
|
-
# Tweek
|
|
17
|
-
allowed_directories:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
# Add more directories as needed:
|
|
22
|
-
# - ~/projects/sensitive-project
|
|
23
|
-
# - /path/to/another/project
|
|
18
|
+
# When global_enabled is false, only activate in these directories.
|
|
19
|
+
# Tweek also activates in subdirectories of listed paths.
|
|
20
|
+
# allowed_directories:
|
|
21
|
+
# - ~/projects/my-project
|
|
22
|
+
# - /path/to/another/project
|
tweek/config/families.yaml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Tweek Pattern Family Definitions v1
|
|
2
|
-
# Groups the
|
|
2
|
+
# Groups the 262 attack patterns by attack class for:
|
|
3
3
|
# 1. Heuristic scoring (near-miss detection via semantic signals)
|
|
4
4
|
# 2. Pattern management (enable/disable by family)
|
|
5
5
|
# 3. Reporting (family-level risk summaries)
|
|
@@ -257,6 +257,9 @@ families:
|
|
|
257
257
|
- 257 # self_describe_purpose
|
|
258
258
|
- 258 # self_describe_protection
|
|
259
259
|
- 259 # self_describe_instructions
|
|
260
|
+
- 260 # summarize_instructions_extraction
|
|
261
|
+
- 261 # special_instructions_probe
|
|
262
|
+
- 262 # system_prompt_reference_broad
|
|
260
263
|
heuristic_signals:
|
|
261
264
|
instruction_keywords:
|
|
262
265
|
- "ignore"
|
tweek/config/manager.py
CHANGED
|
@@ -152,6 +152,23 @@ class ConfigManager:
|
|
|
152
152
|
},
|
|
153
153
|
"default_tier": "default",
|
|
154
154
|
},
|
|
155
|
+
"balanced": {
|
|
156
|
+
# Same tool tiers as cautious, but the preset name signals
|
|
157
|
+
# provenance-aware enforcement: clean sessions get relaxed
|
|
158
|
+
# thresholds (fewer false positives), tainted sessions get
|
|
159
|
+
# escalated scrutiny. See tweek.memory.provenance.
|
|
160
|
+
"tools": {
|
|
161
|
+
"Read": "safe",
|
|
162
|
+
"Glob": "safe",
|
|
163
|
+
"Grep": "safe",
|
|
164
|
+
"Edit": "default",
|
|
165
|
+
"Write": "default",
|
|
166
|
+
"WebFetch": "risky",
|
|
167
|
+
"WebSearch": "risky",
|
|
168
|
+
"Bash": "dangerous",
|
|
169
|
+
},
|
|
170
|
+
"default_tier": "default",
|
|
171
|
+
},
|
|
155
172
|
"trusted": {
|
|
156
173
|
"tools": {
|
|
157
174
|
"Read": "safe",
|
tweek/config/patterns.yaml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Tweek Attack Pattern Definitions v3
|
|
2
|
-
# All
|
|
2
|
+
# All 262 patterns included FREE
|
|
3
3
|
#
|
|
4
4
|
# Update via: tweek update (pulls from github.com/gettweek/tweek)
|
|
5
5
|
#
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
# PRO tier adds: LLM review, session analysis, rate limiting
|
|
26
26
|
|
|
27
27
|
version: 5
|
|
28
|
-
pattern_count:
|
|
28
|
+
pattern_count: 262
|
|
29
29
|
|
|
30
30
|
patterns:
|
|
31
31
|
# ============================================================================
|
|
@@ -711,7 +711,7 @@ patterns:
|
|
|
711
711
|
- id: 83
|
|
712
712
|
name: document_metadata_injection
|
|
713
713
|
description: "Hidden instructions in document metadata"
|
|
714
|
-
regex: '(author|title|subject|keywords|description)\s*[=:]\s
|
|
714
|
+
regex: '(author|title|subject|keywords|description)\s*[=:]\s*[^\n]{0,80}(execute|run|ignore|override|bypass)'
|
|
715
715
|
severity: high
|
|
716
716
|
confidence: heuristic
|
|
717
717
|
family: prompt_injection
|
|
@@ -736,7 +736,7 @@ patterns:
|
|
|
736
736
|
- id: 86
|
|
737
737
|
name: log_to_leak
|
|
738
738
|
description: "Log-To-Leak covert channel attack"
|
|
739
|
-
regex: '(log|write|append|print)
|
|
739
|
+
regex: '(log|write|append|print)[^\n]{0,60}(secret|credential|token|key|password)[^\n]{0,60}(file|output|stream)'
|
|
740
740
|
severity: high
|
|
741
741
|
confidence: heuristic
|
|
742
742
|
family: covert_channels
|
|
@@ -1514,7 +1514,7 @@ patterns:
|
|
|
1514
1514
|
- id: 174
|
|
1515
1515
|
name: leetspeak_instruction_bypass
|
|
1516
1516
|
description: "Heavy character substitution of security-critical words"
|
|
1517
|
-
regex: '([sS5$][yY][sS5$][tT7][eE3][mM]\s*[pP][rR][0oO][mM][pP][tT7]|[1iI][nN][sS5$][tT7][rR][uU][cC][tT7]|[cC][0oO][nN][fF][1iI][gG]|[eE3][xX][eE3][cC][uU][tT7][eE3])'
|
|
1517
|
+
regex: '((?=[a-zA-Z01357$]*[01357$])[sS5$][yY][sS5$][tT7][eE3][mM]\s*[pP][rR][0oO][mM][pP][tT7]|(?=[a-zA-Z01357$]*[01357$])[1iI][nN][sS5$][tT7][rR][uU][cC][tT7]|(?=[a-zA-Z01357$]*[01357$])[cC][0oO][nN][fF][1iI][gG]|(?=[a-zA-Z01357$]*[01357$])[eE3][xX][eE3][cC][uU][tT7][eE3])'
|
|
1518
1518
|
severity: medium
|
|
1519
1519
|
confidence: contextual
|
|
1520
1520
|
family: prompt_injection
|
|
@@ -2251,3 +2251,27 @@ patterns:
|
|
|
2251
2251
|
severity: low
|
|
2252
2252
|
confidence: contextual
|
|
2253
2253
|
family: prompt_injection
|
|
2254
|
+
|
|
2255
|
+
- id: 260
|
|
2256
|
+
name: summarize_instructions_extraction
|
|
2257
|
+
description: "Summarization-based system prompt extraction"
|
|
2258
|
+
regex: '(summarize|recap|outline|condense|describe|paraphrase)\s+.{0,30}(instructions|prompt|rules|guidelines|directives|configuration)\s+.{0,20}(given|told|provided|have|follow|received|programmed)'
|
|
2259
|
+
severity: medium
|
|
2260
|
+
confidence: contextual
|
|
2261
|
+
family: prompt_injection
|
|
2262
|
+
|
|
2263
|
+
- id: 261
|
|
2264
|
+
name: special_instructions_probe
|
|
2265
|
+
description: "Probing for special instructions or unique capabilities"
|
|
2266
|
+
regex: 'what\s+.{0,20}(instructions|capabilities|directives|rules)\s+.{0,30}(do\s+you\s+have|make\s+you|were\s+you|that\s+you)'
|
|
2267
|
+
severity: medium
|
|
2268
|
+
confidence: contextual
|
|
2269
|
+
family: prompt_injection
|
|
2270
|
+
|
|
2271
|
+
- id: 262
|
|
2272
|
+
name: system_prompt_reference_broad
|
|
2273
|
+
description: "Broad references to system prompt in extraction context"
|
|
2274
|
+
regex: '((your|my|the|its)\s+.{0,20}system[\s_]+prompt|system[\s_]+prompt\s*(say|would\s+say|output|reveal|share|display)|output\s+(system[\s_]+)?(prompt|configuration)\b|(instructions|directives)\s+(you|I|they|it)\s+(received|were\s+given|have\b|follow|got)|what.s\s+your\s+system[\s_]+prompt)'
|
|
2275
|
+
severity: medium
|
|
2276
|
+
confidence: contextual
|
|
2277
|
+
family: prompt_injection
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# Tweek User Configuration (~/.tweek/config.yaml)
|
|
3
|
+
# ============================================================================
|
|
4
|
+
#
|
|
5
|
+
# This file overrides Tweek's bundled defaults. Only uncomment what you
|
|
6
|
+
# want to change -- everything else uses safe production defaults.
|
|
7
|
+
#
|
|
8
|
+
# Config hierarchy (highest priority wins):
|
|
9
|
+
# 1. Project: .tweek/config.yaml (per-project overrides)
|
|
10
|
+
# 2. User: ~/.tweek/config.yaml (this file)
|
|
11
|
+
# 3. Bundled: tweek/config/tiers.yaml (production defaults)
|
|
12
|
+
#
|
|
13
|
+
# Commands:
|
|
14
|
+
# tweek config edit Open this file in your editor
|
|
15
|
+
# tweek config show-defaults View all bundled defaults
|
|
16
|
+
# tweek config validate Check for errors
|
|
17
|
+
# tweek config list Show current tool/skill tiers
|
|
18
|
+
# tweek config preset <name> Apply a preset (paranoid|cautious|trusted)
|
|
19
|
+
#
|
|
20
|
+
# Full docs: https://github.com/gettweek/tweek/blob/main/docs/CONFIGURATION.md
|
|
21
|
+
# ============================================================================
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
# LLM Review Provider (Layer 3 — semantic analysis)
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
# Analyzes suspicious commands/content using an LLM for deeper screening.
|
|
28
|
+
# Works alongside pattern matching (Layer 2) — not a replacement.
|
|
29
|
+
#
|
|
30
|
+
# Auto-detect order: Local ONNX model > Google > OpenAI > xAI > Anthropic
|
|
31
|
+
#
|
|
32
|
+
# IMPORTANT: API keys go in ~/.tweek/.env (not here). Run:
|
|
33
|
+
# tweek config edit env
|
|
34
|
+
#
|
|
35
|
+
# BILLING NOTE: Anthropic API keys are billed per-token, SEPARATELY from
|
|
36
|
+
# Claude Pro/Max subscriptions. Google Gemini has a free tier — recommended.
|
|
37
|
+
#
|
|
38
|
+
# llm_review:
|
|
39
|
+
# enabled: true
|
|
40
|
+
# provider: auto # auto | google | openai | xai | anthropic | local
|
|
41
|
+
# model: auto # auto = provider default. Defaults per provider:
|
|
42
|
+
# # google: gemini-2.0-flash
|
|
43
|
+
# # openai: gpt-4o-mini
|
|
44
|
+
# # xai: grok-2
|
|
45
|
+
# # anthropic: claude-3-5-haiku-latest
|
|
46
|
+
# # local: deberta-v3-injection
|
|
47
|
+
# timeout_seconds: 5.0 # Max wait for LLM response
|
|
48
|
+
# base_url: null # For OpenAI-compatible endpoints (Ollama, LM Studio, etc.)
|
|
49
|
+
# api_key_env: null # Override env var name (default: provider-specific)
|
|
50
|
+
#
|
|
51
|
+
# # Local LLM server settings (Ollama, LM Studio)
|
|
52
|
+
# local:
|
|
53
|
+
# enabled: true
|
|
54
|
+
# probe_timeout: 0.5 # Max probe time per server (seconds)
|
|
55
|
+
# timeout_seconds: 3.0 # Per-request timeout (local should be fast)
|
|
56
|
+
# ollama_host: null # Override (default: OLLAMA_HOST env or localhost:11434)
|
|
57
|
+
# lm_studio_host: null # Override (default: localhost:1234)
|
|
58
|
+
# preferred_models: [] # User override for model ranking
|
|
59
|
+
# validate_on_first_use: true
|
|
60
|
+
# min_validation_score: 0.6 # Must pass 3/5 validation commands
|
|
61
|
+
#
|
|
62
|
+
# # Fallback chain (try local first, then cloud)
|
|
63
|
+
# fallback:
|
|
64
|
+
# enabled: true
|
|
65
|
+
# order: [local, cloud]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
# Local ONNX Model (Layer 3 — on-device classifier)
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
# On-device prompt injection classifier. No API key needed.
|
|
72
|
+
# Install: pip install tweek[local-models] && tweek model download
|
|
73
|
+
# When installed, runs before cloud LLM and escalates uncertain results.
|
|
74
|
+
#
|
|
75
|
+
# local_model:
|
|
76
|
+
# enabled: true
|
|
77
|
+
# model: auto # auto = default model, or explicit name
|
|
78
|
+
# escalate_to_llm: true # Escalate uncertain results to cloud LLM
|
|
79
|
+
# escalate_min_confidence: 0.1 # Below this = safe, skip escalation
|
|
80
|
+
# escalate_max_confidence: 0.9 # Above this = use local result, skip escalation
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# ---------------------------------------------------------------------------
|
|
84
|
+
# Rate Limiting (Layer 1 — burst detection)
|
|
85
|
+
# ---------------------------------------------------------------------------
|
|
86
|
+
# Detects rapid-fire tool calls that may indicate automated attacks.
|
|
87
|
+
#
|
|
88
|
+
# rate_limiting:
|
|
89
|
+
# enabled: true
|
|
90
|
+
# burst_window_seconds: 5 # Time window for burst detection
|
|
91
|
+
# burst_threshold: 15 # Max calls in burst window
|
|
92
|
+
# max_per_minute: 60 # Overall rate limit
|
|
93
|
+
# max_dangerous_per_minute: 10 # Limit for dangerous-tier tools
|
|
94
|
+
# max_same_command_per_minute: 5 # Repeated identical commands
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
# Session Analysis (Layer 4 — cross-turn anomaly detection)
|
|
99
|
+
# ---------------------------------------------------------------------------
|
|
100
|
+
# Tracks patterns across multiple tool calls to detect multi-step attacks.
|
|
101
|
+
#
|
|
102
|
+
# session_analysis:
|
|
103
|
+
# enabled: true
|
|
104
|
+
# lookback_minutes: 30 # How far back to analyze
|
|
105
|
+
# alert_on_risk_score: 0.5 # Threshold for session risk alerts
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# ---------------------------------------------------------------------------
|
|
109
|
+
# Heuristic Scorer (Layer 2.5 — signal-based escalation)
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
# Bridges pattern matching and LLM review. When no regex matches but
|
|
112
|
+
# content looks suspicious, scores it and may escalate to LLM.
|
|
113
|
+
#
|
|
114
|
+
# heuristic_scorer:
|
|
115
|
+
# enabled: true
|
|
116
|
+
# threshold: 0.4 # Score [0.0-1.0] to trigger LLM escalation
|
|
117
|
+
# log_all_scores: false # Log below-threshold scores (for tuning)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# ---------------------------------------------------------------------------
|
|
121
|
+
# Tool Tier Overrides
|
|
122
|
+
# ---------------------------------------------------------------------------
|
|
123
|
+
# Override the default security tier for specific tools.
|
|
124
|
+
# Tiers: safe | default | risky | dangerous
|
|
125
|
+
#
|
|
126
|
+
# Bundled defaults:
|
|
127
|
+
# Read: default (read-only but needs path screening)
|
|
128
|
+
# Glob: safe (file pattern matching)
|
|
129
|
+
# Grep: safe (search file contents)
|
|
130
|
+
# Edit: default (modify existing files)
|
|
131
|
+
# Write: risky (create/overwrite files)
|
|
132
|
+
# NotebookEdit: default (edit Jupyter notebooks)
|
|
133
|
+
# WebFetch: risky (fetch content from URLs)
|
|
134
|
+
# WebSearch: risky (search the web)
|
|
135
|
+
# Bash: dangerous (execute shell commands)
|
|
136
|
+
# Task: default (spawn subagent tasks)
|
|
137
|
+
#
|
|
138
|
+
# tools:
|
|
139
|
+
# Read: default
|
|
140
|
+
# Bash: dangerous
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# ---------------------------------------------------------------------------
|
|
144
|
+
# Skill Tier Overrides
|
|
145
|
+
# ---------------------------------------------------------------------------
|
|
146
|
+
# Override the default tier for Claude Code skills.
|
|
147
|
+
#
|
|
148
|
+
# Bundled defaults:
|
|
149
|
+
# review-pr: safe (read-only PR review)
|
|
150
|
+
# explore: safe (read-only codebase exploration)
|
|
151
|
+
# commit: default (git commit operations)
|
|
152
|
+
# frontend-design: risky (generate frontend code)
|
|
153
|
+
# dev-browser: risky (browser automation)
|
|
154
|
+
# deploy: dangerous (deployment operations)
|
|
155
|
+
#
|
|
156
|
+
# skills:
|
|
157
|
+
# commit: default
|
|
158
|
+
# deploy: dangerous
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# ---------------------------------------------------------------------------
|
|
162
|
+
# Non-English Language Handling
|
|
163
|
+
# ---------------------------------------------------------------------------
|
|
164
|
+
# English-only regex patterns can miss injection in other languages.
|
|
165
|
+
# Options:
|
|
166
|
+
# escalate - Auto-escalate to LLM review tier (default, recommended)
|
|
167
|
+
# translate - Translate to English before pattern matching (requires API key)
|
|
168
|
+
# both - Escalate AND translate (maximum coverage)
|
|
169
|
+
# none - No special handling (English-only patterns may miss attacks)
|
|
170
|
+
#
|
|
171
|
+
# non_english_handling: escalate
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
# ---------------------------------------------------------------------------
|
|
175
|
+
# Content-Based Escalations
|
|
176
|
+
# ---------------------------------------------------------------------------
|
|
177
|
+
# Regex patterns that bump a tool's tier based on content.
|
|
178
|
+
# These ADD to the bundled escalations (they don't replace them).
|
|
179
|
+
#
|
|
180
|
+
# escalations:
|
|
181
|
+
# - pattern: '\b(prod|production)\b'
|
|
182
|
+
# description: "Production environment reference"
|
|
183
|
+
# escalate_to: risky
|
|
184
|
+
#
|
|
185
|
+
# - pattern: 'rm\s+(-rf|-fr|--recursive)'
|
|
186
|
+
# description: "Recursive deletion"
|
|
187
|
+
# escalate_to: dangerous
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# ---------------------------------------------------------------------------
|
|
191
|
+
# Path Boundary Escalation
|
|
192
|
+
# ---------------------------------------------------------------------------
|
|
193
|
+
# Escalates tier when tools access files outside the project directory.
|
|
194
|
+
#
|
|
195
|
+
# path_boundary:
|
|
196
|
+
# enabled: true
|
|
197
|
+
# default_escalate_to: risky # Generic out-of-project access
|
|
198
|
+
# sensitive_directories:
|
|
199
|
+
# - pattern: ".ssh"
|
|
200
|
+
# escalate_to: dangerous
|
|
201
|
+
# description: "SSH directory access"
|
|
202
|
+
# - pattern: ".aws"
|
|
203
|
+
# escalate_to: dangerous
|
|
204
|
+
# description: "AWS credentials directory"
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# ---------------------------------------------------------------------------
|
|
208
|
+
# Default Tier
|
|
209
|
+
# ---------------------------------------------------------------------------
|
|
210
|
+
# Fallback tier for tools/skills not explicitly classified.
|
|
211
|
+
#
|
|
212
|
+
# default_tier: default
|