Skip to content

chore: port desctructive/exfil/SC rules - #12

Merged
sarahxsanders merged 5 commits into
mainfrom
port-destructive-exfil-supply-rules
May 4, 2026
Merged

sarahxsanders merged 5 commits into
mainfrom
port-destructive-exfil-supply-rules

Conversation

@sarahxsanders

@sarahxsanders sarahxsanders commented Apr 24, 2026 •

Copy link
Copy Markdown
Collaborator

Note: stacked on top of the prompt-injection PR #10

Summary

more warlock ports for destructive exfil and supply chain rules + a bit of type and test infrastructure cleanup

new rules

9 rules:

  • destructive_git_force_push – medium/warn
  • destructive_git_force_push_protected_branch – critical/block
  • destructive_git_working_tree_loss – high/block
  • destructive_recursive_delete – medium/warn
  • destructive_recursive_delete_high_risk – critical/block
  • exfiltration_secret_via_shell – critical/block
  • supply_chain_wrong_posthog_package – high/block
  • supply_chain_package_json_exfil – critical/block (new, not a port)
  • supply_chain_github_workflow_exfil – critical/block (new, not a port)

rename: action: "revert" → action: "remediate"

renamed across posthog_pii_in_capture_call, posthog_hardcoded_personal_api_key, and the Action type.

why???????

  • revert collides with git vocabulary – reads like "run git revert" next to the new git push rules
  • implies something already happened, but warlock fires pre-write AND post-write; remediate covers both
  • standard term in Semgrep/Snyk, so consumers already recognize it

new helpers

3 new helpers in helpers.ts:

  • expectRuleMatch(content, ruleName) – asserts the specific rule matched, not "some rule matched."
  • expectRulesMatch(content, ruleNames[]) – asserts every named rule fires. Used for companion-rule tests.
  • expectRuleDidNotMatch(content, ruleName) – asserts the specific rule stayed silent.

type hygiene

  • added ACTIONS const + Action type, same append-only pattern as CATEGORIES / Severity
  • action?: Action on RuleMetadata so typos get caught at build time
  • expectRuleMetadata helper uses the real types now, not bare `string

companion-rule tests

new companion_rules.test.ts with 6 tests locking in that paired rules both fire on shared inputs (e.g. rm -rf / → both destructive-delete rules, git push --force origin main → both force-push rules). prevents silent drift where one companion breaks but the other compensates

Test plan

  • pnpm test – 314/314 green
  • Spot-check rule metadata on a matched scan (severity, category, action, remediation strings all present)
  • pnpm build succeeds (type narrowing on the new Action type)

@sarahxsanders
sarahxsanders requested a review from a team April 24, 2026 21:28
@sarahxsanders
sarahxsanders changed the base branch from main to port-prompt-injection-rules April 27, 2026 17:20
@sarahxsanders sarahxsanders changed the title chore: port desctructive, exfil, and supply chain rules chore: port desctructive/exfil/SC rules Apr 27, 2026
@sarahxsanders sarahxsanders mentioned this pull request Apr 27, 2026
3 tasks done
Base automatically changed from port-prompt-injection-rules to main May 1, 2026 21:05

@gewenyu99 gewenyu99 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.

Thanks for putting these together! I will surely lose sleep thinking about these now xD

// rule destructive_git_force_push_protected_branch handles the
// critical case where the target is a protected branch.

rule destructive_git_force_push

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.

ah nice you cover it here

action = "block"

strings:
// git reset --hard (with or without a target)

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.

git rebase -i HEAD~<some number> can also be quite annoying. You won't lose code, but you will lose commit history. This lets you squash commits so that the past N commits are squashed together

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

gonna add this and a few others as issues for the backlog to do in a quick follow up https://github.com/PostHog/warlock/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen

action = "warn"

strings:
// rm -rf / -fr / -Rf / -fR / -r -f / -f -r / --recursive --force / --force --recursive

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.

Can/should we break these into multiple rules? This is getting very hard to read, and I think long term htis regex pattern is gonna be more error prone...

I'm not entirely sure how tho

strings:
// rm -rf (and -fr / -Rf / -fR / etc) aimed at root, home, bare
// wildcard, bare dot / dotdot, or a canonical system path
$rm_recursive_dangerous = /\brm\s+(-[a-zA-Z]*[rR][a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*[rR]|-[rR]\s+-f|-f\s+-[rR]|--recursive\s+--force|--force\s+--recursive)\s+("\/"|'\/'|\/($|\s|\*)|~($|\/|\s)|\.($|\s)|\.\.($|\s)|\*($|\s)|\/(etc|bin|sbin|usr|var|home|Users|sys|boot|lib|opt|root)(\/|\s|$))/

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.

Same with comment above. There might be a case where we can have different pattern for each of `-fr, -Rf, -fR, etc.)

