Skip to content

🛡️ Sentinel: [MEDIUM] Fix regex newline injection and broken exclusion logic#113

Open
calionauta wants to merge 1 commit into
mainfrom
sentinel-fix-regex-exclusion-13976260757112085987
Open

🛡️ Sentinel: [MEDIUM] Fix regex newline injection and broken exclusion logic#113
calionauta wants to merge 1 commit into
mainfrom
sentinel-fix-regex-exclusion-13976260757112085987

Conversation

@calionauta

Copy link
Copy Markdown
Owner

🚨 Severity: MEDIUM

💡 Vulnerability:

  1. Regex newline injection: The _is_valid_skill_name functions used the $ anchor, which in Python's re module matches before a trailing newline. This could allow injecting newlines into skill names, potentially leading to argument injection if used in shell commands later.
  2. Broken exclusion logic: The _ignore_func in src/agent_sync/publish/git_publish.py had a manual matching logic that only handled patterns starting with *. or ., failing to filter out simple directory names like sessions or cache listed in DEFAULT_IGNORE_PATTERNS.

🎯 Impact:

  • Skill names containing newlines could bypass validation, posing a security risk if these names are used in security-sensitive contexts (like shell command construction).
  • Private data (sessions, cache, sqlite dbs) could be accidentally published to public repositories due to failing exclusion filters.

🔧 Fix:

  • Updated regex patterns to use \Z for absolute end-of-string matching.
  • Rewrote _ignore_func to use fnmatch.filter for standard and robust pattern matching against DEFAULT_IGNORE_PATTERNS.

✅ Verification:

  • Created tests/test_skill_validation_sentinel.py to verify that trailing newlines are now rejected.
  • Verified with a reproduction script that sessions and cache directories are now correctly ignored by shutil.copytree during the publish flow.
  • Ran the full project test suite: 568 passed.

PR created automatically by Jules for task 13976260757112085987 started by @renatocaliari

- Replaced vulnerable `$` anchor with `\Z` in `_is_valid_skill_name` within both `local_source.py` and `external_source.py` to prevent newline injection.
- Refactored `_ignore_func` in `git_publish.py` to use `fnmatch.filter`, ensuring that patterns like `sessions` and `cache` are correctly ignored during publication.
- Added regression test `tests/test_skill_validation_sentinel.py`.

Co-authored-by: renatocaliari <1719651+renatocaliari@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request improves skill name validation in both external_source.py and local_source.py by using \Z in the regular expression to prevent newline injection, and adds corresponding unit tests. It also refactors the file ignoring logic in git_publish.py to use fnmatch.filter. The reviewer suggested simplifying the file ignoring logic further by directly leveraging shutil.ignore_patterns from the standard library.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +33 to 41
import fnmatch

def _ignore(path, names):
ignored = []
for name in names:
for pattern in patterns:
if pattern.startswith('*.'):
if name.endswith(pattern[1:]):
ignored.append(name)
break
elif pattern.startswith('.'):
if name == pattern or name.startswith(pattern.rstrip('/') + '/'):
ignored.append(name)
break
return ignored
for pattern in patterns:
ignored.extend(fnmatch.filter(names, pattern))
return set(ignored)

return _ignore

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Instead of manually implementing the pattern filtering logic using fnmatch.filter and returning a custom callable, you can directly leverage the standard library's shutil.ignore_patterns function. This simplifies the code, improves maintainability, and avoids redundant implementation of standard library features.

Suggested change
import fnmatch
def _ignore(path, names):
ignored = []
for name in names:
for pattern in patterns:
if pattern.startswith('*.'):
if name.endswith(pattern[1:]):
ignored.append(name)
break
elif pattern.startswith('.'):
if name == pattern or name.startswith(pattern.rstrip('/') + '/'):
ignored.append(name)
break
return ignored
for pattern in patterns:
ignored.extend(fnmatch.filter(names, pattern))
return set(ignored)
return _ignore
return shutil.ignore_patterns(*patterns)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant