Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
saizai committed Feb 2, 2010
0 parents commit cd9e966
Show file tree
Hide file tree
Showing 195 changed files with 1,017,071 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .gitignore
@@ -0,0 +1,14 @@
.DS_Store
log/*.log
log/*.pid
tmp/**/*
config/database.yml
db/*.sqlite3
db/*.db
db/schema.rb
config/initializers/*_keys.rb
.tmp*
public/files/
public/javascripts/all.js
public/stylesheets/all.css
config/mail.yml
12 changes: 12 additions & 0 deletions .loadpath
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<loadpath>
<pathentry path="" type="src"/>
<pathentry path="org.rubypeople.rdt.launching.RUBY_CONTAINER" type="con"/>
<pathentry path="GEM_LIB/rake-0.8.4/lib" type="var"/>
<pathentry path="GEM_LIB/actionmailer-2.3.5/lib" type="var"/>
<pathentry path="GEM_LIB/actionpack-2.3.5/lib" type="var"/>
<pathentry path="GEM_LIB/activeresource-2.3.5/lib" type="var"/>
<pathentry path="GEM_LIB/activerecord-2.3.5/lib" type="var"/>
<pathentry path="GEM_LIB/rails-2.3.5/lib" type="var"/>
<pathentry path="GEM_LIB/activesupport-2.3.5/lib" type="var"/>
</loadpath>
18 changes: 18 additions & 0 deletions .project
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>historyprint</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.rubypeople.rdt.core.rubybuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.rubypeople.rdt.core.rubynature</nature>
<nature>org.radrails.rails.core.railsnature</nature>
</natures>
</projectDescription>
5 changes: 5 additions & 0 deletions README
@@ -0,0 +1,5 @@
CSS Fingerprint is a research project inspired by the EFF's Panopticlick.

Its intent is to see how well the CSS history hack can be used with AI techniques (like an SVM) to uniquely fingerprint users despite changes in their browsing history, even on new computers or new browsers.

At the moment, the AI component is not yet active, pending data collected from preliminary testing.
10 changes: 10 additions & 0 deletions Rakefile
@@ -0,0 +1,10 @@
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

require(File.join(File.dirname(__FILE__), 'config', 'boot'))

require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'

require 'tasks/rails'
23 changes: 23 additions & 0 deletions app/controllers/application_controller.rb
@@ -0,0 +1,23 @@
# Filters added to this controller apply to all controllers in the application.
# Likewise, all the methods added will be available for all controllers.

class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
protect_from_forgery # See ActionController::RequestForgeryProtection for details

# Scrub sensitive parameters from your log
# filter_parameter_logging :password

after_filter OutputCompressionFilter # this is a workaround for lack of Apache mod_deflate. If mod_deflate is installed, remove this.

before_filter :get_set_cookie

def get_set_cookie
last_cookie = cookies[:remember_token]
if last_cookie
@current_user = User.find_by_cookie(last_cookie) rescue nil
else
cookies[:remember_token] = ActiveSupport::SecureRandom.hex(30)
end
end
end
14 changes: 14 additions & 0 deletions app/controllers/scrapings_controller.rb
@@ -0,0 +1,14 @@
class ScrapingsController < ApplicationController
def create
@user = User.find_or_create_by_cookie(params[:cookie])
cookies[:remember_token] = params[:cookie]
@scraping = @user.scrapings.create
@offset, @limit = 0, 200
@sites = Site.find(:all, :order => 'alexa_rank', :limit => @limit, :offset => @offset)
@start_time = params[:start_time]

render '/visitations/new'
end

end

21 changes: 21 additions & 0 deletions app/controllers/visitations_controller.rb
@@ -0,0 +1,21 @@
class VisitationsController < ApplicationController
def create
@scraping = Scraping.find(params[:scraping_id].to_i)
@limit, @offset, @start_time, batch_start = params[:limit].to_i, params[:offset].to_i, Time.parse(params[:start_time]), Time.parse(params[:batch_start])
results = JSON.parse(params[:results])
sites = Site.find(:all, :conditions => ['url IN (?)', results.keys]).map{|s| [s.id, s.url]}

Visitation.import [:scraping_id, :site_id, :visited], results.map{|key,value| [@scraping.id, sites.rassoc(key)[0], value]}, :validate => false # save a bit of RAM
Scraping.update_counters @scraping.id, :visitations_count => results.size # because we're using mass import, this isn't getting updated automagically

if @start_time > 30.seconds.ago
p "Last batch took #{Time.now - batch_start} seconds roundtrip" # TODO: modify batch size dynamically
@offset += @limit
@sites = Site.find(:all, :order => 'alexa_rank', :limit => @limit, :offset => @offset)
render '/visitations/new'
else
render '/scrapings/analyze'
end
end

end
3 changes: 3 additions & 0 deletions app/helpers/application_helper.rb
@@ -0,0 +1,3 @@
# Methods added to this helper will be available to all templates in the application.
module ApplicationHelper
end
2 changes: 2 additions & 0 deletions app/helpers/scrapings_helper.rb
@@ -0,0 +1,2 @@
module ScrapingsHelper
end
2 changes: 2 additions & 0 deletions app/helpers/visitations_helper.rb
@@ -0,0 +1,2 @@
module VisitationsHelper
end
6 changes: 6 additions & 0 deletions app/models/scraping.rb
@@ -0,0 +1,6 @@
class Scraping < ActiveRecord::Base
belongs_to :user
has_many :visitations, :dependent => :destroy

validates_numericality_of :visitations_count
end
6 changes: 6 additions & 0 deletions app/models/site.rb
@@ -0,0 +1,6 @@
class Site < ActiveRecord::Base
has_many :visitations
has_many :scrapings, :through => :visitations

validates_presence_of :url, :alexa_rank, :users_count
end
6 changes: 6 additions & 0 deletions app/models/user.rb
@@ -0,0 +1,6 @@
class User < ActiveRecord::Base
has_many :scrapings

validates_presence_of :cookie
validates_uniqueness_of :cookie
end
7 changes: 7 additions & 0 deletions app/models/visitation.rb
@@ -0,0 +1,7 @@
class Visitation < ActiveRecord::Base
belongs_to :scraping, :counter_cache => true
belongs_to :site

validates_presence_of :scraping_id, :site_id
validates_inclusion_of :visited, :in => [true, false]
end
18 changes: 18 additions & 0 deletions app/views/layouts/application.html.erb
@@ -0,0 +1,18 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<%= I18n.locale %>" lang="<%= I18n.locale %>">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<meta name='title' content='Historyprint - CSS history fingerprinting' />
<title>Historyprint - CSS history fingerprinting</title>
<%= stylesheet_link_tag :all, :cache => true %>
<%= javascript_include_tag :all, :cache => true %>
</head>

<body>
<%= yield %>
</body>
</html>


1 change: 1 addition & 0 deletions app/views/scrapings/analyze.js.erb
@@ -0,0 +1 @@
<%= update_page {|page| page['status'].replace_html "Data collected. Some fancy analysis here..." } %>
42 changes: 42 additions & 0 deletions app/views/scrapings/new.html.erb
@@ -0,0 +1,42 @@
<p>CSS Fingerprint is a research project inspired by the EFF's <a href="http://panopticlick.com">Panopticlick</a>.</p>

<p>Its intent is to see how well the <a href="http://ha.ckers.org/weird/CSS-history-hack.html">CSS history hack</a> can be used with AI techniques (like an SVM) to uniquely fingerprint users
despite changes in their browsing history, even on new computers or new browsers.</p>

<p>At the moment, the AI component is not yet active. In order to write it, I need data.</p>

<p>To help out, please enter some unique string below. It will be used as a cookie value to identify you when you come back.</p>

<p>It would especially help if you visit again after a while, or with a different browser/computer whose browsing history you are mostly responsible for,
and enter the SAME string again so that I can see how one person's browser histories vary.</p>

<p>This will take 30 seconds, and end in a status message saying it's done.</p>

<p>Thanks!</p>

<p>- <A href="http://saizai.com">Sai Emrys</A></p>

<div style="margin:1em;">
<% form_remote_tag(:url => scrapings_url(:start_time => Time.now), :method => :post,
:loading => update_page {|page| page['status'].replace_html "Sending first batch..." }) do %>
<p>Enter something unique to you (not a password): <%= text_field_tag :cookie, cookies[:remember_token] %>
<%= submit_tag "Fingerprint me" %></p>
<p>Please change this to be something you can remember, so you can enter it again from a different browser/computer.</p>
<% end %>

<div id="status">Not yet started...</div>
</div>

<small>
<p>For my fellow geeks:</p>

<p>What I store is the cookie value you submit and, for each of the top ~2-14k Alexa sites (depending on your CPU and internet speed), whether or not you have visited that site.</p>

<p>I make no attempt to find out who you are personally, and I don't store your IP; the point of this is simply to tell whether I can automatically identify when you visit again with a different browser.</p>

<p>The data will not be shared with anyone except other EFF-friendly researchers who agree to keep it confidential.</p>

<p>The source code is available at <A href="http://github.com/saizai/cssfingerprint">github</A>. Commits welcome.</p>

<p>Please do not slashdot this just yet, as it's not ready for the traffic.</p>
</small>
4 changes: 4 additions & 0 deletions app/views/visitations/new.js.erb
@@ -0,0 +1,4 @@
<%= remote_function :url => visitations_url(:start_time => @start_time, :limit => @limit, :offset => @offset, :scraping_id => @scraping.id, :batch_start => Time.now ), :method => :post,
:with => "'results=' + JSON.stringify(CSSHistory.check_batch(#{@sites.map(&:url).to_json}))",
:loading => update_page {|page| page['status'].replace_html "Testing sites #{@offset + 1} through #{@limit+@offset+1}..."}
%>
110 changes: 110 additions & 0 deletions config/boot.rb
@@ -0,0 +1,110 @@
# Don't change this file!
# Configure your app in config/environment.rb and config/environments/*.rb

RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)

module Rails
class << self
def boot!
unless booted?
preinitialize
pick_boot.run
end
end

def booted?
defined? Rails::Initializer
end

def pick_boot
(vendor_rails? ? VendorBoot : GemBoot).new
end

def vendor_rails?
File.exist?("#{RAILS_ROOT}/vendor/rails")
end

def preinitialize
load(preinitializer_path) if File.exist?(preinitializer_path)
end

def preinitializer_path
"#{RAILS_ROOT}/config/preinitializer.rb"
end
end

class Boot
def run
load_initializer
Rails::Initializer.run(:set_load_path)
end
end

class VendorBoot < Boot
def load_initializer
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
Rails::Initializer.run(:install_gem_spec_stubs)
Rails::GemDependency.add_frozen_gem_path
end
end

class GemBoot < Boot
def load_initializer
self.class.load_rubygems
load_rails_gem
require 'initializer'
end

def load_rails_gem
if version = self.class.gem_version
gem 'rails', version
else
gem 'rails'
end
rescue Gem::LoadError => load_error
$stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
exit 1
end

class << self
def rubygems_version
Gem::RubyGemsVersion rescue nil
end

def gem_version
if defined? RAILS_GEM_VERSION
RAILS_GEM_VERSION
elsif ENV.include?('RAILS_GEM_VERSION')
ENV['RAILS_GEM_VERSION']
else
parse_gem_version(read_environment_rb)
end
end

def load_rubygems
min_version = '1.3.2'
require 'rubygems'
unless rubygems_version >= min_version
$stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
exit 1
end

rescue LoadError
$stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
exit 1
end

def parse_gem_version(text)
$1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
end

private
def read_environment_rb
File.read("#{RAILS_ROOT}/config/environment.rb")
end
end
end
end

# All that for this:
Rails.boot!
43 changes: 43 additions & 0 deletions config/environment.rb
@@ -0,0 +1,43 @@
# Be sure to restart your server when you modify this file

# Specifies gem version of Rails to use when vendor/rails is not present
RAILS_GEM_VERSION = '2.3.5' unless defined? RAILS_GEM_VERSION
ENV['LOAD_ADAPTER_EXTENSIONS'] = 'mysql'

# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), 'boot')

Rails::Initializer.run do |config|
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.

# Add additional load paths for your own custom dirs
# config.load_paths += %W( #{RAILS_ROOT}/extras )

# Specify gems that this application depends on and have them installed with rake gems:install
# config.gem "bj"
# config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
# config.gem "sqlite3-ruby", :lib => "sqlite3"
# config.gem "aws-s3", :lib => "aws/s3"
config.gem 'fastercsv'

# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]

# Skip frameworks you're not going to use. To use Rails without a database,
# you must remove the Active Record framework.
# config.frameworks -= [ :active_record, :active_resource, :action_mailer ]

# Activate observers that should always be running
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer

# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names.
config.time_zone = 'UTC'

# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')]
# config.i18n.default_locale = :de
end

0 comments on commit cd9e966

Please sign in to comment.