// variable turns this into rm -rf /
$rm_recursive_unquoted_var = /\brm\s+(-[a-zA-Z]*[rR][a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*[rR]|-[rR]\s+-f|-f\s+-[rR]|--recursive\s+--force|--force\s+--recursive)\s+\$[A-Za-z_{]/

// sudo + any recursive rm (target doesn't matter, sudo escalates the blast radius)

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.

Should there be a rule against user switching and sudo in general? all sudo and su commands should be blocked

{
meta:
description = "Base64 blob (40+ chars) embedded in a code comment. A common way to hide prompt-injection instructions from human reviewers."
description = "Long base64 blob (100+ chars) embedded in a code comment. A common way to hide prompt-injection instructions from human reviewers."

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.

Similar to before. I feel like blobs are just sus in comments. Maybe we can be more strict here... I'm not sure

// "workflows", "endpoints") — require a posthog qualifier
// somewhere in the phrase.
$attack_generic_feature_posthog_qualifier = /\b(disable|turn\s+off|stop|skip|remove|break|bypass|deactivate|kill|stop\s+using|comment\s+out|(don'?t|do\s+not)\s+(enable|use|track))\s+(the\s+)?posthog\s+(product\s+analytics|web\s+analytics|error\s+(tracking|monitoring)|surveys?|support|data\s+(pipelines?|warehouse)|llm\s+analytics|revenue\s+analytics|customer\s+analytics|workflows?|logs?|endpoints?|identify|event\s+(tracking|capture))\b/i
$attack_generic_feature_posthog_qualifier = /\b(disable|turn\s+off|stop|skip|remove|break|bypass|deactivate|kill|don'?t\s+(enable|use|track))\s+(the\s+)?posthog\s+(product\s+analytics|web\s+analytics|error\s+(tracking|monitoring)|surveys?|support|data\s+(pipelines?|warehouse)|llm\s+analytics|revenue\s+analytics|customer\s+analytics|workflows?|logs?|endpoints?|identify|event\s+tracking)\b/i

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.

This is really cool? what would something like this do?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

if an attacker could disable session replay for example, it could blind the customer so they can't see what happens during another attacker. if there's no session replay or event tracking, it's harder to notice malicious behavior or trigger alarm bells. imo a sophisticated attacker could do this instead of uninstalling the SDK because it would go longer unnoticed

// qualifier): "posthog surveys should be disabled"
$attack_passive_generic = /\bposthog\s+(product\s+analytics|web\s+analytics|error\s+(tracking|monitoring)|surveys?|support|data\s+(pipelines?|warehouse)|llm\s+analytics|revenue\s+analytics|customer\s+analytics|workflows?|logs?|endpoints?|identify|event\s+(tracking|capture))\s+(should\s+be|must\s+be|has\s+to\s+be|have\s+to\s+be|needs\s+to\s+be|need\s+to\s+be|is\s+to\s+be|are\s+to\s+be)\s+(disabled|removed|skipped|deactivated|turned\s+off|killed|broken|bypassed)\b/i
$attack_skip_capture_call = /\b(skip|remove|delete|don'?t\s+(add|include|call))\s+(the\s+)?(posthog\.)?(capture|identify|reset|group)\s*\(/i

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.

lol (this is just for memes) is there a world where someone could inject, say, amplitude in comments to make the wizard install amplitude instead?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

honestly yeah this is a legit attack vector 😆 #20 had PHCode log it in the backlog


condition:
$install_bare_posthog and not $install_suffixed_posthog
}

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.

Do we have a separate thing for things like:

  • pointing package managers to non npm registries? npm config set registry https://registry.totally-legit.com/
  • pointing package managers to local registries

etc.

Break down the flag-combo and dangerous-target alternations in the
recursive delete rules so each branch of the regex is documented.
No pattern changes — comments only.

Generated-By: PostHog Code
Task-Id: cd736619-e011-4bb0-abfc-a92066533ccf
@sarahxsanders
sarahxsanders force-pushed the port-destructive-exfil-supply-rules branch from 4dbacca to 65443fc Compare May 4, 2026 19:33
@sarahxsanders
sarahxsanders merged commit 93c660f into main May 4, 2026
9 checks passed
@sarahxsanders
sarahxsanders deleted the port-destructive-exfil-supply-rules branch May 4, 2026 19:37
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.

2 participants