Open tuxgitbot[bot] opened 3 months ago
We use the Rust flavor of regex and recommend writing your regex in Rust syntax to minimize errors. To edit and test your regex syntax in Rust, we recommend using Rustexp. Case-insensitive and unicode support flags are on by default for every regex pattern.
here is a prototype regex (?i)\brm\s(-{1,2}(r|f|fr|rf|recursive|force)\s){1,2}\b|\b(r\sm|m\sr)\s(-{1,2}\w+\s){1,2}\b
what do you think about @kzndotsh
it need also
what do you think about @kzndotsh
check if it works with the rustexp lib they use
People might try to bypass your filter by purposefully adding typos or slight variations. You should account for common misspellings and variations like:
sudo
, Sudo
, suDo
, etc.rm
, rm
, r-m
, etc.-rf
, -r -f
, -r--f
, etc.\s*s(u|U)(d|D)(o|O)\s*r\s*-{0,2}r(\s*|\s*-){0,2}f\s*
You should ensure that the context in which these terms are used is also considered, as you don't want to block legitimate discussions around these commands.
\b(s(u|U)(d|D)(o|O)\s+r\s+-r-{0,2}f)\b
People might use multiple spaces or different types of separators (like underscores, dashes) to bypass simple regex.
\s*s(\\_?\s*u_?\s*d_?\s*o_?)\s*r(\\_?\s*)-?r(\\_?\s*)-?f\s*
Users might use similar-looking characters from different character sets to bypass simple text matching.
\s*[sSsS](u|U|u|U)(d|D|d|D)(o|O|o|O)\s*r\s*-{0,2}r(\s*|\s*-){0,2}f\s*
Ensure your regex is case insensitive to capture all uppercase and lowercase variations.
(?i)\b(sudo\s+rm\s+-?r?-?f)\b
Using word boundaries (\b
) to avoid partial matches.
\b(?:sudo|su|s u d o)\s+rm\s+(?:-r|-r\s+|-r\s+-\s*f|\s+-r\s+\s*f)\b
(?i)\b(?:sudo|s u d o)\s+rm\s+(?:-r\s*-f|-rf|-r\s*-?\s*f)\b
Thoroughly test your regex with various test cases including different contexts, spacing variations, and common legitimate uses of similar phrases.
Create a suite of test messages and see how your regex performs on both blocking jokes and allowing legitimate content.
sudo rm -rf /
S U D O r m -r -f
"Please avoid using sudo rm -rf in commands!"
sorry i'm going to research more 😭 @kzndotsh
Create a list of test cases that covers all the scenarios you need to consider. Split them into two categories: Positive Cases (which should be blocked) and Negative Cases (which should be allowed).
sudo rm -rf /
S U D O r m -r -f
sudo rm -rf
suDo rM - R F
sudo rm -r f
sudo rm -r file
Let's discuss sudo and rm commands
The command sudo rm -r file is dangerous
sudorrmf
sudo rm myfile
You can use various platforms and languages to run your tests. Python is a good choice because of its simplicity and powerful regex library.
Here’s an example of how you could set up and run these tests in Python:
import re
# Your regex pattern
pattern = re.compile(r"(?i)\b(?:sudo|s u d o)\s+rm\s+(?:-r\s*-f|-rf|-r\s*-?\s*f)\b")
# Define test cases
test_cases = {
"positive": [
"sudo rm -rf /",
"S U D O r m -r -f",
"sudo rm -rf",
"suDo rM - R F",
"sudo rm -r f"
],
"negative": [
"sudo rm -r file",
"Let's discuss sudo and rm commands",
"The command sudo rm -r file is dangerous",
"sudorrmf",
"sudo rm myfile"
]
}
# Run tests
def run_tests():
success = True
print("Running Positive Test Cases\n--------------------------")
for test_case in test_cases["positive"]:
if pattern.search(test_case):
print(f"PASSED: '{test_case}' was correctly flagged.")
else:
success = False
print(f"FAILED: '{test_case}' was not flagged as expected.")
print("\nRunning Negative Test Cases\n--------------------------")
for test_case in test_cases["negative"]:
if pattern.search(test_case):
success = False
print(f"FAILED: '{test_case}' was incorrectly flagged.")
else:
print(f"PASSED: '{test_case}' was correctly allowed.")
if success:
print("\nAll tests passed successfully!")
else:
print("\nSome tests failed. Please review the failed cases.")
# Execute tests
run_tests()
If you want to continually test as you update your regex, consider integrating these tests into a CI/CD pipeline using a tool like GitHub Actions, Travis CI, or Jenkins.
You can use the pytest
framework to make it more structured and scalable:
import re
import pytest
# Your regex pattern
pattern = re.compile(r"(?i)\b(?:sudo|s u d o)\s+rm\s+(?:-r\s*-f|-rf|-r\s*-?\s*f)\b")
# Define test cases
positive_cases = [
"sudo rm -rf /",
"S U D O r m -r -f",
"sudo rm -rf",
"suDo rM - R F",
"sudo rm -r f"
]
negative_cases = [
"sudo rm -r file",
"Let's discuss sudo and rm commands",
"The command sudo rm -r file is dangerous",
"sudorrmf",
"sudo rm myfile"
]
@pytest.mark.parametrize("test_case", positive_cases)
def test_positive_cases(test_case):
assert pattern.search(test_case), f"Positive test case failed: {test_case}"
@pytest.mark.parametrize("test_case", negative_cases)
def test_negative_cases(test_case):
assert not pattern.search(test_case), f"Negative test case failed: {test_case}"
if __name__ == "__main__":
pytest.main()
After running the tests, review any failed cases, adjust your regex pattern, and rerun the tests until you get the desired results.
sorry i'm going to research more 😭 @kzndotsh
im just brain dumping stuff rn no worries
we need ai to generate more test case @kzndotsh
This seems like a herculean task with the range of unicode characters that exist. E.g. of a test case not covered: there exists unicode character to replace (long) spaces (⠀, U+2800).
>>> "rm\u2800-rf\u2800/42"
'rm⠀-rf⠀/42'
I think a pragmatic way of doing it is auto-modding the most seen and manually doing it for the rest if spotted.
My first instinct is a (sudo|doas)?\s+rm\s+((-[frR]+|--force|--recursive|--no-preserve-root)\s+)+(/[a-zA-Z\.]*|~|/var/log)(\s|$)
with the i
flag.
You can of course replace any instance of /
with [/...]
where ...
are variants of /
, and similarly for \s
. If you really needed to, you could also insert [...]*
between every character, where ...
are the zero-width unicode characters, but I think that'd be overkill.
I think its reasonable to only catch common cases as @abxh said and manually mod the rest.
Now what scenarios could occur where it falsely flags something e.g. someone instructing someone for a support purpose?
I think checking for a stricter criteria where spaced apart letters are not checked for could be considered.
The goal of automodding rm -rf
is to prevent newbies from typing out the command accidentally (or directly copy-pasting the command). I believe that is important to consider in a problem like this where it's likely not all cases can be covered.
Viewed from the angle of a newbie, the most likely commands that would mislead a newbie would be:
1) Very slight variations in upper-lowercase. (this can be mitigated with .lower()
or regexes that account for that)
2) Variations with unicode chars. I have found a python library that could account for this; Unidecode.
3) ...
Even the above could be ignored. And just the literal commands (in ASCII) that poses a risk could be checked for.
If each letter is spaced apart, in my opinion, the command poses less of risk for newbies.
Note also: The more complex the regex, the worse it is for performance and resource usage.
I think checking for a stricter criteria where spaced apart letters are not checked for could be considered.
The goal of automodding
rm -rf
is to prevent newbies from typing out the command accidentally (or directly copy-pasting the command). I believe that is important to consider in a problem like this where it's likely not all cases can be covered.Viewed from the angle of a newbie, the most likely commands that would mislead a newbie would be:
- Very slight variations in upper-lowercase. (this can be mitigated with
.lower()
or regexes that account for that)- Variations with unicode chars. I have found a python library that could account for this; Unidecode.
- ...
Even the above could be ignored. And just the literal commands (in ASCII) that poses a risk could be checked for.
If each letter is spaced apart, in my opinion, the command poses less of risk for newbies.
Note also: The more complex the regex, the worse it is for performance and resource usage.
the regex is to prevent trolls
Now what scenarios could occur where it falsely flags something e.g. someone instructing someone for a support purpose?
There's bound to be a scenario where a support person may provide a rm -rf
command, so the context of the command will be important to consider.
The most common targets of the rm
trolls are usually the root "/
" and home "~
" directories, these may be utilized as a starting point for filtering out nonmalicious rm
commands, but it isn't perfect, for example, a troll could easily bypass said filters by using command substitution:
sudo rm -rf "$(pwd)"
This Bash command invokes "pwd
" to print the full filename of the current working directory, which in most cases would be a substitute for "~
".
Another case would be the usage of the dot symbol ".
" to substitute the current directory:
sudo rm -rf .
I genuinely think just filtering out /
, common /...
dirs, and ~
only is reasonable, and letting everything fall back to manual modding. Any more than that will have too many false positives imo
I do really like @abxh's idea with unidecode or similar libraries. Don't see why .lower() has to be used though, since Python regex has /re/i
.
Patterns that I think we should filter out automatically:
/
/bin/?
/boot/?
/dev/?
/etc/?
/home/?
/lib/?
/opt/?
/proc/?
/root/?
/run/?
/sbin/?
/sys/?
/usr/?
/var/?
~/?
/*/?
I can see /home/...
being used occasionally in install support, and .
, although rare, has popped up for me a few times, so I haven't included those. Any other cases to add, or weird edge cases warranting a removal from this list?
to discuss proper automod regex for blocking rm rf jokes properly
let's move from the tux automod to discord native automod so messages are not sent at all