@caddis/cli 0.4.58 → 0.4.60

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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "caddis",
3
- "version": "1.3.144",
3
+ "version": "1.3.146",
4
4
  "description": "caddis — agent-agnostic dev harness as an Antigravity (agy) plugin: TDD skills + commands, single-sourced with the Claude Code plugin."
5
5
  }
@@ -103,6 +103,9 @@ except ModuleNotFoundError as exc:
103
103
  _NO_WINDOW = 0x08000000 if os.name == "nt" else 0
104
104
 
105
105
  SCRIPTS_DIR = Path(__file__).resolve().parent
106
+ # This module object, taken while it loads. `render` hands it to the page builder. A lookup by name at call time
107
+ # (`sys.modules[__name__]`) can return a different copy when the file was loaded twice in one process.
108
+ _THIS_MODULE = sys.modules[__name__]
106
109
  STATUSES = ("todo", "in-progress", "code-done/data-pending", "done", "not-needed")
107
110
  FINISHED = ("done", "not-needed")
108
111
  OPEN = ("todo", "in-progress")
@@ -148,12 +151,19 @@ OUTPUT_MASKS: list[tuple[re.Pattern, str]] = [
148
151
  (re.compile(r"\bgh[pousr]_[A-Za-z0-9]{36,}\b"), "***"), # secret_filter github-token
149
152
  (re.compile(r"\bxox[abprs]-[A-Za-z0-9-]{10,}\b"), "***"), # secret_filter slack-token
150
153
  (re.compile(r"\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b"), "***"), # secret_filter jwt
151
- # secret_filter assignment, widened: a quoted value may hold spaces (a passphrase of several words).
152
- (re.compile(r"(?i)([A-Za-z0-9_]*(?:password|passwd|secret|api[_-]?key|token)[A-Za-z0-9_]*\s*[:=]\s*)"
154
+ # secret_filter assignment, widened: a quoted value may hold spaces (a passphrase of several words). A match
155
+ # starts only at the start of a word: a match inside a word always has one from the word start too, and without
156
+ # the lookbehind each character of a long word started a new scan (quadratic: 50 KB of one word took 64 s).
157
+ (re.compile(r"(?i)(?<![A-Za-z0-9_])"
158
+ r"([A-Za-z0-9_]*(?:password|passwd|secret|api[_-]?key|token)[A-Za-z0-9_]*\s*[:=]\s*)"
153
159
  r"(['\"])[^'\"\n]{8,}\2"), r"\1\2***\2"),
154
160
  (re.compile(r"(?im)^(\s*(?:export\s+)?[A-Za-z0-9_]*(?:PASSWORD|PASSWD|SECRET|API_KEY|APIKEY|TOKEN|PRIVATE_KEY)"
155
161
  r"[A-Za-z0-9_]*\s*=\s*)[^\s'\"#]{8,}"), r"\1***"), # secret_filter env-assignment
156
- (re.compile(r"\b([A-Za-z][A-Za-z0-9+.-]*://)[^/\s:@]+:[^/\s@]+@"), r"\1***@"), # URL credentials
162
+ # URL credentials. The scheme starts at a letter after a word boundary. Every such letter in one run of scheme
163
+ # characters reaches the same `://`, so only the first one is tried: group 1 skips, possessively, the run's
164
+ # digits, `+.-` and letters that follow a word character. Without that, `a.a.a.a...` was quadratic.
165
+ (re.compile(r"(?<![A-Za-z0-9+.-])((?:[0-9+.-]|(?<=\w)[A-Za-z])*+)([A-Za-z][A-Za-z0-9+.-]*://)"
166
+ r"[^/\s:@]+:[^/\s@]+@"), r"\1\2***@"),
157
167
  # Absolute paths, which can name a user or a host. A path may hold spaces, so each one runs to the end of the
158
168
  # line or a quote; a Windows drive or UNC path also stops at `<`, `>` or `|` (never part of a Windows path).
159
169
  (re.compile(r"(?<![A-Za-z0-9])[A-Za-z]:[\\/][^\r\n'\"<>|]*"), "<path>"), # a Windows drive path
@@ -2285,7 +2295,7 @@ def _render(root: Path, args: argparse.Namespace) -> int:
2285
2295
  for slug in slugs: # --all: one bad roadmap must not stop the others
2286
2296
  try:
2287
2297
  path = caddis_roadmap_page.write_page(root, slug, Path(args.out) if args.out else None,
2288
- sys.modules[__name__])
2298
+ _THIS_MODULE)
2289
2299
  if path is None: # a newer caddis owns this page: kept, and neither written nor failed
2290
2300
  continue
2291
2301
  written += 1