Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 13
- Log:
Initial import of Typo 2.6.0 sources from a downloaded Tarball.
Typo is a Ruby On Rails based blog engine.
- Author:
- adh
- Date:
- Sat Jul 22 22:25:02 +0100 2006
- Size:
- 3961 Bytes
1 | require_dependency 'blacklist_pattern' |
2 | |
3 | class SpamProtection |
4 | |
5 | IP_RBL = [ 'opm.blitzed.us', 'bsb.empty.us' ] |
6 | HOST_RBL = [ 'sc.surbl.org', 'bsb.empty.us' ] |
7 | SECOND_LEVEL = [ 'co', 'com', 'net', 'org', 'gov' ] |
8 | |
9 | def article_closed?(record) |
10 | if config['sp_article_auto_close'] > 0 |
11 | if record.article.created_at.to_i < config['sp_article_auto_close'].days.ago.to_i |
12 | logger.info("[SP] Blocked interaction with #{record.article.title}") |
13 | return true |
14 | end |
15 | end |
16 | end |
17 | |
18 | def is_spam?(string) |
19 | return false unless config['sp_global'] |
20 | return false if string.blank? |
21 | |
22 | reason = catch(:hit) do |
23 | case string |
24 | when Format::IP_ADDRESS: self.scan_ip(string) |
25 | when Format::HTTP_URI: self.scan_uri(URI.parse(string).host) rescue URI::InvalidURIError |
26 | else self.scan_text(string) |
27 | end |
28 | end |
29 | |
30 | if reason |
31 | logger.info("[SP] Hit: #{reason}") |
32 | return true |
33 | end |
34 | end |
35 | |
36 | protected |
37 | |
38 | def scan_ip(ip_address) |
39 | logger.info("[SP] Scanning IP #{ip_address}") |
40 | |
41 | IP_RBL.each do |rbl| |
42 | begin |
43 | if IPSocket.getaddress((ip_address.split('.').reverse + [rbl]).join('.')) == "127.0.0.2" |
44 | throw :hit, "#{rbl} positively resolved #{ip_address}" |
45 | end |
46 | rescue SocketError |
47 | end |
48 | end |
49 | |
50 | return false |
51 | end |
52 | |
53 | def scan_text(string) |
54 | # Scan contained URLs |
55 | uri_list = string.scan(/(http:\/\/[^\s"]+)/m).flatten |
56 | |
57 | # Check for URL count limit |
58 | if config['sp_url_limit'] > 0 |
59 | throw :hit, "Hard URL Limit hit: #{uri_list.size} > #{config['sp_url_limit']}" if uri_list.size > config['sp_url_limit'] |
60 | end |
61 | |
62 | uri_list.collect { |uri| URI.parse(uri).host rescue nil }.uniq.compact.each do |host| |
63 | scan_uri(host) |
64 | end |
65 | |
66 | # Pattern scanning |
67 | BlacklistPattern.find_all.each do |pattern| |
68 | logger.info("[SP] Scanning for #{pattern.class} #{pattern.pattern}") |
69 | |
70 | if pattern.kind_of?(RegexPattern) |
71 | throw :hit, "Regex #{pattern.pattern} matched" if string.match(/#{pattern.pattern}/) |
72 | else |
73 | throw :hit, "String #{pattern.pattern} matched" if string.match(/\b#{Regexp.quote(pattern.pattern)}\b/) |
74 | end |
75 | end |
76 | |
77 | return false |
78 | end |
79 | |
80 | def scan_uri(host) |
81 | return scan_ip(host) if host =~ Format::IP_ADDRESS |
82 | |
83 | host_parts = host.split('.').reverse |
84 | domain = Array.new |
85 | |
86 | |
87 | # Check for two level TLD |
88 | (SECOND_LEVEL.include?(host_parts[1]) ? 3:2).times do |
89 | domain.unshift(host_parts.shift) |
90 | end |
91 | |
92 | logger.info("[SP] Scanning domain #{domain.join('.')}") |
93 | |
94 | HOST_RBL.each do |rbl| |
95 | begin |
96 | if [ |
97 | IPSocket.getaddress([host, rbl].join('.')), |
98 | IPSocket.getaddress((domain + [rbl]).join('.')) |
99 | ].include?("127.0.0.2") |
100 | throw :hit, "#{rbl} positively resolved #{domain.join('.')}" |
101 | end |
102 | rescue SocketError |
103 | end |
104 | end |
105 | |
106 | return false |
107 | end |
108 | |
109 | def logger |
110 | @logger ||= RAILS_DEFAULT_LOGGER || Logger.new(STDOUT) |
111 | end |
112 | end |
113 | |
114 | module ActiveRecord |
115 | module Validations |
116 | module ClassMethods |
117 | def validates_against_spamdb(*attr_names) |
118 | configuration = { :message => "blocked by SpamProtection" } |
119 | configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash) |
120 | |
121 | validates_each(attr_names, configuration) do |record, attr_name, value| |
122 | record.errors.add(attr_name, configuration[:message]) if SpamProtection.new.is_spam?(value) |
123 | end |
124 | end |
125 | def validates_age_of(*attr_names) |
126 | configuration = { :message => "points to an item that is no longer available for interaction"} |
127 | configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash) |
128 | |
129 | validates_each(attr_names, configuration) do |record, attr_name, value| |
130 | next unless value.to_i > 0 |
131 | record.errors.add(attr_name, configuration[:message]) if SpamProtection.new.article_closed?(record) |
132 | end |
133 | end |
134 | end |
135 | end |
136 | end |