evil_faker 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +62 -0
- data/GENERATORS.md +164 -0
- data/LICENSE.txt +21 -0
- data/NOTICE.md +196 -0
- data/README.md +156 -0
- data/Rakefile +12 -0
- data/lib/evil_faker/base.rb +50 -0
- data/lib/evil_faker/data/blns.txt +742 -0
- data/lib/evil_faker/data/groups/blns/emoji/emoji.txt +10 -0
- data/lib/evil_faker/data/groups/blns/emoji/emoticons.txt +12 -0
- data/lib/evil_faker/data/groups/blns/emoji/regional.txt +3 -0
- data/lib/evil_faker/data/groups/blns/exploits/cves.txt +4 -0
- data/lib/evil_faker/data/groups/blns/exploits/human.txt +1 -0
- data/lib/evil_faker/data/groups/blns/exploits/ios.txt +3 -0
- data/lib/evil_faker/data/groups/blns/exploits/irc.txt +1 -0
- data/lib/evil_faker/data/groups/blns/filenames.txt +14 -0
- data/lib/evil_faker/data/groups/blns/injection/command.txt +6 -0
- data/lib/evil_faker/data/groups/blns/injection/file_inclusion.txt +2 -0
- data/lib/evil_faker/data/groups/blns/injection/interpolation.txt +9 -0
- data/lib/evil_faker/data/groups/blns/injection/jinja2.txt +2 -0
- data/lib/evil_faker/data/groups/blns/injection/script.txt +236 -0
- data/lib/evil_faker/data/groups/blns/injection/server_code.txt +9 -0
- data/lib/evil_faker/data/groups/blns/injection/sql.txt +8 -0
- data/lib/evil_faker/data/groups/blns/injection/xxe.txt +1 -0
- data/lib/evil_faker/data/groups/blns/numeric/numeric.txt +71 -0
- data/lib/evil_faker/data/groups/blns/numeric/unicode_numbers.txt +2 -0
- data/lib/evil_faker/data/groups/blns/profanity.txt +22 -0
- data/lib/evil_faker/data/groups/blns/reserved.txt +19 -0
- data/lib/evil_faker/data/groups/blns/rtl.txt +7 -0
- data/lib/evil_faker/data/groups/blns/terminal.txt +3 -0
- data/lib/evil_faker/data/groups/blns/unicode/accents.txt +4 -0
- data/lib/evil_faker/data/groups/blns/unicode/bom.txt +2 -0
- data/lib/evil_faker/data/groups/blns/unicode/controls.txt +3 -0
- data/lib/evil_faker/data/groups/blns/unicode/font.txt +8 -0
- data/lib/evil_faker/data/groups/blns/unicode/lowercase.txt +2 -0
- data/lib/evil_faker/data/groups/blns/unicode/quotes.txt +11 -0
- data/lib/evil_faker/data/groups/blns/unicode/scripts.txt +2 -0
- data/lib/evil_faker/data/groups/blns/unicode/special.txt +4 -0
- data/lib/evil_faker/data/groups/blns/unicode/symbols.txt +12 -0
- data/lib/evil_faker/data/groups/blns/unicode/trick.txt +5 -0
- data/lib/evil_faker/data/groups/blns/unicode/two_byte.txt +10 -0
- data/lib/evil_faker/data/groups/blns/unicode/upsidedown.txt +2 -0
- data/lib/evil_faker/data/groups/blns/unicode/whitespace.txt +1 -0
- data/lib/evil_faker/data/groups/blns/zalgo.txt +5 -0
- data/lib/evil_faker/data/groups/fuzzdb/email/invalid.txt +26 -0
- data/lib/evil_faker/data/groups/fuzzdb/email/valid.txt +32 -0
- data/lib/evil_faker/data/groups/fuzzdb/emoji/emoji.txt +8 -0
- data/lib/evil_faker/data/groups/fuzzdb/emoji/emoticons.txt +11 -0
- data/lib/evil_faker/data/groups/fuzzdb/emoji/regional.txt +3 -0
- data/lib/evil_faker/data/groups/fuzzdb/exploits/ios.txt +1 -0
- data/lib/evil_faker/data/groups/fuzzdb/headers/mimetypes.txt +1864 -0
- data/lib/evil_faker/data/groups/fuzzdb/headers/protocol.txt +357 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/command.txt +1115 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/directory_traversal.txt +536 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/file_inclusion.txt +906 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/file_upload_bypass.txt +371 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/format_strings.txt +67 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/generic.txt +584 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/integer_overflow.txt +12 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/json.txt +89 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/ldap.txt +35 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/magic_hashes.txt +24 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/nosql.txt +17 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/open_redirect.txt +37 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/rfi.txt +2241 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/script.txt +930 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/server_side_include.txt +78 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/source_disclosure.txt +18 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/sql.txt +661 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/ssrf.txt +56 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/type_confusion.txt +329 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/xpath.txt +13 -0
- data/lib/evil_faker/data/groups/fuzzdb/injection/xxe.txt +65 -0
- data/lib/evil_faker/data/groups/fuzzdb/routes/debug_params.txt +157 -0
- data/lib/evil_faker/data/groups/fuzzdb/routes/directory_indexing.txt +15 -0
- data/lib/evil_faker/data/groups/fuzzdb/routes/local_paths.txt +26 -0
- data/lib/evil_faker/data/groups/fuzzdb/rtl.txt +5 -0
- data/lib/evil_faker/data/groups/fuzzdb/terminal.txt +3 -0
- data/lib/evil_faker/data/groups/fuzzdb/unicode/bad_chars.txt +15 -0
- data/lib/evil_faker/data/groups/fuzzdb/unicode/special.txt +31 -0
- data/lib/evil_faker/data/groups/fuzzdb/unicode/trick.txt +5 -0
- data/lib/evil_faker/data/groups/fuzzdb/unicode/two_byte.txt +9 -0
- data/lib/evil_faker/data/groups/fuzzdb/unicode/upsidedown.txt +2 -0
- data/lib/evil_faker/data/groups/intruder_payloads/filenames.txt +124 -0
- data/lib/evil_faker/data/groups/intruder_payloads/injection/command.txt +445 -0
- data/lib/evil_faker/data/groups/intruder_payloads/injection/directory_traversal.txt +2578 -0
- data/lib/evil_faker/data/groups/intruder_payloads/injection/file_inclusion.txt +698 -0
- data/lib/evil_faker/data/groups/intruder_payloads/injection/generic.txt +1401 -0
- data/lib/evil_faker/data/groups/intruder_payloads/injection/overflow.txt +36 -0
- data/lib/evil_faker/data/groups/intruder_payloads/injection/script.txt +61 -0
- data/lib/evil_faker/data/groups/intruder_payloads/injection/server_side_include.txt +18 -0
- data/lib/evil_faker/data/groups/intruder_payloads/injection/sql.txt +753 -0
- data/lib/evil_faker/data/groups/intruder_payloads/injection/xxe.txt +66 -0
- data/lib/evil_faker/data/groups/intruder_payloads/unicode/bad_chars.txt +1 -0
- data/lib/evil_faker/data/groups/payloads_all_the_things/headers/cache_deception.txt +1125 -0
- data/lib/evil_faker/data/groups/payloads_all_the_things/injection/command.txt +496 -0
- data/lib/evil_faker/data/groups/payloads_all_the_things/injection/directory_traversal.txt +22607 -0
- data/lib/evil_faker/data/groups/payloads_all_the_things/injection/file_inclusion.txt +4787 -0
- data/lib/evil_faker/data/groups/payloads_all_the_things/injection/ldap.txt +60 -0
- data/lib/evil_faker/data/groups/payloads_all_the_things/injection/nosql.txt +26 -0
- data/lib/evil_faker/data/groups/payloads_all_the_things/injection/open_redirect.txt +305 -0
- data/lib/evil_faker/data/groups/payloads_all_the_things/injection/script.txt +2165 -0
- data/lib/evil_faker/data/groups/payloads_all_the_things/injection/sql.txt +1354 -0
- data/lib/evil_faker/data/groups/payloads_all_the_things/injection/ssti.txt +105 -0
- data/lib/evil_faker/data/groups/payloads_all_the_things/injection/xxe.txt +102 -0
- data/lib/evil_faker/data/groups/payloads_all_the_things/routes/admin_interfaces.txt +52 -0
- data/lib/evil_faker/groups/email.rb +11 -0
- data/lib/evil_faker/groups/emoji.rb +12 -0
- data/lib/evil_faker/groups/exploits.rb +13 -0
- data/lib/evil_faker/groups/filenames.rb +8 -0
- data/lib/evil_faker/groups/headers.rb +12 -0
- data/lib/evil_faker/groups/injection.rb +35 -0
- data/lib/evil_faker/groups/numeric.rb +11 -0
- data/lib/evil_faker/groups/profanity.rb +8 -0
- data/lib/evil_faker/groups/reserved.rb +8 -0
- data/lib/evil_faker/groups/routes.rb +13 -0
- data/lib/evil_faker/groups/rtl.rb +8 -0
- data/lib/evil_faker/groups/terminal.rb +8 -0
- data/lib/evil_faker/groups/unicode.rb +23 -0
- data/lib/evil_faker/groups/zalgo.rb +8 -0
- data/lib/evil_faker/source_base.rb +30 -0
- data/lib/evil_faker/sources/blns.rb +8 -0
- data/lib/evil_faker/sources/fuzzdb.rb +8 -0
- data/lib/evil_faker/sources/intruder_payloads.rb +8 -0
- data/lib/evil_faker/sources/payloads_all_the_things.rb +8 -0
- data/lib/evil_faker/version.rb +5 -0
- data/lib/evil_faker.rb +13 -0
- data/scripts/blns_build_corpus.rb +159 -0
- data/scripts/fuzzdb_build_corpus.rb +166 -0
- data/scripts/generate_namespaces.rb +87 -0
- data/scripts/intruder_payloads_build_corpus.rb +112 -0
- data/scripts/payloads_all_the_things_build_corpus.rb +74 -0
- data/sig/evil_faker.rbs +4 -0
- metadata +181 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Regenerates the per-group corpus files from lib/evil_faker/data/blns.txt
|
|
4
|
+
# (Big List of Naughty Strings, MIT, Max Woolf -- github.com/minimaxir/big-list-of-naughty-strings).
|
|
5
|
+
#
|
|
6
|
+
# Output: lib/evil_faker/data/groups/blns/<namespace>.txt (single-topic namespaces)
|
|
7
|
+
# lib/evil_faker/data/groups/blns/<namespace>/<slug>.txt (namespaces with several sub-groups)
|
|
8
|
+
#
|
|
9
|
+
# Nested under blns/ because that's the source -- other sources get their own
|
|
10
|
+
# sibling folder (groups/payloads_all_the_things/, groups/intruder_payloads/),
|
|
11
|
+
# never mixed into this one.
|
|
12
|
+
#
|
|
13
|
+
# Usage: ruby scripts/blns_build_corpus.rb
|
|
14
|
+
|
|
15
|
+
ROOT = File.expand_path("..", __dir__)
|
|
16
|
+
OUT_DIR = File.join(ROOT, "lib/evil_faker/data/groups/blns")
|
|
17
|
+
|
|
18
|
+
BLNS_SOURCE = File.join(ROOT, "lib/evil_faker/data/blns.txt")
|
|
19
|
+
|
|
20
|
+
# Map: namespace -> { slug -> [title patterns] }.
|
|
21
|
+
# Patterns are matched as case-insensitive substrings against a section's title.
|
|
22
|
+
# Order matters: the first (namespace, slug) whose patterns match wins.
|
|
23
|
+
GROUPS = {
|
|
24
|
+
"injection" => {
|
|
25
|
+
"script" => ["Script Injection"],
|
|
26
|
+
"sql" => ["SQL Injection"],
|
|
27
|
+
"server_code" => ["Server Code Injection"],
|
|
28
|
+
"command" => ["Command Injection"],
|
|
29
|
+
"xxe" => ["XXE Injection"],
|
|
30
|
+
"interpolation" => ["Unwanted Interpolation"],
|
|
31
|
+
"file_inclusion" => ["File Inclusion"],
|
|
32
|
+
"jinja2" => ["jinja2 injection"]
|
|
33
|
+
},
|
|
34
|
+
"exploits" => {
|
|
35
|
+
"cves" => ["Known CVEs"],
|
|
36
|
+
"irc" => ["IRC specific"],
|
|
37
|
+
"human" => ["Human injection"],
|
|
38
|
+
"ios" => ["iOS Vulnerabilities"]
|
|
39
|
+
},
|
|
40
|
+
"emoji" => {
|
|
41
|
+
"emoji" => ["Emoji"],
|
|
42
|
+
"regional" => ["Regional Indicator"],
|
|
43
|
+
"emoticons" => ["Japanese Emoticons"]
|
|
44
|
+
},
|
|
45
|
+
"numeric" => {
|
|
46
|
+
"numeric" => ["Numeric Strings"],
|
|
47
|
+
"unicode_numbers" => ["Unicode Numbers"]
|
|
48
|
+
},
|
|
49
|
+
"reserved" => { "reserved" => ["Reserved Strings"] },
|
|
50
|
+
"rtl" => { "rtl" => ["Right-To-Left"] },
|
|
51
|
+
"zalgo" => { "zalgo" => ["Zalgo"] },
|
|
52
|
+
"filenames" => { "filenames" => ["MSDOS/Windows Special Filenames"] },
|
|
53
|
+
"profanity" => { "profanity" => ["Scunthorpe"] },
|
|
54
|
+
"terminal" => { "terminal" => ["Terminal escape codes"] },
|
|
55
|
+
# Catch-all for unicode weirdness: anything that breaks by rendering/encoding, not an attack.
|
|
56
|
+
# Kept last: its patterns are broad and would swallow more specific sections above.
|
|
57
|
+
"unicode" => {
|
|
58
|
+
"special" => ["Special Characters"],
|
|
59
|
+
"controls" => ["Non-whitespace C0", "Non-whitespace C1", "Unicode additional control"],
|
|
60
|
+
"whitespace" => ["Whitespace:"],
|
|
61
|
+
"bom" => ["Byte order marks"],
|
|
62
|
+
"symbols" => ["Unicode Symbols", "Special Unicode Characters Union"],
|
|
63
|
+
"accents" => ["Unicode Subscript/Superscript/Accents"],
|
|
64
|
+
"quotes" => ["Quotation Marks"],
|
|
65
|
+
"two_byte" => ["Two-Byte Characters", "two-byte letters"],
|
|
66
|
+
"lowercase" => ["Changing length when lowercased"],
|
|
67
|
+
"scripts" => ["Ogham", "Persian special characters"],
|
|
68
|
+
"trick" => ["Trick Unicode"],
|
|
69
|
+
"upsidedown" => ["Unicode Upsidedown"],
|
|
70
|
+
"font" => ["Unicode font"]
|
|
71
|
+
}
|
|
72
|
+
}.freeze
|
|
73
|
+
|
|
74
|
+
# Namespaces with exactly one slug get a flat file (groups/<namespace>.txt) instead
|
|
75
|
+
# of a folder with a single file in it.
|
|
76
|
+
FLAT_NAMESPACES = %w[reserved rtl zalgo filenames profanity terminal].freeze
|
|
77
|
+
|
|
78
|
+
# --- parse: raw BLNS section -> [strings] ---
|
|
79
|
+
class SectionParser
|
|
80
|
+
attr_reader :sections
|
|
81
|
+
|
|
82
|
+
def initialize
|
|
83
|
+
@sections = []
|
|
84
|
+
@prev_comment = false
|
|
85
|
+
@current = nil
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def feed(line)
|
|
89
|
+
if line.start_with?("#")
|
|
90
|
+
start_section(line) unless @prev_comment
|
|
91
|
+
@prev_comment = true
|
|
92
|
+
else
|
|
93
|
+
@prev_comment = false
|
|
94
|
+
@current[:strings] << line if @current && !line.empty?
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
def start_section(line)
|
|
101
|
+
@current = { title: line.sub(/^#\s*/, "").strip, strings: [] }
|
|
102
|
+
@sections << @current
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def parse_sections(path)
|
|
107
|
+
parser = SectionParser.new
|
|
108
|
+
File.readlines(path, chomp: true).each { |line| parser.feed(line) }
|
|
109
|
+
parser.sections
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# --- classify each section into a (namespace, slug) ---
|
|
113
|
+
def classify(title)
|
|
114
|
+
GROUPS.each do |ns, slugs|
|
|
115
|
+
slugs.each do |slug, patterns|
|
|
116
|
+
return [ns, slug] if patterns.any? { |p| title.downcase.include?(p.downcase) }
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
sections = parse_sections(BLNS_SOURCE)
|
|
123
|
+
|
|
124
|
+
buckets = Hash.new { |h, k| h[k] = Hash.new { |hh, kk| hh[kk] = [] } }
|
|
125
|
+
unmatched = []
|
|
126
|
+
sections.each do |sec|
|
|
127
|
+
ns_slug = classify(sec[:title])
|
|
128
|
+
if ns_slug
|
|
129
|
+
buckets[ns_slug.first][ns_slug.last].concat(sec[:strings])
|
|
130
|
+
else
|
|
131
|
+
unmatched << sec[:title]
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# --- write one file per (namespace, slug), no headers: the path is the group ---
|
|
136
|
+
require "fileutils"
|
|
137
|
+
# Wipe first: a section BLNS removes/renames must not leave a stale file behind
|
|
138
|
+
# for Base's subtype auto-discovery to pick up.
|
|
139
|
+
FileUtils.rm_rf(OUT_DIR)
|
|
140
|
+
FileUtils.mkdir_p(OUT_DIR)
|
|
141
|
+
|
|
142
|
+
buckets.each do |ns, slugs|
|
|
143
|
+
flat = FLAT_NAMESPACES.include?(ns)
|
|
144
|
+
FileUtils.mkdir_p(File.join(OUT_DIR, ns)) unless flat
|
|
145
|
+
|
|
146
|
+
slugs.each do |slug, strings|
|
|
147
|
+
path = flat ? File.join(OUT_DIR, "#{ns}.txt") : File.join(OUT_DIR, ns, "#{slug}.txt")
|
|
148
|
+
File.open(path, "w") { |f| strings.each { |s| f.puts s } }
|
|
149
|
+
printf(" %<path>-40s %<count>3d strings\n", path: path.sub("#{OUT_DIR}/", ""), count: strings.size)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
puts
|
|
154
|
+
if unmatched.empty?
|
|
155
|
+
puts "OK: all sections classified."
|
|
156
|
+
else
|
|
157
|
+
puts "UNCLASSIFIED (#{unmatched.size}):"
|
|
158
|
+
unmatched.each { |t| puts " - #{t[0, 70]}" }
|
|
159
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Vendors fuzzdb-project/fuzzdb's attack/ folder into
|
|
4
|
+
# lib/evil_faker/data/groups/fuzzdb/. Only attack/ is vendored -- discovery/,
|
|
5
|
+
# wordlists-misc/, wordlists-user-passwd/, web-backdoors/, docs/ and regex/
|
|
6
|
+
# are recon wordlists/tooling, out of scope (same criterion used for every
|
|
7
|
+
# other source: adversarial field input, not discovery/credential wordlists).
|
|
8
|
+
#
|
|
9
|
+
# fuzzdb has no declared license (no LICENSE file, license: null via the
|
|
10
|
+
# GitHub API), same gap as IntruderPayloads -- vendored anyway per explicit
|
|
11
|
+
# call. See NOTICE.md.
|
|
12
|
+
#
|
|
13
|
+
# Heavy overlap with payloads_all_the_things/ is expected and accepted: that
|
|
14
|
+
# source's SQL/traversal/command lists were themselves copied from fuzzdb
|
|
15
|
+
# (several files share the exact same name, e.g. traversals-8-deep-exotic-
|
|
16
|
+
# encoding.txt). Base already dedupes within a merged subtype's file list
|
|
17
|
+
# isn't performed across files at read time, but each importer dedupes its
|
|
18
|
+
# own output, so cross-source duplicate lines just mean a slightly larger
|
|
19
|
+
# corpus, not broken data.
|
|
20
|
+
#
|
|
21
|
+
# attack/file-upload/malicious-images/ is skipped: real binary POC files
|
|
22
|
+
# (images, an EICAR test string file), not text payloads.
|
|
23
|
+
#
|
|
24
|
+
# attack/xss/JHADDIX_XSS_WITH_CONTEXT.doc.txt is skipped: it's prose
|
|
25
|
+
# ("Exploit Name: ... Exploit String: ... Exploit Description: ...") not
|
|
26
|
+
# one payload per line -- importing it raw would inject description text
|
|
27
|
+
# as if it were a payload.
|
|
28
|
+
#
|
|
29
|
+
# Requires the `gh` CLI, authenticated (`gh auth status`).
|
|
30
|
+
#
|
|
31
|
+
# Usage: ruby scripts/fuzzdb_build_corpus.rb
|
|
32
|
+
|
|
33
|
+
require "open3"
|
|
34
|
+
require "fileutils"
|
|
35
|
+
require "json"
|
|
36
|
+
|
|
37
|
+
ROOT = File.expand_path("..", __dir__)
|
|
38
|
+
OUT_DIR = File.join(ROOT, "lib/evil_faker/data/groups/fuzzdb")
|
|
39
|
+
REPO = "fuzzdb-project/fuzzdb"
|
|
40
|
+
|
|
41
|
+
SKIP_FILES = ["README.md", "Readme.md", "docs.http-method-defs.html", "JHADDIX_XSS_WITH_CONTEXT.doc.txt"].freeze
|
|
42
|
+
|
|
43
|
+
# upstream attack/ folder -> [EvilFaker namespace, subtype]. Multiple folders
|
|
44
|
+
# can feed the same subtype (they're concatenated).
|
|
45
|
+
SOURCES = {
|
|
46
|
+
"attack/sql-injection/detect" => %w[injection sql],
|
|
47
|
+
"attack/sql-injection/exploit" => %w[injection sql],
|
|
48
|
+
"attack/sql-injection/payloads-sql-blind" => %w[injection sql],
|
|
49
|
+
"attack/xss" => %w[injection script],
|
|
50
|
+
"attack/html_js_fuzz" => %w[injection script],
|
|
51
|
+
"attack/xml" => %w[injection xxe],
|
|
52
|
+
"attack/os-cmd-execution" => %w[injection command],
|
|
53
|
+
"attack/string-expansion" => %w[injection command],
|
|
54
|
+
"attack/lfi" => %w[injection file_inclusion],
|
|
55
|
+
"attack/path-traversal" => %w[injection directory_traversal],
|
|
56
|
+
"attack/redirect" => %w[injection open_redirect],
|
|
57
|
+
"attack/no-sql-injection" => %w[injection nosql],
|
|
58
|
+
"attack/ldap" => %w[injection ldap],
|
|
59
|
+
"attack/server-side-include" => %w[injection server_side_include],
|
|
60
|
+
"attack/all-attacks" => %w[injection generic],
|
|
61
|
+
"attack/xpath" => %w[injection xpath],
|
|
62
|
+
"attack/integer-overflow" => %w[injection integer_overflow],
|
|
63
|
+
"attack/rfi" => %w[injection rfi],
|
|
64
|
+
"attack/ip" => %w[injection ssrf],
|
|
65
|
+
"attack/json" => %w[injection json],
|
|
66
|
+
"attack/format-strings" => %w[injection format_strings],
|
|
67
|
+
"attack/authentication" => %w[injection magic_hashes],
|
|
68
|
+
"attack/disclosure-source" => %w[injection source_disclosure],
|
|
69
|
+
"attack/file-upload" => %w[injection file_upload_bypass],
|
|
70
|
+
"attack/control-chars" => %w[injection type_confusion],
|
|
71
|
+
"attack/business-logic" => %w[routes debug_params],
|
|
72
|
+
"attack/disclosure-directory" => %w[routes directory_indexing],
|
|
73
|
+
"attack/os-dir-indexing" => %w[routes directory_indexing],
|
|
74
|
+
"attack/disclosure-localpaths/unix" => %w[routes local_paths],
|
|
75
|
+
"attack/http-protocol" => %w[headers protocol],
|
|
76
|
+
"attack/mimetypes" => %w[headers mimetypes]
|
|
77
|
+
}.freeze
|
|
78
|
+
|
|
79
|
+
# folder -> plain filename (no subtype bucket), merged into existing flat/
|
|
80
|
+
# subtyped namespaces from other sources.
|
|
81
|
+
MERGE_SOURCES = {
|
|
82
|
+
"attack/control-chars/terminal-escape-codes.txt" => %w[terminal], # flat: EvilFaker::Terminal
|
|
83
|
+
"attack/control-chars/imessage.txt" => %w[exploits ios],
|
|
84
|
+
"attack/unicode/specialchars.txt" => %w[unicode special],
|
|
85
|
+
"attack/unicode/two-byte-chars.txt" => %w[unicode two_byte],
|
|
86
|
+
"attack/unicode/upsidedown.txt" => %w[unicode upsidedown],
|
|
87
|
+
"attack/unicode/corrupted.txt" => %w[unicode trick],
|
|
88
|
+
"attack/unicode/naughty-unicode.txt" => %w[unicode bad_chars],
|
|
89
|
+
"attack/unicode/emoji.txt" => %w[emoji emoji],
|
|
90
|
+
"attack/unicode/japanese-emoticon.txt" => %w[emoji emoticons],
|
|
91
|
+
"attack/unicode/regionalindicators.txt" => %w[emoji regional],
|
|
92
|
+
"attack/unicode/right-to-left.txt" => %w[rtl] # flat: EvilFaker::Rtl
|
|
93
|
+
}.freeze
|
|
94
|
+
|
|
95
|
+
# a NEW namespace, Faker-parity: valid/invalid edge-case emails instead of
|
|
96
|
+
# realistic ones.
|
|
97
|
+
EMAIL_SOURCES = {
|
|
98
|
+
"attack/email/valid-email-addresses.txt" => "valid",
|
|
99
|
+
"attack/email/invalid-email-addresses.txt" => "invalid"
|
|
100
|
+
}.freeze
|
|
101
|
+
|
|
102
|
+
def gh_json(path)
|
|
103
|
+
out, status = Open3.capture2("gh", "api", "repos/#{REPO}/contents/#{path}")
|
|
104
|
+
raise "gh api failed for #{path}" unless status.success?
|
|
105
|
+
|
|
106
|
+
JSON.parse(out)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def gh_raw(path)
|
|
110
|
+
out, status = Open3.capture2(
|
|
111
|
+
"gh", "api", "-H", "Accept: application/vnd.github.raw", "repos/#{REPO}/contents/#{path}"
|
|
112
|
+
)
|
|
113
|
+
raise "gh api failed for #{path}" unless status.success?
|
|
114
|
+
|
|
115
|
+
out
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def clean(lines)
|
|
119
|
+
lines.reject { |l| l.strip.empty? || l.start_with?("#") }.uniq
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def write_grouped(namespace, slug, lines)
|
|
123
|
+
FileUtils.mkdir_p(File.join(OUT_DIR, namespace))
|
|
124
|
+
File.write(File.join(OUT_DIR, namespace, "#{slug}.txt"), lines.join("\n") << "\n")
|
|
125
|
+
printf(" %<path>-28s -> %<count>5d lines\n", path: "#{namespace}/#{slug}", count: lines.size)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def write_flat(namespace, lines)
|
|
129
|
+
File.write(File.join(OUT_DIR, "#{namespace}.txt"), lines.join("\n") << "\n")
|
|
130
|
+
printf(" %<path>-28s -> %<count>5d lines\n", path: namespace, count: lines.size)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
FileUtils.mkdir_p(OUT_DIR)
|
|
134
|
+
|
|
135
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
|
136
|
+
|
|
137
|
+
SOURCES.each do |folder, (namespace, slug)|
|
|
138
|
+
files = gh_json(folder).select do |e|
|
|
139
|
+
e["type"] == "file" && e["name"].end_with?(".txt") && !SKIP_FILES.include?(e["name"])
|
|
140
|
+
end
|
|
141
|
+
buckets[[namespace, slug]].concat(files.flat_map { |e| gh_raw("#{folder}/#{e["name"]}").lines(chomp: true) })
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
MERGE_SOURCES.each do |path, dest|
|
|
145
|
+
lines = gh_raw(path).lines(chomp: true)
|
|
146
|
+
if dest.size == 1
|
|
147
|
+
buckets[[:flat, dest.first]].concat(lines)
|
|
148
|
+
else
|
|
149
|
+
buckets[dest].concat(lines)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
buckets.each do |key, lines|
|
|
154
|
+
if key.first == :flat
|
|
155
|
+
write_flat(key.last, clean(lines))
|
|
156
|
+
else
|
|
157
|
+
namespace, slug = key
|
|
158
|
+
write_grouped(namespace, slug, clean(lines))
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
EMAIL_SOURCES.each do |path, slug|
|
|
163
|
+
write_grouped("email", slug, clean(gh_raw(path).lines(chomp: true)))
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
puts "\nDone. Run scripts/generate_namespaces.rb next to wire up any new subtypes."
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Generates one Ruby class per namespace found under lib/evil_faker/data/groups/,
|
|
4
|
+
# with an explicit `subtype` line for each sub-group file it finds -- plus one
|
|
5
|
+
# class per source (e.g. EvilFaker::Blns), exposing that source's full corpus
|
|
6
|
+
# unsorted by category.
|
|
7
|
+
#
|
|
8
|
+
# Reads namespace/subtype/source names straight off the filesystem (not off
|
|
9
|
+
# any *_build_corpus.rb script), so it works no matter which source(s)
|
|
10
|
+
# populated data/groups -- a namespace is whatever top-level entry (file or
|
|
11
|
+
# folder) exists under any groups/<source>/, and its subtypes are whatever
|
|
12
|
+
# .txt files live inside it.
|
|
13
|
+
#
|
|
14
|
+
# Usage: ruby scripts/generate_namespaces.rb
|
|
15
|
+
# Run after any of the scripts/*_build_corpus.rb importers.
|
|
16
|
+
|
|
17
|
+
require "fileutils"
|
|
18
|
+
|
|
19
|
+
ROOT = File.expand_path("..", __dir__)
|
|
20
|
+
GROUPS_ROOT = File.join(ROOT, "lib/evil_faker/data/groups")
|
|
21
|
+
OUT_DIR = File.join(ROOT, "lib/evil_faker/groups")
|
|
22
|
+
SOURCES_OUT_DIR = File.join(ROOT, "lib/evil_faker/sources")
|
|
23
|
+
|
|
24
|
+
def camelize(name)
|
|
25
|
+
name.split("_").map(&:capitalize).join
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# namespace name -> subtype names (empty for a flat, single-file namespace)
|
|
29
|
+
namespace_subtypes = Hash.new { |h, k| h[k] = [] }
|
|
30
|
+
|
|
31
|
+
Dir.children(GROUPS_ROOT).sort.each do |source|
|
|
32
|
+
source_dir = File.join(GROUPS_ROOT, source)
|
|
33
|
+
|
|
34
|
+
Dir.children(source_dir).sort.each do |entry|
|
|
35
|
+
path = File.join(source_dir, entry)
|
|
36
|
+
|
|
37
|
+
if File.directory?(path)
|
|
38
|
+
ns = entry
|
|
39
|
+
subtypes = Dir.children(path).select { |f| f.end_with?(".txt") }.map { |f| File.basename(f, ".txt") }
|
|
40
|
+
namespace_subtypes[ns].concat(subtypes)
|
|
41
|
+
else
|
|
42
|
+
namespace_subtypes[File.basename(entry, ".txt")] # touch the key, no subtypes
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
FileUtils.rm_rf(OUT_DIR)
|
|
48
|
+
FileUtils.mkdir_p(OUT_DIR)
|
|
49
|
+
|
|
50
|
+
namespace_subtypes.sort.each do |ns, subtypes|
|
|
51
|
+
subtype_lines = subtypes.uniq.sort.map { |s| " subtype :#{s}\n" }.join
|
|
52
|
+
|
|
53
|
+
File.write(File.join(OUT_DIR, "#{ns}.rb"), <<~RUBY)
|
|
54
|
+
# frozen_string_literal: true
|
|
55
|
+
|
|
56
|
+
module EvilFaker
|
|
57
|
+
# Generated by scripts/generate_namespaces.rb -- do not edit by hand.
|
|
58
|
+
class #{camelize(ns)} < Base
|
|
59
|
+
group "#{ns}"
|
|
60
|
+
#{subtype_lines.empty? ? "" : "\n#{subtype_lines}"} end
|
|
61
|
+
end
|
|
62
|
+
RUBY
|
|
63
|
+
puts " #{ns}.rb#{subtypes.empty? ? "" : " (#{subtypes.uniq.size} subtypes)"}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
puts "\nGenerated #{namespace_subtypes.size} namespace classes in lib/evil_faker/groups/"
|
|
67
|
+
|
|
68
|
+
FileUtils.rm_rf(SOURCES_OUT_DIR)
|
|
69
|
+
FileUtils.mkdir_p(SOURCES_OUT_DIR)
|
|
70
|
+
|
|
71
|
+
sources = Dir.children(GROUPS_ROOT).sort
|
|
72
|
+
|
|
73
|
+
sources.each do |source|
|
|
74
|
+
File.write(File.join(SOURCES_OUT_DIR, "#{source}.rb"), <<~RUBY)
|
|
75
|
+
# frozen_string_literal: true
|
|
76
|
+
|
|
77
|
+
module EvilFaker
|
|
78
|
+
# Generated by scripts/generate_namespaces.rb -- do not edit by hand.
|
|
79
|
+
class #{camelize(source)} < SourceBase
|
|
80
|
+
source "#{source}"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
RUBY
|
|
84
|
+
puts " #{source}.rb"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
puts "\nGenerated #{sources.size} source classes in lib/evil_faker/sources/"
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Vendors a curated subset of 1N3/IntruderPayloads (MIT) into
|
|
4
|
+
# lib/evil_faker/data/groups/intruder_payloads/.
|
|
5
|
+
#
|
|
6
|
+
# Skipped entirely: BurpAttacks/ (binary .burp ZIP exports, not text),
|
|
7
|
+
# Plugins/ (.jar files), BurpBountyPayloads/ (JSON scanner-rule configs with
|
|
8
|
+
# only 1-2 payloads each behind a lot of scanner metadata -- not worth a
|
|
9
|
+
# parser for that little yield).
|
|
10
|
+
#
|
|
11
|
+
# Many FuzzLists/*.txt lines aren't standalone strings: they use the literal
|
|
12
|
+
# token INJECTX as a placeholder where Burp Intruder would splice in a
|
|
13
|
+
# payload from a *second* list. Those lines are meaningless out of context,
|
|
14
|
+
# so every line containing "injectx" (case-insensitive) is dropped. Three
|
|
15
|
+
# files are almost entirely such templates (xss_payloads_quick.txt,
|
|
16
|
+
# xss_remote_payloads-http(s).txt) and are skipped outright, as are
|
|
17
|
+
# grep_injectx.txt / payload_injectx.txt (pure template lists).
|
|
18
|
+
#
|
|
19
|
+
# Also skipped: dirbuster-*.txt / toplist-sorted.txt / passwords_*.txt /
|
|
20
|
+
# usernames.txt / vulnerability_discovery.txt (recon/credential wordlists,
|
|
21
|
+
# not adversarial field input) and url_payloads.txt (the author's own
|
|
22
|
+
# callback domains, not generically reusable).
|
|
23
|
+
#
|
|
24
|
+
# Uploads/ is different: the malicious payload IS the filename (e.g.
|
|
25
|
+
# `shell.php%00.jpg`), not file content -- so its entries are read from the
|
|
26
|
+
# directory listing itself and merged into the flat EvilFaker::Filenames group.
|
|
27
|
+
#
|
|
28
|
+
# Requires the `gh` CLI, authenticated (`gh auth status`).
|
|
29
|
+
#
|
|
30
|
+
# Usage: ruby scripts/intruder_payloads_build_corpus.rb
|
|
31
|
+
|
|
32
|
+
require "open3"
|
|
33
|
+
require "fileutils"
|
|
34
|
+
require "json"
|
|
35
|
+
|
|
36
|
+
ROOT = File.expand_path("..", __dir__)
|
|
37
|
+
OUT_DIR = File.join(ROOT, "lib/evil_faker/data/groups/intruder_payloads")
|
|
38
|
+
REPO = "1N3/IntruderPayloads"
|
|
39
|
+
|
|
40
|
+
# FuzzLists/<file> -> [EvilFaker namespace, subtype], merged file-by-file
|
|
41
|
+
# (unlike PayloadsAllTheThings, sources here are individual files, not
|
|
42
|
+
# whole folders).
|
|
43
|
+
FUZZLIST_SOURCES = {
|
|
44
|
+
"sqli-error-based.txt" => %w[injection sql],
|
|
45
|
+
"sqli-time-based.txt" => %w[injection sql],
|
|
46
|
+
"sqli-union-select.txt" => %w[injection sql],
|
|
47
|
+
"sqli_escape_chars.txt" => %w[injection sql],
|
|
48
|
+
"auth_bypass.txt" => %w[injection sql],
|
|
49
|
+
"xss_escape_chars.txt" => %w[injection script],
|
|
50
|
+
"xss_find_inject.txt" => %w[injection script],
|
|
51
|
+
"xss_funny_stored.txt" => %w[injection script],
|
|
52
|
+
"xss_grep.txt" => %w[injection script],
|
|
53
|
+
"xss_swf_fuzz.txt" => %w[injection script],
|
|
54
|
+
"lfi.txt" => %w[injection file_inclusion],
|
|
55
|
+
"traversal.txt" => %w[injection directory_traversal],
|
|
56
|
+
"traversal-short.txt" => %w[injection directory_traversal],
|
|
57
|
+
"xml-attacks.txt" => %w[injection xxe],
|
|
58
|
+
"command_exec.txt" => %w[injection command],
|
|
59
|
+
"ssi_quick.txt" => %w[injection server_side_include],
|
|
60
|
+
"overflow.txt" => %w[injection overflow],
|
|
61
|
+
"overflow-dos.txt" => %w[injection overflow],
|
|
62
|
+
"bad_chars.txt" => %w[unicode bad_chars],
|
|
63
|
+
"basic_fuzz.txt" => %w[injection generic],
|
|
64
|
+
"full_fuzz.txt" => %w[injection generic],
|
|
65
|
+
"quick_fuzz.txt" => %w[injection generic]
|
|
66
|
+
}.freeze
|
|
67
|
+
|
|
68
|
+
def gh_json(path)
|
|
69
|
+
out, status = Open3.capture2("gh", "api", "repos/#{REPO}/contents/#{path}")
|
|
70
|
+
raise "gh api failed for #{path}" unless status.success?
|
|
71
|
+
|
|
72
|
+
JSON.parse(out)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def gh_raw(path)
|
|
76
|
+
out, status = Open3.capture2(
|
|
77
|
+
"gh", "api", "-H", "Accept: application/vnd.github.raw", "repos/#{REPO}/contents/#{path}"
|
|
78
|
+
)
|
|
79
|
+
raise "gh api failed for #{path}" unless status.success?
|
|
80
|
+
|
|
81
|
+
out
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def clean(lines)
|
|
85
|
+
lines.reject { |l| l.strip.empty? || l.start_with?("#") || l.match?(/injectx/i) }.uniq
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def write(namespace, slug, lines)
|
|
89
|
+
FileUtils.mkdir_p(File.join(OUT_DIR, namespace))
|
|
90
|
+
File.write(File.join(OUT_DIR, namespace, "#{slug}.txt"), lines.join("\n") << "\n")
|
|
91
|
+
printf(" %<path>-28s -> %<count>5d lines\n", path: "#{namespace}/#{slug}", count: lines.size)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Flat namespace (EvilFaker::Filenames has no subtypes) -> file goes straight at
|
|
95
|
+
# groups/intruder_payloads/<namespace>.txt, not nested in a subtype folder.
|
|
96
|
+
def write_flat(namespace, lines)
|
|
97
|
+
File.write(File.join(OUT_DIR, "#{namespace}.txt"), lines.join("\n") << "\n")
|
|
98
|
+
printf(" %<path>-28s -> %<count>5d lines\n", path: namespace, count: lines.size)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
FileUtils.mkdir_p(OUT_DIR)
|
|
102
|
+
|
|
103
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
|
104
|
+
FUZZLIST_SOURCES.each do |file, (namespace, slug)|
|
|
105
|
+
buckets[[namespace, slug]].concat(gh_raw("FuzzLists/#{file}").lines(chomp: true))
|
|
106
|
+
end
|
|
107
|
+
buckets.each { |(namespace, slug), lines| write(namespace, slug, clean(lines)) }
|
|
108
|
+
|
|
109
|
+
upload_names = gh_json("Uploads").select { |e| e["type"] == "file" }.map { |e| e["name"] }
|
|
110
|
+
write_flat("filenames", upload_names.reject(&:empty?).uniq)
|
|
111
|
+
|
|
112
|
+
puts "\nDone. Run scripts/generate_namespaces.rb next to wire up any new subtypes."
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Vendors a curated subset of swisskyrepo/PayloadsAllTheThings (MIT) into
|
|
4
|
+
# lib/evil_faker/data/groups/payloads_all_the_things/.
|
|
5
|
+
#
|
|
6
|
+
# Only folders that ship a real Intruder/Intruders directory of plain .txt
|
|
7
|
+
# payloads are pulled in -- most of the repo's ~60 top-level folders are
|
|
8
|
+
# Markdown write-ups with no payload list to vendor, so this is a hand-picked
|
|
9
|
+
# allowlist, not a full mirror. Re-run to refresh; each namespace/slug's file
|
|
10
|
+
# is fully replaced from upstream every time (dedup + drop blank/comment lines).
|
|
11
|
+
#
|
|
12
|
+
# Requires the `gh` CLI, authenticated (`gh auth status`).
|
|
13
|
+
#
|
|
14
|
+
# Usage: ruby scripts/payloads_all_the_things_build_corpus.rb
|
|
15
|
+
|
|
16
|
+
require "open3"
|
|
17
|
+
require "fileutils"
|
|
18
|
+
|
|
19
|
+
ROOT = File.expand_path("..", __dir__)
|
|
20
|
+
OUT_DIR = File.join(ROOT, "lib/evil_faker/data/groups/payloads_all_the_things")
|
|
21
|
+
REPO = "swisskyrepo/PayloadsAllTheThings"
|
|
22
|
+
|
|
23
|
+
# upstream folder (with its Intruder/Intruders dir) -> [EvilFaker namespace, subtype]
|
|
24
|
+
SOURCES = {
|
|
25
|
+
"SQL Injection/Intruder" => %w[injection sql],
|
|
26
|
+
"NoSQL Injection/Intruder" => %w[injection nosql],
|
|
27
|
+
"LDAP Injection/Intruder" => %w[injection ldap],
|
|
28
|
+
"XSS Injection/Intruders" => %w[injection script],
|
|
29
|
+
"XXE Injection/Intruders" => %w[injection xxe],
|
|
30
|
+
"Server Side Template Injection/Intruder" => %w[injection ssti],
|
|
31
|
+
"Command Injection/Intruder" => %w[injection command],
|
|
32
|
+
"File Inclusion/Intruders" => %w[injection file_inclusion],
|
|
33
|
+
"Directory Traversal/Intruder" => %w[injection directory_traversal],
|
|
34
|
+
"Open Redirect/Intruder" => %w[injection open_redirect],
|
|
35
|
+
# Not injection payloads (single field strings) -- these are wordlists, so
|
|
36
|
+
# they get their own namespaces instead of forcing them into Injection.
|
|
37
|
+
"Insecure Management Interface/Intruder" => %w[routes admin_interfaces],
|
|
38
|
+
"Web Cache Deception/Intruders" => %w[headers cache_deception]
|
|
39
|
+
}.freeze
|
|
40
|
+
|
|
41
|
+
def gh_json(path)
|
|
42
|
+
out, status = Open3.capture2("gh", "api", "repos/#{REPO}/contents/#{path}")
|
|
43
|
+
raise "gh api failed for #{path}" unless status.success?
|
|
44
|
+
|
|
45
|
+
require "json"
|
|
46
|
+
JSON.parse(out)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def gh_raw(path)
|
|
50
|
+
out, status = Open3.capture2(
|
|
51
|
+
"gh", "api", "-H", "Accept: application/vnd.github.raw", "repos/#{REPO}/contents/#{path}"
|
|
52
|
+
)
|
|
53
|
+
raise "gh api failed for #{path}" unless status.success?
|
|
54
|
+
|
|
55
|
+
out
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def clean(lines)
|
|
59
|
+
lines.reject { |l| l.strip.empty? || l.start_with?("#") }.uniq
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def write(namespace, slug, lines)
|
|
63
|
+
FileUtils.mkdir_p(File.join(OUT_DIR, namespace))
|
|
64
|
+
File.write(File.join(OUT_DIR, namespace, "#{slug}.txt"), lines.join("\n") << "\n")
|
|
65
|
+
printf(" %<path>-28s -> %<count>5d lines\n", path: "#{namespace}/#{slug}", count: lines.size)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
SOURCES.each do |folder, (namespace, slug)|
|
|
69
|
+
entries = gh_json(folder).select { |e| e["type"] == "file" }
|
|
70
|
+
lines = entries.flat_map { |e| gh_raw("#{folder}/#{e["name"]}").lines(chomp: true) }
|
|
71
|
+
write(namespace, slug, clean(lines))
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
puts "\nDone. Run scripts/generate_namespaces.rb next to wire up any new subtypes."
|
data/sig/evil_faker.rbs
ADDED