-
Notifications
You must be signed in to change notification settings - Fork 482
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Keyword regexes improvement #418
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for addressing this! I was going to work on a fix, but I'm so glad that you were able to fix these issues yourselves.
I like the detail that you have in your PR summary; I think that this should be captured in code as comments (especially your thorough breakdown of the new SECRET
regex rationale).
With regards to test cases, I want to abandon the "old-style" tests, and (eventually) replace them with much more readable tests. After all, readable tests are a great way of documenting intended usage of functions. I think you have a good set of test cases, and maybe my interim test cases will help you get started?
def test_ignore_case():
secret = next(scan_line('os.environ["AWS_SECRET_ACCESS_KEY"] = "testing"'))
assert secret.secret_value == 'testing'
def test_secret_with_whitespace():
secret = next(scan_line('password = "not a secret"'))
assert secret.secret_value == 'not a secret'
def test_comparison_operator():
secret = next(scan_line('api_key == "hunter2"'))
assert secret.secret_value == 'hunter2'
def test_bounds():
for index, secret in scan_line(
'detect-secrets scan '
'--exclude-lines "password = blah" '
'--exclude-lines "password = fake"',
):
assert secret.secret_value in {'blah', 'fake'}
# TODO(2021-03-03): This should probably yield `fake` as well.
# assert index == 1
def test_backticks():
secret = next(scan_line('api_key = `cat password`'))
assert secret.secret_value == 'cat password'
@pytest.mark.parametrize(
'line',
(
'if ("value" == password)',
'else if ("value" === password)',
...
),
)
def test_preceded_by_equals_sign(line):
pass
@pytest.fixture(autouse=True)
def use_keyword_detector():
with transient_settings({
'plugins_used': [{
'name': 'KeywordDetector',
}],
}):
yield
* feat: add auth keyword to detectors Another common word for secrets that isn't currently picked up. * feat: tests for new db2 keywords too * test: test case for user reported false negative
We implement a new We obtain a 98% of coverage with the new Thank you very much! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow! What you've done with the KeywordDetector test cases is amazing! So much better, and more than I expected. Thanks!
detect_secrets/plugins/keyword.py
Outdated
''' | ||
Secret regex details: | ||
[^\r\n]* -> this section match with every character except line breaks. | ||
This allows to find secrets that starts with symbols or | ||
alphanumeric characters. | ||
[a-zA-Z0-9]+ -> this section match only with alphanumeric characters, and at | ||
least one is required. This allows to reduce the false positives | ||
number. | ||
[^\r\n]* -> this section match with every character except line breaks. | ||
This allows to find secrets with symbols at the end. | ||
[^\r\n,\'"`] -> this section match with the last secret character that can be | ||
everything except line breaks, comma, backticks or quotes. This | ||
allows to reduce the false positives number and to prevent | ||
errors in the code snippet highlighting. | ||
''' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, are you running pre-commit
? I'm hitting this error:
$ pre-commit run --all-files
Check builtin type constructor use.......................................Passed
Check docstring is first.................................................Failed
hookid: check-docstring-first
detect_secrets/plugins/keyword.py:53 Multiple module docstrings (first docstring on line 1).
Debug Statements (Python)................................................Passed
Fix double quoted strings................................................Passed
Fix End of Files.........................................................Passed
Tests should end in _test.py.............................................Passed
Flake8...................................................................Passed
Trim Trailing Whitespace.................................................Passed
Reorder python imports...................................................Passed
Add trailing commas......................................................Passed
autopep8.................................................................Passed
Detect secrets...........................................................Failed
hookid: detect-secrets
Files were modified by this hook. Additional output:
The baseline file was updated.
Probably to keep line numbers of secrets up-to-date.
Please `git add .secrets.baseline`, thank you.
Your baseline file (.secrets.baseline) is unstaged.
`git add .secrets.baseline` to fix this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we are running pre-commit
. This error is in the line 53 that is the comment of FALSE_POSITIVES
variable. The details of the SECRET
regex can be added using one line comments (#
) instead of multiple line comments ('''
). What do you prefer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, let's just do multiple line comments, so pre-commit will be happy. Then I can merge this in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I think that pre-commit
is working fine now
Thank you very much!!! |
# everything except line breaks, comma, backticks or quotes. This | ||
# allows to reduce the false positives number and to prevent | ||
# errors in the code snippet highlighting. | ||
SECRET = r'[^\r\n]*[a-zA-Z0-9]+[^\r\n]*[^\r\n,\'"`]' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pablosantiagolopez : I think you might have introduced an accidental exponential performance degradation here, with files with long lines.
(stable_version) $ time detect-secrets scan detect_secrets/filters/gibberish/rfc.model
...
real 0m0.286s
user 0m0.243s
sys 0m0.036s
(version_on_master) $ time detect-secrets scan detect_secrets/filters/gibberish/rfc.model
...
real 29m28.225s
user 29m24.396s
sys 0m2.074s
(version_on_master) $ time detect-secrets scan detect_secrets/filters/gibberish/rfc.model --disable-plugin KeywordDetector
...
real 0m0.385s
user 0m0.322s
sys 0m0.054s
Please look into this.
@pablosantiagolopez : sorry it's been a while since I was able to look at your other pending PRs. I realized that since the In my investigations, I have found that accidental exponential performance degradation is still there, on the master branch. Here is the example: <img src="data:image/png;base64,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
"
> On the latest published version, this payload works. However, on the current master ( Maybe we should just add a max char limit to the regex? |
Hi @domanchi, how are you? About our pending PRs, no problem, we understand that reviewing them suppose very much time and effort, so don't worry. About the performance problem of the
Based in this links, I have changed the SECRET = r'(?=[^\v\'\"]*)(?=\w+)[^\v\'\"]*[^\v,\'\"`]' The regex content is the same, but the first two sub-groups are moved into Please, test this regex and tell me what do you think about this. If it works for you, I can add it into one of our pending PRs because they are also related to the |
Thanks for your quick response! I'll test it out, and let you know. |
Hi! I totally agree with the backtracking performance problem that @pablosantiagolopez has mentioned. I would like to comment that it might be interesting to create a filter for these cases, in addition to fixing the backtracking problem (if the @domanchi 's tests confirm it). I think that a Also, other good idea can be include more non-code files extensions into the This tips can improve the |
@pablosantiagolopez : I can confirm it performs a lot better. Let's cut out a PR for this change alone, so that I can merge it independently of other modifications to the EDIT: Feel free to use the example payload as a test case for long lines, so that we can add regression tests. |
Perfect, it's comming...Thank you! |
Amazing PR @pablosnt 😮 |
Thank you very much @KevinHock!!! Everything is easier with the help of @syn-4ck ;) |
This Pull Request solves some issues detected in #414 and includes some improvements described in #396 to the latest version of detect-secrets.
This code includes:
FOLLOWED_BY_EQUAL_SIGNS_REGEX
andFOLLOWED_BY_EQUAL_SIGNS_QUOTES_REQUIRED_REGEX
regexes. For example:PRECEDED_BY_EQUAL_COMPARISON_SIGNS_QUOTES_REQUIRED_REGEX
regex. For example:Corrections in the
SECRET
regex to avoid some problems detected in Bugfix of Yaml exception with simple quotes #414 and reduce the false positive percentage. The regex can be decomposed in the following sections:[^\r\n]*
: this section match with every character except line breaks. This allows to find secrets that starts with symbols or alphanumeric characters.[a-zA-Z0-9]+
: this section match only with alphanumeric characters, and at least one is required. This allows to reduce the false positives number.[^\r\n]*
: this section match with every character except line breaks. This allows to find secrets with symbols at the end.[^\r\n,\'"]
: this section match with the last secret character that can be everything except line breaks, comma or quotes. This allows to reduce the false positives number and to prevent errors in the code snippet highlighting. Next you can see an example:The following image shows a test with this new regex:
Of course,
keyword_test.py
has been updated to check all this changes.