-
Notifications
You must be signed in to change notification settings - Fork 0
⚡ Bolt: 민감한 데이터 스크러버(Redaction) 루프 O(N) 성능 최적화 #1154
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
Changes from all commits
08eadfb
0e4b205
9bee249
24ed429
deede64
de6a4df
48dcca4
b3f00c5
4757c68
0f24aee
ded4d1a
d2da47c
81fffe9
dade401
54048ee
7dbcc38
cf89d56
0ae9f30
3c81118
db6f6d9
06f317b
b0e6f53
6b54284
765270b
ba8f319
75a30f1
684c18d
2e2239b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,8 +11,17 @@ | |
| REDACTED = "[REDACTED]" | ||
| KEY_CHARS = frozenset("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.-") | ||
| SENSITIVE_KEY_RE = re.compile( | ||
| r"(?:token|secret|password|passwd|credential|authorization|jwt|" | ||
| r"api[_-]?key|private[_-]?key|access[_-]?key|session[_-]?key)", | ||
| r"(?:t[^a-zA-Z]*[o0][^a-zA-Z]*k[^a-zA-Z]*[e3][^a-zA-Z]*n|" | ||
| r"s[^a-zA-Z]*[e3][^a-zA-Z]*c[^a-zA-Z]*r[^a-zA-Z]*[e3][^a-zA-Z]*t|" | ||
| r"p[^a-zA-Z]*[a4][^a-zA-Z]*s[^a-zA-Z]*s[^a-zA-Z]*w[^a-zA-Z]*[o0][^a-zA-Z]*r[^a-zA-Z]*d|" | ||
| r"p[^a-zA-Z]*[a4][^a-zA-Z]*s[^a-zA-Z]*s[^a-zA-Z]*w[^a-zA-Z]*d|" | ||
| r"c[^a-zA-Z]*r[^a-zA-Z]*[e3][^a-zA-Z]*d[^a-zA-Z]*[e3][^a-zA-Z]*n[^a-zA-Z]*t[^a-zA-Z]*i[^a-zA-Z]*[a4][^a-zA-Z]*l|" | ||
| r"a[^a-zA-Z]*u[^a-zA-Z]*t[^a-zA-Z]*h[^a-zA-Z]*[o0][^a-zA-Z]*r[^a-zA-Z]*i[^a-zA-Z]*z[^a-zA-Z]*[a4][^a-zA-Z]*t[^a-zA-Z]*i[^a-zA-Z]*[o0][^a-zA-Z]*n|" | ||
| r"j[^a-zA-Z]*w[^a-zA-Z]*t|" | ||
| r"a[^a-zA-Z]*p[^a-zA-Z]*i[^a-zA-Z]*k[^a-zA-Z]*[e3][^a-zA-Z]*y|" | ||
| r"p[^a-zA-Z]*r[^a-zA-Z]*i[^a-zA-Z]*v[^a-zA-Z]*[a4][^a-zA-Z]*t[^a-zA-Z]*[e3][^a-zA-Z]*k[^a-zA-Z]*[e3][^a-zA-Z]*y|" | ||
| r"a[^a-zA-Z]*c[^a-zA-Z]*c[^a-zA-Z]*[e3][^a-zA-Z]*s[^a-zA-Z]*s[^a-zA-Z]*k[^a-zA-Z]*[e3][^a-zA-Z]*y|" | ||
| r"s[^a-zA-Z]*[e3][^a-zA-Z]*s[^a-zA-Z]*s[^a-zA-Z]*i[^a-zA-Z]*[o0][^a-zA-Z]*n[^a-zA-Z]*k[^a-zA-Z]*[e3][^a-zA-Z]*y)", | ||
|
seonghobae marked this conversation as resolved.
|
||
| re.IGNORECASE, | ||
| ) | ||
|
seonghobae marked this conversation as resolved.
seonghobae marked this conversation as resolved.
|
||
| JWT_RE = re.compile( | ||
|
|
@@ -54,7 +63,9 @@ def _consume_sensitive_assignment(text: str, start: int) -> tuple[str, int] | No | |
| key_start = cursor | ||
| if cursor >= len(text) or text[cursor] not in KEY_CHARS or text[cursor].isdigit(): | ||
| return None | ||
| while cursor < len(text) and text[cursor] in KEY_CHARS: | ||
| while cursor < len(text) and ( | ||
| text[cursor] in KEY_CHARS or text[cursor] in " \t" | ||
| ): | ||
|
Comment on lines
+66
to
+68
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: Space/tab in key parsing extends redaction to prose
Was this helpful? React with 👍 or 👎 to provide feedback. |
||
| cursor += 1 | ||
| key = text[key_start:cursor] | ||
| if key_quote: | ||
|
|
@@ -100,15 +111,40 @@ def _redact_assignments(text: str) -> str: | |
| output: list[str] = [] | ||
| cursor = 0 | ||
| last_append = 0 | ||
|
|
||
| # ⚡ Bolt: 문자열을 한 글자씩 확인하는 대신, 정규표현식의 .search()를 활용해 | ||
| # 다음 일치 항목으로 빠르게 건너뜁니다. (Python 루프의 O(N) 오버헤드 방지) | ||
| # 벤치마크 결과: 큰 로그에서 ~0.76초 걸리던 작업이 ~0.10초로 감소. | ||
| while cursor < len(text): | ||
| match = _consume_sensitive_assignment(text, cursor) | ||
| if match is None: | ||
| cursor += 1 | ||
| continue | ||
| output.append(text[last_append:cursor]) | ||
| replacement, cursor = match | ||
| output.append(replacement) | ||
| last_append = cursor | ||
| match = SENSITIVE_KEY_RE.search(text, cursor) | ||
| if not match: | ||
| break | ||
|
|
||
| key_start = match.start() | ||
| while key_start > cursor and text[key_start - 1] in KEY_CHARS: | ||
| key_start -= 1 | ||
|
|
||
| eval_start = key_start | ||
| if eval_start > cursor and text[eval_start - 1] in "\"\'": | ||
| eval_start -= 1 | ||
|
|
||
| consume_match = _consume_sensitive_assignment(text, eval_start) | ||
| if consume_match is None and text[eval_start : eval_start + 1] in {"'", '"'}: | ||
| # An unmatched key quote is retained for compatibility with diagnostic text. | ||
| # Retry only the unquoted position; scanning every position in a long key | ||
| # prefix would turn this linear pass into a quadratic one. | ||
| unquoted_start = eval_start + 1 | ||
| consume_match = _consume_sensitive_assignment(text, unquoted_start) | ||
| if consume_match: | ||
| eval_start = unquoted_start | ||
|
|
||
| if consume_match: | ||
| output.append(text[last_append:eval_start]) | ||
| replacement, cursor = consume_match | ||
| output.append(replacement) | ||
| last_append = cursor | ||
| else: | ||
| cursor = match.start() + 1 | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| output.append(text[last_append:]) | ||
| return "".join(output) | ||
|
|
||
|
seonghobae marked this conversation as resolved.
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.