77RULES = [
88 [
99 "machine-local home path" ,
10- %r{(?:\A |[\s "'`=:(,])(?:/(?:Users|home)/[A-Za-z0-9._-]+(?:/|\b )|/root(?:/|(?![A-Za-z0-9._-])))|\b [A-Za-z]:[\\ /]Users[\\ /][^\\ /\r \n ]+(?:[\\ /]|$)}
10+ %r{(?:\A |[\s "'`=:(,])(?:/(?:Users|home)/[A-Za-z0-9._-]+(?:/|\b )|/root(?:/|(?![A-Za-z0-9._-])))|\b [A-Za-z]:[\\ /]{1,2} Users[\\ /]{1,2} [^\\ /\r \n ]+(?:[\\ /]{1,2} |$)}
1111 ] ,
1212 [ "AWS access key" , /\b (?:AKIA|ASIA)[0-9A-Z]{16}\b / ] ,
1313 [ "bearer credential" , /Authorization\s *:\s *Bearer\s +[A-Za-z0-9._~+\/ =:-]{16,}/i ] ,
1818 [ "OpenAI API key" , /\b sk-(?:proj-)?[A-Za-z0-9_-]{20,}\b / ] ,
1919 [
2020 "private key" ,
21- /-----BEGIN (?:[A-Z0-9][A-Z0-9 ]* )?PRIVATE KEY-----/
21+ /-----BEGIN (?:[A-Z0-9][A-Z0-9 ]* )?PRIVATE KEY(?: BLOCK)? -----/
2222 ]
2323] . freeze
2424
@@ -70,6 +70,14 @@ def display_path(relative_path, sensitive_paths)
7070 sensitive_paths . include? ( relative_path ) ? "<redacted path>" : relative_path . dump
7171end
7272
73+ def symlinked_parent? ( repo , relative_path )
74+ current = repo . b
75+ relative_path . b . split ( "/" . b ) [ 0 ...-1 ] . any? do |component |
76+ current = File . join ( current , component )
77+ File . symlink? ( current )
78+ end
79+ end
80+
7381usage ( "Too many arguments" ) if ARGV . length > 1
7482repo = File . expand_path ( ARGV . first || "." )
7583usage ( "Repository must be a directory" ) unless File . directory? ( repo )
@@ -86,9 +94,9 @@ def display_path(relative_path, sensitive_paths)
8694findings = Set . new
8795sensitive_paths = Set . new
8896blob_result_cache = { }
89- index_entries = index_output . split ( "\0 " ) . reject ( &:empty? ) . filter_map do |record |
90- metadata , relative_path = record . split ( "\t " , 2 )
91- mode , object_id , stage = metadata &.split ( " " , 3 )
97+ index_entries = index_output . b . split ( "\0 " . b ) . reject ( &:empty? ) . filter_map do |record |
98+ metadata , relative_path = record . split ( "\t " . b , 2 )
99+ mode , object_id , stage = metadata &.split ( " " . b , 3 )
92100 unless mode && object_id && stage && relative_path
93101 errors . add ( [ relative_path || "<unknown>" , "unreadable Git index entry" ] )
94102 next
@@ -146,11 +154,15 @@ def display_path(relative_path, sensitive_paths)
146154)
147155usage ( "Unable to enumerate repository worktree source" ) unless worktree_status . success?
148156
149- worktree_paths = worktree_output . split ( "\0 " ) . reject ( &:empty? ) . uniq
157+ worktree_paths = worktree_output . b . split ( "\0 " . b ) . reject ( &:empty? ) . uniq
150158worktree_paths . each do |relative_path |
151- absolute_path = File . join ( repo , relative_path )
152159 scan_source ( relative_path , relative_path , findings , sensitive_paths )
153160 begin
161+ if symlinked_parent? ( repo , relative_path )
162+ errors . add ( [ relative_path , "symlinked parent component" ] )
163+ next
164+ end
165+ absolute_path = File . join ( repo . b , relative_path )
154166 content = if File . symlink? ( absolute_path )
155167 File . readlink ( absolute_path )
156168 elsif File . file? ( absolute_path )
@@ -164,10 +176,10 @@ def display_path(relative_path, sensitive_paths)
164176
165177unless errors . empty? && findings . empty?
166178 warn "current public-source check failed:"
167- errors . to_a . sort . each do |relative_path , label |
179+ errors . to_a . sort_by { | relative_path , label | [ relative_path . b , label ] } . each do |relative_path , label |
168180 warn "- #{ display_path ( relative_path , sensitive_paths ) } : #{ label } "
169181 end
170- findings . to_a . sort . each do |relative_path , label |
182+ findings . to_a . sort_by { | relative_path , label | [ relative_path . b , label ] } . each do |relative_path , label |
171183 warn "- #{ display_path ( relative_path , sensitive_paths ) } : #{ label } "
172184 end
173185 warn "Matched content is intentionally omitted." unless findings . empty?
0 commit comments