packages feed

GoogleSB (empty) → 0.1

raw patch · 5 files changed

+258/−0 lines, 5 filesdep +Cryptodep +HTTPdep +basesetup-changed

Dependencies added: Crypto, HTTP, base, binary, haskell98, mtl, network, split

Files

+ GoogleSB.cabal view
@@ -0,0 +1,21 @@+Name:                GoogleSB+Version:             0.1+Synopsis:            Interface to Google Safe Browsing API+License:             BSD3+License-file:        LICENSE+Author:              Alex Ott+Copyright:           (c) Alex Ott, 2009+Maintainer:          alexott@gmail.com+Build-Type:          Simple+Cabal-Version:       >=1.2+Category:            Security+-- Homepage:            +Description:+  This package implements support for Google Safe Browsing API, that provides+  almost real-time information about malware & other malicious sites on the Web++Extra-Source-Files: README++Library+        Exposed-modules: GoogleSB+        Build-depends: base >= 3, base <= 5, HTTP, mtl, binary, Crypto, split, network, haskell98
+ GoogleSB.hs view
@@ -0,0 +1,196 @@+{- |This module provides support for Google Safe Browsing API (<http://code.google.com/apis/safebrowsing/>).+   To use this module you need to obtain access key from project's page.++  Typical use of this module will look like:++>  Right mhsh <- updateHash your_key makeEmptyMalwareHash+>  checkURL mhsh "http://kaishi2009.com/"+>  checkURL mhsh ...++It's better to save hashes between runs to avoid of full updates every time.  So at the end of program it's better to store hash on disk with following code:++>  saveHash "malware.dat" mhsh++And during startup, load it from file, if it exists on disk:++>  Right mhsh <- load Hash "malware.dat"++-}+module GoogleSB+    (Hash,+     updateHash,+     loadHash,+     saveHash,+     checkURL,+     makeEmptyBlackHash,+     makeEmptyMalwareHash+    )+    where++import IO+import Network.URI+import Network.Browser+import Network.HTTP+import Data.List.Split (splitOn)+import Data.List (intercalate, nub, foldl', elem, delete, intersect, sort)+import Data.Digest.MD5 (hash)+import Data.Char (ord, chr, toLower)+import Codec.Utils+import Numeric (showHex, readHex)+import Data.Binary+import Control.Monad+import qualified Control.Exception as Ex+import Control.Monad.Error+++{- |Data structure to store data about Hash. It contains information about version, name+    of hash, and actual Hash data -}+data Hash = Hash {+      versionMajor :: Int -- ^Major part of version number+    , versionMinor :: Int -- ^Minor part of version number+    , name :: String      -- ^Hash name (see documentation about Google SB)+    , hashes :: [Integer] -- ^Actual data+    } deriving (Show, Eq)++instance Binary Hash where+    put (Hash vmj vmn name hsh) = put vmj >> put vmn >> put name >> put hsh+    get = liftM4 Hash get get get get++-- |Creates empty 'black' Hash object+makeEmptyBlackHash :: Hash+makeEmptyBlackHash = Hash { versionMajor = 1, versionMinor= -1, hashes= [], name = "goog-black-hash"}++-- |Creates empty 'malware' Hash object+makeEmptyMalwareHash :: Hash+makeEmptyMalwareHash = Hash { versionMajor = 1, versionMinor= -1, hashes= [], name = "goog-malware-hash"}++{- |Performs update of Hash from server.  For first update, you need to provide hash+ created by 'makeEmptyBlackHash' or 'makeEmptyMalwareHash'.  And for later updates, you+ will use existing hashes to get only updates to hashes, not the full database. -}+updateHash :: String -- ^Access key for Google SB. See module description+           -> Hash -- ^Hash to update.  Use 'makeEmptyBlackHash' or 'makeEmptyMalwareHash' for first update+           -> IO (Either String Hash) {- ^Result of update: 'Left String' if error happens+                                          (String contains error message), or 'Right Hash' on success -}+updateHash key hash = Ex.handle (makeHandler "Hash update error: ") $ do+                           hdata <- downloadHash key hash+                           return $ case hdata of+                                      Left err  -> Left err;+                                      Right str -> parseHash hash str++{- |Loads Hash from given file -}+loadHash :: FilePath -- ^File from which Hash will loaded+         -> IO (Either String Hash) {- ^Result of loading: 'Left String' if error happens+                                       (String contains error message), or 'Right Hash' on success -}+loadHash fname = Ex.handle (makeHandler "Hash load error: ") $ do+                   hash <- decodeFile fname+                   return (Right hash)++-- |Saves Hash into given file+saveHash :: FilePath -- ^File in which Hash will stored+         -> Hash -- ^Hash to store+         -> IO ()+saveHash = encodeFile++{- |Performs checking of presence of given URL (second argument of function) in the Hash (first argument). -}+checkURL :: Hash -- ^Hash to check against+         -> String -- ^URL to check+         -> Bool -- ^True, if URL exists in database, False - otherwise+checkURL hsh url = not $ null sl+    where hl = map (\ x -> octets2int $ hash $ str2octets x) $ generateURLVariants url+          set = hashes hsh+          sl = intersect set hl++-- Auxiliary functions++--+makeHandler :: (Monad m) => String -> Ex.SomeException -> m (Either String Hash)+makeHandler str ex = return (Left $ str ++ show (ex::Ex.SomeException))++--+makeSBURL :: String -> Hash -> String+makeSBURL key hash = "http://sb.google.com/safebrowsing/update?client=api&apikey=" ++ key+                   ++ "&version=" ++ name hash+                   ++ ":" ++ (show $ versionMajor hash) ++ ":" ++ (show $ versionMinor hash)++--+downloadHash :: String -> Hash -> IO (Either String String)+downloadHash key hash = do+  rspO <- browse $ do+            setAllowRedirects True+            request $ getRequest $ makeSBURL key hash+  let rsp = snd rspO+      code = rspCode rsp+  return $ case code of+             (2,_,_) -> Right $ rspBody rsp;+             _       -> Left "Error fetching data"++--+getHashVersion :: String -> (Int,Int)+getHashVersion ('[':xs) | last xs == ']' =+                            let vs = splitOn "." $ head $ tail $ splitOn " " $ init xs+                            in if null vs+                               then (-1,-1)+                               else (read (head vs)::Int, read (last vs)::Int)+                        | otherwise = (-1,-1)+getHashVersion _ = (-1, -1)++parseHash :: Hash -> String -> (Either String Hash)+parseHash h [] = Right h+parseHash h d =+    let l = lines d+        (vmj,vmn) = getHashVersion $ head l+        stripTab s = if last s == '\t' then init s else s+        convString = fst . head . readHex . stripTab+        loop h' [""] = h'+        loop h' [] = h'+        loop h' (x:xs) | head x == '-' = loop (delete (convString $ tail x)  h') xs+                       | head x == '+' = loop ((convString $ tail x) `seq` (convString $ tail x) : h') xs+                       | otherwise = loop ((convString x) `seq` (convString x) : h') xs+    in if null l || vmj == -1 || vmn == -1+       then Left "Hash parse error"+       else Right h { hashes = sort $ loop (hashes h) $ tail l,+                      versionMajor = vmj,+                      versionMinor = vmn}+++str2octets :: String -> [Octet]+str2octets = concatMap (\ x -> toTwosComp $ ord x)++--+octets2int :: [Octet] -> Integer+octets2int = foldl' (\x y -> x*256 + fromIntegral y) 0++--+lowercase :: String -> String+lowercase = map (\x -> toLower x)++--+generateURLVariants :: String -> [String]+generateURLVariants uri =+    let u = parseURI uri+    in case u of+         Nothing -> [""]+         Just r -> nub [ h ++ p | h <- generateHosts (uriAuthority r),+                                  p <- generatePaths (uriPath r) (uriQuery r)]++--+generateHosts :: (Maybe URIAuth) -> [String]+generateHosts Nothing = []+generateHosts (Just auth) = h:nl+    where h = lowercase $ uriRegName auth+          hl=reverse $ splitOn "." h+          nl=map (\ x -> intercalate "." $ reverse $ take x hl) [2..(min 5 $ length hl -1)]++-- length $ generateURLVariants "http://a.b.c.d.e.f.g/1.html" == 10+-- length $ generateURLVariants "http://a.b.c/1/2.html?param=1" == 8++--+generatePaths :: String -> String -> [String]+generatePaths path [] = path:nl+    where np = normalizePathSegments path+          pl = drop 1 $ splitOn "/" np+          plLenght = length pl+          lastChar x = if x == 0 || x == plLenght then "" else "/"+          nl = map (\ x -> '/':(intercalate "/" $ take x pl) ++ lastChar x) [0..(min 3 plLenght)]+generatePaths path query = (path ++ query) : generatePaths path []+
+ LICENSE view
@@ -0,0 +1,31 @@+Copyright (c) 2009, Alex Ott++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * The names of contributors may not be used to endorse or promote+      products derived from this software without specific prior+      written permission. ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README view
@@ -0,0 +1,5 @@+This package implements support for Google Safe Browsing API+(http://code.google.com/apis/safebrowsing/)++For description of package, please refer to documentation inside package sources (it could+be extracted with haddock)
+ Setup.lhs view
@@ -0,0 +1,5 @@+#! /usr/bin/env runhaskell++> module Main where+> import Distribution.Simple( defaultMain )+> main = defaultMain