Skip to content

Instantly share code, notes, and snippets.

@devd
Last active July 12, 2016 22:31
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devd/1499684 to your computer and use it in GitHub Desktop.
Save devd/1499684 to your computer and use it in GitHub Desktop.
Simple Tool to maybe rewrite extensions to be CSP compatible
#Run this on Extension Source code to make it inline-script-less
#perl ext2csp.pl <extdir>;
#where extdir is the directory with the manifest.json.
#This will make a inplace change: make a copy if you want it to work on a copy.
use strict;
use warnings;
use HTML::Parser ();
use HTML::Entities;
use File::Util;
use File::Basename;
use autodie;
use Digest::SHA qw(sha512_base64);
#TODO cleanup error handling and use GetOpt to make
# all these command line switches
#Change the last 0 to 1 if you want to use the hash src support
my ($extension_base_dir,$jsfolder,$jsbasename,$filectr,$newidbase,$usehashsrc) = ($ARGV[0],"js","addedForCSP",0,"_added_by_transform_",0);
my ($buffer,$idctr)=("",0);
die "No base directory specified" unless $extension_base_dir;
die "$extension_base_dir does not exist" unless (-d $extension_base_dir);
unless(-d "$extension_base_dir/$jsfolder"){
mkdir "$extension_base_dir/$jsfolder";
}
my $f = File::Util->new();
my @hashsrcs=();
foreach my $filename ($f->list_dir($extension_base_dir,"--recurse","--files-only")){
next unless $filename =~ m/html?$/i;
$buffer="";$idctr=0;
my $parser = HTML::Parser->new(api_version=>3);
$parser->handler(default=>sub{$buffer.=$_[0];},"text");
$parser->handler(start => \&startHandler,"tag,attr,text");
$parser->parse_file($filename);
#ok now we remove the script tags
$buffer =~ s{<script\s*(?:type=['"]text/javascript['"])?\s*>(.+?)</script>}{scriptRewrite($1,$filename)}seg;
writeFile($filename,$buffer);
if (@hashsrcs) {
print "\n---- CSP hash src for $filename ----\n";
print join(' ',@hashsrcs);
print "\n------------\n";
@hashsrcs=();
}
}
sub scriptRewrite{
my ($contents,$origfile)=($_[0],basename($_[1]));
if ($usehashsrc) {
#TODO: testing needed
push @hashsrcs,"'sha512-".sha512_base64($contents)."'";
return "<script type='text/javascript'>$contents</script>";
}
$filectr++ while (-e "$extension_base_dir/$jsfolder/$jsbasename.$origfile.$filectr.js");
my $newfilename="$jsfolder/$jsbasename.$origfile.$filectr.js";
writeFile("$extension_base_dir/$newfilename",$contents);
return "<script type='text/javascript' src='/$newfilename'></script>";
}
sub writeFile{
my($filename,$contents)=($_[0],$_[1]);
open my $fh,">",$filename;
print $fh $contents;
close($fh);
}
sub startHandler{
my ($tag,$attr,$origtext)=@_;
my $scriptcode="";
my %attrhash = %$attr;
my %codehash=();
my $id = "";
{
while(my ($key,$value) = each %attrhash){
if($key =~ /^on(.*)$/i){
$codehash{$1}=$value;
}
if($key =~ /^src$/i){
if($value =~ /^http/){
print "Found src in:",$origtext,"\n";
}
}
$id = $value if($key eq 'id');
}
}
unless(keys %codehash){
#didn't find any inline handlers, bail out!
$buffer.=$origtext; return;
}
unless($id){
#
$id="_added_by_transform_".$idctr++;
$attrhash{'id'}=$id;
}
{
foreach my $key (keys %codehash){
$scriptcode.="temp.addEventListener('".$key."',function(event){".$codehash{$key}."});\n";
}
}
my $newtext="";
if($scriptcode){
$scriptcode = "<script>\n(function(){ var temp = document.getElementById('$id');\n".$scriptcode."\n})();\n</script>";
$newtext = "<$tag ";
foreach my $key (keys %attrhash){
next if $key =~ m/^on/;
$newtext .= " $key='".decode_entities($attrhash{$key})."' ";
}
$newtext.=" >";
}
$buffer.="$newtext\n$scriptcode";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment