Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions data/hlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1476,6 +1476,40 @@
- warn: {lhs: foldr' f c (reverse x), rhs: foldl' (flip f) c x, name: Use left fold instead of right fold}
- warn: {lhs: foldl' f c (reverse x), rhs: foldr (flip f) c x, note: IncreasesLaziness, name: Use right fold instead of left fold}

- group:
# Security hints, enabled by default. Disable per-rule with
# `- ignore: {name: "..."}` in .hlint.yaml, or suppress the whole
# group with `- group: {name: security, enabled: false}`.
#
# Rule categories:
# * `read` -> `readMaybe` on non-literal arguments (CWE-502)
# * broken / weak hash primitives: MD5, SHA-1 (CWE-327)
name: security
enabled: true
rules:

# `read` throws a runtime exception on malformed input; it is the
# Haskell analogue of Python's `pickle.load` / `ast.literal_eval` on
# untrusted data. Replace with `readMaybe` from `Text.Read` and
# handle `Nothing` explicitly. Literal arguments (e.g. `read "42"`)
# are not flagged because the failure is a programmer bug caught on
# first run, not a runtime attack surface.
- warn: {lhs: read x, rhs: Text.Read.readMaybe x, side: not (isLitString x), name: "Use readMaybe on untrusted input (CWE-502)"}
- warn: {lhs: reads x, rhs: Text.Read.readMaybe x, side: not (isLitString x), name: "Use readMaybe on untrusted input (CWE-502)"}

# Broken or weak cryptographic hashes. MD5 and SHA-1 are
# cryptographically broken (MD5: chosen-prefix collisions since 2008;
# SHA-1: SHAttered, 2017) and must not be used for any security
# purpose (authentication, integrity, digital signatures). Use
# SHA-256 or stronger from `Crypto.Hash` (cryptonite/crypton); for
# password hashing use `Crypto.KDF.BCrypt` or `Crypto.KDF.Argon2`.
- warn: {lhs: Crypto.Hash.MD5.hash x, rhs: Crypto.Hash.SHA256.hash x, name: "Avoid broken hash MD5 (CWE-327)"}
- warn: {lhs: Crypto.Hash.MD5.hashlazy x, rhs: Crypto.Hash.SHA256.hashlazy x, name: "Avoid broken hash MD5 (CWE-327)"}
- warn: {lhs: Crypto.Hash.SHA1.hash x, rhs: Crypto.Hash.SHA256.hash x, name: "Avoid broken hash SHA-1 (CWE-327)"}
- warn: {lhs: Crypto.Hash.SHA1.hashlazy x, rhs: Crypto.Hash.SHA256.hashlazy x, name: "Avoid broken hash SHA-1 (CWE-327)"}
- warn: {lhs: Data.Digest.Pure.MD5.md5 x, rhs: Crypto.Hash.SHA256.hashlazy x, name: "Avoid broken hash MD5 (CWE-327)"}
- warn: {lhs: Data.Digest.Pure.SHA.sha1 x, rhs: Data.Digest.Pure.SHA.sha256 x, name: "Avoid broken hash SHA-1 (CWE-327)"}

- group:
# used for tests, enabled when testing this file
name: testing
Expand Down
164 changes: 164 additions & 0 deletions tests/security.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
---------------------------------------------------------------------
RUN tests/security-hash.hs --hint=data/hlint.yaml
FILE tests/security-hash.hs
module Sample where

import qualified Crypto.Hash.MD5 as MD5
import qualified Crypto.Hash.SHA1 as SHA1
import qualified Data.Digest.Pure.MD5 as PMD5
import qualified Data.Digest.Pure.SHA as PSHA
import qualified Data.ByteString as BS

input :: BS.ByteString
input = BS.empty

viaMD5, viaMD5Lazy, viaSHA1, viaSHA1Lazy :: BS.ByteString
viaMD5 = MD5.hash input
viaMD5Lazy = MD5.hashlazy undefined
viaSHA1 = SHA1.hash input
viaSHA1Lazy = SHA1.hashlazy undefined

viaPureMD5 = PMD5.md5 undefined
viaPureSHA1 = PSHA.sha1 undefined
OUTPUT
tests/security-hash.hs:13:10-23: Warning: Avoid broken hash MD5 (CWE-327)
Found:
MD5.hash input
Perhaps:
Crypto.Hash.SHA256.hash input

tests/security-hash.hs:13:10-23: Warning: Avoid broken hash MD5 (CWE-327)
Found:
MD5.hash input
Perhaps:
Crypto.Hash.SHA256.hash input

tests/security-hash.hs:14:14-35: Warning: Avoid broken hash MD5 (CWE-327)
Found:
MD5.hashlazy undefined
Perhaps:
Crypto.Hash.SHA256.hashlazy undefined

tests/security-hash.hs:14:14-35: Warning: Avoid broken hash MD5 (CWE-327)
Found:
MD5.hashlazy undefined
Perhaps:
Crypto.Hash.SHA256.hashlazy undefined

tests/security-hash.hs:15:11-25: Warning: Avoid broken hash SHA-1 (CWE-327)
Found:
SHA1.hash input
Perhaps:
Crypto.Hash.SHA256.hash input

tests/security-hash.hs:15:11-25: Warning: Avoid broken hash SHA-1 (CWE-327)
Found:
SHA1.hash input
Perhaps:
Crypto.Hash.SHA256.hash input

tests/security-hash.hs:16:15-37: Warning: Avoid broken hash SHA-1 (CWE-327)
Found:
SHA1.hashlazy undefined
Perhaps:
Crypto.Hash.SHA256.hashlazy undefined

tests/security-hash.hs:16:15-37: Warning: Avoid broken hash SHA-1 (CWE-327)
Found:
SHA1.hashlazy undefined
Perhaps:
Crypto.Hash.SHA256.hashlazy undefined

tests/security-hash.hs:18:14-31: Warning: Avoid broken hash MD5 (CWE-327)
Found:
PMD5.md5 undefined
Perhaps:
Crypto.Hash.SHA256.hashlazy undefined

tests/security-hash.hs:18:14-31: Warning: Avoid broken hash MD5 (CWE-327)
Found:
PMD5.md5 undefined
Perhaps:
Crypto.Hash.SHA256.hashlazy undefined

tests/security-hash.hs:19:15-33: Warning: Avoid broken hash SHA-1 (CWE-327)
Found:
PSHA.sha1 undefined
Perhaps:
PSHA.sha256 undefined

tests/security-hash.hs:19:15-33: Warning: Avoid broken hash SHA-1 (CWE-327)
Found:
PSHA.sha1 undefined
Perhaps:
PSHA.sha256 undefined

12 hints

---------------------------------------------------------------------
RUN tests/security-read-nonliteral.hs --hint=data/hlint.yaml
FILE tests/security-read-nonliteral.hs
module Sample where

parseStr :: String -> Int
parseStr s = 1 + read s

parseOther :: String -> Double
parseOther raw = 0.5 * read raw
OUTPUT
tests/security-read-nonliteral.hs:4:18-23: Warning: Use readMaybe on untrusted input (CWE-502)
Found:
read s
Perhaps:
Text.Read.readMaybe s

tests/security-read-nonliteral.hs:4:18-23: Warning: Use readMaybe on untrusted input (CWE-502)
Found:
read s
Perhaps:
Text.Read.readMaybe s

tests/security-read-nonliteral.hs:7:24-31: Warning: Use readMaybe on untrusted input (CWE-502)
Found:
read raw
Perhaps:
Text.Read.readMaybe raw

tests/security-read-nonliteral.hs:7:24-31: Warning: Use readMaybe on untrusted input (CWE-502)
Found:
read raw
Perhaps:
Text.Read.readMaybe raw

4 hints


---------------------------------------------------------------------
RUN tests/security-read-literal.hs --hint=data/hlint.yaml
FILE tests/security-read-literal.hs
module Sample where

answer :: Int
answer = read "42"

piApprox :: Double
piApprox = read "3.14"
OUTPUT
No hints


---------------------------------------------------------------------
RUN tests/security-ignored.hs --hint=data/hlint.yaml -i "Avoid broken hash MD5 (CWE-327)" -i "Use readMaybe on untrusted input (CWE-502)"
FILE tests/security-ignored.hs
module Sample where

import qualified Crypto.Hash.MD5 as MD5
import qualified Data.ByteString as BS

digest :: BS.ByteString
digest = MD5.hash BS.empty

parseStr :: String -> Int
parseStr s = 1 + read s
OUTPUT
No hints