typosquatting 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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/LICENSE +21 -0
  5. data/README.md +218 -0
  6. data/Rakefile +8 -0
  7. data/exe/typosquatting +6 -0
  8. data/lib/typosquatting/algorithms/addition.rb +20 -0
  9. data/lib/typosquatting/algorithms/base.rb +34 -0
  10. data/lib/typosquatting/algorithms/delimiter.rb +48 -0
  11. data/lib/typosquatting/algorithms/homoglyph.rb +61 -0
  12. data/lib/typosquatting/algorithms/misspelling.rb +78 -0
  13. data/lib/typosquatting/algorithms/numeral.rb +45 -0
  14. data/lib/typosquatting/algorithms/omission.rb +16 -0
  15. data/lib/typosquatting/algorithms/plural.rb +74 -0
  16. data/lib/typosquatting/algorithms/repetition.rb +16 -0
  17. data/lib/typosquatting/algorithms/replacement.rb +59 -0
  18. data/lib/typosquatting/algorithms/transposition.rb +17 -0
  19. data/lib/typosquatting/algorithms/vowel_swap.rb +27 -0
  20. data/lib/typosquatting/algorithms/word_order.rb +25 -0
  21. data/lib/typosquatting/cli.rb +380 -0
  22. data/lib/typosquatting/confusion.rb +70 -0
  23. data/lib/typosquatting/ecosystems/base.rb +65 -0
  24. data/lib/typosquatting/ecosystems/cargo.rb +45 -0
  25. data/lib/typosquatting/ecosystems/composer.rb +64 -0
  26. data/lib/typosquatting/ecosystems/golang.rb +56 -0
  27. data/lib/typosquatting/ecosystems/hex.rb +42 -0
  28. data/lib/typosquatting/ecosystems/maven.rb +64 -0
  29. data/lib/typosquatting/ecosystems/npm.rb +66 -0
  30. data/lib/typosquatting/ecosystems/nuget.rb +41 -0
  31. data/lib/typosquatting/ecosystems/pub.rb +43 -0
  32. data/lib/typosquatting/ecosystems/pypi.rb +38 -0
  33. data/lib/typosquatting/ecosystems/rubygems.rb +42 -0
  34. data/lib/typosquatting/generator.rb +58 -0
  35. data/lib/typosquatting/lookup.rb +138 -0
  36. data/lib/typosquatting/sbom.rb +98 -0
  37. data/lib/typosquatting/version.rb +5 -0
  38. data/lib/typosquatting.rb +103 -0
  39. data/sig/typosquatting.rbs +4 -0
  40. metadata +114 -0
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sbom"
4
+ require "purl"
5
+
6
+ module Typosquatting
7
+ class SBOMChecker
8
+ attr_reader :sbom
9
+
10
+ def initialize(file_path)
11
+ @sbom = Sbom::Parser.new.parse_file(file_path)
12
+ end
13
+
14
+ def check
15
+ results = []
16
+
17
+ sbom.packages.each do |pkg|
18
+ purl_string = extract_purl(pkg)
19
+ next unless purl_string
20
+
21
+ begin
22
+ purl = Purl.parse(purl_string)
23
+ rescue StandardError
24
+ next
25
+ end
26
+
27
+ begin
28
+ ecosystem = Ecosystems::Base.get(purl.type)
29
+ rescue ArgumentError
30
+ next
31
+ end
32
+
33
+ package_name = purl.namespace ? "#{purl.namespace}/#{purl.name}" : purl.name
34
+ suspicions = find_typosquat_matches(package_name, ecosystem)
35
+ next if suspicions.empty?
36
+
37
+ results << SBOMResult.new(
38
+ name: package_name,
39
+ version: purl.version,
40
+ ecosystem: purl.type,
41
+ purl: purl_string,
42
+ suspicions: suspicions
43
+ )
44
+ end
45
+
46
+ results
47
+ end
48
+
49
+ SBOMResult = Struct.new(:name, :version, :ecosystem, :purl, :suspicions, keyword_init: true) do
50
+ def to_h
51
+ {
52
+ name: name,
53
+ version: version,
54
+ ecosystem: ecosystem,
55
+ purl: purl,
56
+ similar_to: suspicions.map(&:to_h)
57
+ }
58
+ end
59
+ end
60
+
61
+ Suspicion = Struct.new(:name, :algorithm, :registries, keyword_init: true) do
62
+ def to_h
63
+ {
64
+ name: name,
65
+ algorithm: algorithm,
66
+ registries: registries
67
+ }
68
+ end
69
+ end
70
+
71
+ def extract_purl(package)
72
+ refs = package[:external_references] || []
73
+ purl_ref = refs.find { |r| r[1] == "purl" }
74
+ purl_ref&.[](2)
75
+ end
76
+
77
+ def find_typosquat_matches(package_name, ecosystem)
78
+ generator = Generator.new(ecosystem: ecosystem)
79
+ lookup = Lookup.new(ecosystem: ecosystem)
80
+
81
+ variants = generator.generate(package_name)
82
+ suspicions = []
83
+
84
+ variants.each do |variant|
85
+ result = lookup.check(variant.name)
86
+ next unless result.exists?
87
+
88
+ suspicions << Suspicion.new(
89
+ name: variant.name,
90
+ algorithm: variant.algorithm,
91
+ registries: result.registries
92
+ )
93
+ end
94
+
95
+ suspicions
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Typosquatting
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "typosquatting/version"
4
+
5
+ require_relative "typosquatting/algorithms/base"
6
+ require_relative "typosquatting/algorithms/omission"
7
+ require_relative "typosquatting/algorithms/repetition"
8
+ require_relative "typosquatting/algorithms/replacement"
9
+ require_relative "typosquatting/algorithms/transposition"
10
+ require_relative "typosquatting/algorithms/addition"
11
+ require_relative "typosquatting/algorithms/homoglyph"
12
+ require_relative "typosquatting/algorithms/vowel_swap"
13
+ require_relative "typosquatting/algorithms/delimiter"
14
+ require_relative "typosquatting/algorithms/word_order"
15
+ require_relative "typosquatting/algorithms/plural"
16
+ require_relative "typosquatting/algorithms/misspelling"
17
+ require_relative "typosquatting/algorithms/numeral"
18
+
19
+ require_relative "typosquatting/ecosystems/base"
20
+ require_relative "typosquatting/ecosystems/pypi"
21
+ require_relative "typosquatting/ecosystems/npm"
22
+ require_relative "typosquatting/ecosystems/rubygems"
23
+ require_relative "typosquatting/ecosystems/cargo"
24
+ require_relative "typosquatting/ecosystems/golang"
25
+ require_relative "typosquatting/ecosystems/maven"
26
+ require_relative "typosquatting/ecosystems/nuget"
27
+ require_relative "typosquatting/ecosystems/composer"
28
+ require_relative "typosquatting/ecosystems/hex"
29
+ require_relative "typosquatting/ecosystems/pub"
30
+
31
+ require_relative "typosquatting/generator"
32
+ require_relative "typosquatting/lookup"
33
+ require_relative "typosquatting/confusion"
34
+ require_relative "typosquatting/sbom"
35
+ require_relative "typosquatting/cli"
36
+
37
+ module Typosquatting
38
+ class Error < StandardError; end
39
+
40
+ class << self
41
+ def generate(package_name, ecosystem:)
42
+ generator = Generator.new(ecosystem: ecosystem)
43
+ generator.generate(package_name).map(&:name)
44
+ end
45
+
46
+ def generate_with_algorithms(package_name, ecosystem:)
47
+ generator = Generator.new(ecosystem: ecosystem)
48
+ generator.generate(package_name)
49
+ end
50
+
51
+ def check(package_name, ecosystem:)
52
+ generator = Generator.new(ecosystem: ecosystem)
53
+ variants = generator.generate(package_name)
54
+
55
+ lookup = Lookup.new(ecosystem: ecosystem)
56
+ variants.map do |variant|
57
+ result = lookup.check(variant.name)
58
+ CheckResult.new(
59
+ name: variant.name,
60
+ algorithm: variant.algorithm,
61
+ exists: result.exists?,
62
+ registries: result.packages.map { |p| RegistryInfo.new(p.dig("registry", "name"), p.dig("registry", "url")) }
63
+ )
64
+ end
65
+ end
66
+
67
+ def check_confusion(package_name, ecosystem:)
68
+ confusion = Confusion.new(ecosystem: ecosystem)
69
+ confusion.check(package_name)
70
+ end
71
+ end
72
+
73
+ CheckResult = Struct.new(:name, :algorithm, :exists, :registries, keyword_init: true) do
74
+ def exists?
75
+ exists
76
+ end
77
+
78
+ def to_h
79
+ {
80
+ name: name,
81
+ algorithm: algorithm,
82
+ exists: exists?,
83
+ registries: registries.map(&:to_h)
84
+ }
85
+ end
86
+ end
87
+
88
+ RegistryInfo = Struct.new(:name, :url) do
89
+ def to_h
90
+ { name: name, url: url }
91
+ end
92
+ end
93
+
94
+ module Ecosystem
95
+ def self.get(name)
96
+ Ecosystems::Base.get(name)
97
+ end
98
+
99
+ def self.all
100
+ Ecosystems::Base.all
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,4 @@
1
+ module Typosquatting
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: typosquatting
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Nesbitt
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: sbom
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.2'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.2'
26
+ - !ruby/object:Gem::Dependency
27
+ name: purl
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.6'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.6'
40
+ description: Generate typosquat variants of package names and check if they exist
41
+ on package registries. Supports PyPI, npm, RubyGems, Cargo, Go, Maven, NuGet, Composer,
42
+ Hex, and Pub.
43
+ email:
44
+ - andrewnez@gmail.com
45
+ executables:
46
+ - typosquatting
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - CHANGELOG.md
51
+ - CODE_OF_CONDUCT.md
52
+ - LICENSE
53
+ - README.md
54
+ - Rakefile
55
+ - exe/typosquatting
56
+ - lib/typosquatting.rb
57
+ - lib/typosquatting/algorithms/addition.rb
58
+ - lib/typosquatting/algorithms/base.rb
59
+ - lib/typosquatting/algorithms/delimiter.rb
60
+ - lib/typosquatting/algorithms/homoglyph.rb
61
+ - lib/typosquatting/algorithms/misspelling.rb
62
+ - lib/typosquatting/algorithms/numeral.rb
63
+ - lib/typosquatting/algorithms/omission.rb
64
+ - lib/typosquatting/algorithms/plural.rb
65
+ - lib/typosquatting/algorithms/repetition.rb
66
+ - lib/typosquatting/algorithms/replacement.rb
67
+ - lib/typosquatting/algorithms/transposition.rb
68
+ - lib/typosquatting/algorithms/vowel_swap.rb
69
+ - lib/typosquatting/algorithms/word_order.rb
70
+ - lib/typosquatting/cli.rb
71
+ - lib/typosquatting/confusion.rb
72
+ - lib/typosquatting/ecosystems/base.rb
73
+ - lib/typosquatting/ecosystems/cargo.rb
74
+ - lib/typosquatting/ecosystems/composer.rb
75
+ - lib/typosquatting/ecosystems/golang.rb
76
+ - lib/typosquatting/ecosystems/hex.rb
77
+ - lib/typosquatting/ecosystems/maven.rb
78
+ - lib/typosquatting/ecosystems/npm.rb
79
+ - lib/typosquatting/ecosystems/nuget.rb
80
+ - lib/typosquatting/ecosystems/pub.rb
81
+ - lib/typosquatting/ecosystems/pypi.rb
82
+ - lib/typosquatting/ecosystems/rubygems.rb
83
+ - lib/typosquatting/generator.rb
84
+ - lib/typosquatting/lookup.rb
85
+ - lib/typosquatting/sbom.rb
86
+ - lib/typosquatting/version.rb
87
+ - sig/typosquatting.rbs
88
+ homepage: https://github.com/andrew/typosquatting
89
+ licenses:
90
+ - MIT
91
+ metadata:
92
+ allowed_push_host: https://rubygems.org
93
+ homepage_uri: https://github.com/andrew/typosquatting
94
+ source_code_uri: https://github.com/andrew/typosquatting
95
+ changelog_uri: https://github.com/andrew/typosquatting/blob/main/CHANGELOG.md
96
+ rubygems_mfa_required: 'true'
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: 3.2.0
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubygems_version: 4.0.1
112
+ specification_version: 4
113
+ summary: Detect potential typosquatting packages across package ecosystems
114
+ test_files: []