From 29cd72d09dff5fce72f64ec014cc22f60a240364 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9ophile=20Choutri?= <theophile@choutri.eu>
Date: Sat, 12 Oct 2024 00:19:18 +0200
Subject: [PATCH 1/2] Start the data model for security advisories

---
 cabal.project                                 |  25 +-
 changelog.d/762                               |   4 +
 flora.cabal                                   |  61 ++
 hie.yaml                                      |   9 +-
 ...41011153354_create_security_advisories.sql |  16 +
 ...0241011154110_create_affected_packages.sql |  15 +
 ...4081932_create_affected_version_ranges.sql |  15 +
 src/advisories/Advisories/Import.hs           | 170 ++++
 src/advisories/Advisories/Import/Error.hs     |  14 +
 .../Advisories/Model/Advisory/Query.hs        |  32 +
 .../Advisories/Model/Advisory/Types.hs        |  49 +
 .../Advisories/Model/Advisory/Update.hs       |  10 +
 .../Advisories/Model/Affected/Query.hs        |  39 +
 .../Advisories/Model/Affected/Types.hs        |  75 ++
 .../Advisories/Model/Affected/Update.hs       |  19 +
 src/core/Flora/Model/Package/Guard.hs         |  27 +
 src/core/Flora/Model/Package/Query.hs         |   3 +-
 src/core/Flora/Model/PersistentSession.hs     |   2 +-
 src/core/Flora/Model/Release/Guard.hs         |  26 +
 .../AffectedVersionRange/Orphans.hs           |  29 +
 src/datatypes/Advisories/CAPEC/Orphans.hs     |  14 +
 src/datatypes/Advisories/CVSS/Orphans.hs      |  26 +
 src/datatypes/Advisories/CWE/Orphans.hs       |  14 +
 src/datatypes/Advisories/HsecId/Orphans.hs    |  28 +
 src/datatypes/Advisories/Keyword/Orphans.hs   |  15 +
 src/datatypes/Advisories/System/Orphans.hs    |  29 +
 src/datatypes/Distribution/Orphans/ConfVar.hs |   3 +-
 src/datatypes/Distribution/Orphans/Version.hs |   5 +
 src/datatypes/OSV/Reference/Orphans.hs        |  26 +
 src/datatypes/Pandoc/Orphans.hs               |  12 +
 src/web/FloraWeb/API/Server/Packages.hs       |   3 +-
 src/web/FloraWeb/Common/Guards.hs             |  39 -
 src/web/FloraWeb/Pages/Server/Packages.hs     |   2 +
 test/Flora/AdvisorySpec.hs                    |  24 +
 test/Flora/PackageSpec.hs                     |   2 +-
 test/Flora/TestUtils.hs                       |   5 +-
 test/Main.hs                                  |  17 +-
 .../hackage/aeson/HSEC-2023-0001.md           |  34 +
 .../advisories/hackage/base/HSEC-2023-0007.md |  78 ++
 .../hackage/biscuit-haskell/HSEC-2023-0002.md |  31 +
 .../hackage/biscuit-haskell/HSEC-2024-0009.md |  30 +
 .../hackage/cabal-install/HSEC-2023-0015.md   |  95 ++
 .../hackage/hledger-web/HSEC-2023-0008.md     |  47 +
 .../hackage/keter/HSEC-2024-0001.md           |  30 +
 .../hackage/pandoc/HSEC-2023-0014.md          |  27 +
 .../hackage/process/HSEC-2024-0003.md         | 175 ++++
 .../hackage/tls-extra/HSEC-2023-0005.md       |  34 +
 .../hackage/toml-reader/HSEC-2023-0007.md     |   1 +
 .../Advisories/advisories/reserved/.gitkeep   |   0
 .../advisories/reserved/HSEC-2024-0004.md     |   0
 .../advisories/reserved/HSEC-2024-0005.md     |   0
 .../Cabal/hackage/aeson-0.4.0.0.cabal         | 171 ++++
 .../Cabal/hackage/aeson-2.0.1.0.cabal         | 241 +++++
 .../Cabal/hackage/aeson-2.2.3.0.cabal         | 246 +++++
 .../fixtures/Cabal/hackage/base-3.0.3.1.cabal | 154 ++++
 .../hackage/biscuit-haskell-0.1.0.0.cabal     | 136 +++
 .../hackage/biscuit-haskell-0.2.0.0.cabal     | 146 +++
 .../hackage/biscuit-haskell-0.3.0.0.cabal     | 123 +++
 .../hackage/biscuit-haskell-0.4.0.0.cabal     | 123 +++
 .../hackage/cabal-install-1.24.0.0.cabal      | 409 +++++++++
 .../hackage/cabal-install-3.10.2.0.cabal      | 425 +++++++++
 .../Cabal/hackage/hledged-web-0.24.cabal      | 279 ++++++
 .../Cabal/hackage/hledged-web-1.23.cabal      | 250 ++++++
 test/fixtures/Cabal/hackage/keter-0.3.4.cabal |  69 ++
 test/fixtures/Cabal/hackage/keter-1.8.4.cabal | 139 +++
 test/fixtures/Cabal/hackage/keter-1.8.cabal   | 126 +++
 test/fixtures/Cabal/hackage/pandoc-1.13.cabal | 427 +++++++++
 .../fixtures/Cabal/hackage/pandoc-3.1.4.cabal | 842 ++++++++++++++++++
 .../Cabal/hackage/process-1.0.0.0.cabal       |  36 +
 .../Cabal/hackage/process-1.6.19.0.cabal      | 109 +++
 .../Cabal/hackage/process-1.6.23.0.cabal      |  94 ++
 .../Cabal/hackage/tls-extra-0.1.0.cabal       |  78 ++
 .../Cabal/hackage/tls-extra-0.4.6.1.cabal     |  60 ++
 .../Cabal/hackage/toml-reader-0.1.0.0.cabal   | 113 +++
 .../Cabal/hackage/toml-reader-0.2.0.0.cabal   | 113 +++
 75 files changed, 6338 insertions(+), 57 deletions(-)
 create mode 100644 changelog.d/762
 create mode 100644 migrations/20241011153354_create_security_advisories.sql
 create mode 100644 migrations/20241011154110_create_affected_packages.sql
 create mode 100644 migrations/20241014081932_create_affected_version_ranges.sql
 create mode 100644 src/advisories/Advisories/Import.hs
 create mode 100644 src/advisories/Advisories/Import/Error.hs
 create mode 100644 src/advisories/Advisories/Model/Advisory/Query.hs
 create mode 100644 src/advisories/Advisories/Model/Advisory/Types.hs
 create mode 100644 src/advisories/Advisories/Model/Advisory/Update.hs
 create mode 100644 src/advisories/Advisories/Model/Affected/Query.hs
 create mode 100644 src/advisories/Advisories/Model/Affected/Types.hs
 create mode 100644 src/advisories/Advisories/Model/Affected/Update.hs
 create mode 100644 src/core/Flora/Model/Package/Guard.hs
 create mode 100644 src/core/Flora/Model/Release/Guard.hs
 create mode 100644 src/datatypes/Advisories/AffectedVersionRange/Orphans.hs
 create mode 100644 src/datatypes/Advisories/CAPEC/Orphans.hs
 create mode 100644 src/datatypes/Advisories/CVSS/Orphans.hs
 create mode 100644 src/datatypes/Advisories/CWE/Orphans.hs
 create mode 100644 src/datatypes/Advisories/HsecId/Orphans.hs
 create mode 100644 src/datatypes/Advisories/Keyword/Orphans.hs
 create mode 100644 src/datatypes/Advisories/System/Orphans.hs
 create mode 100644 src/datatypes/OSV/Reference/Orphans.hs
 create mode 100644 src/datatypes/Pandoc/Orphans.hs
 create mode 100644 test/Flora/AdvisorySpec.hs
 create mode 100644 test/fixtures/Advisories/advisories/hackage/aeson/HSEC-2023-0001.md
 create mode 100644 test/fixtures/Advisories/advisories/hackage/base/HSEC-2023-0007.md
 create mode 100644 test/fixtures/Advisories/advisories/hackage/biscuit-haskell/HSEC-2023-0002.md
 create mode 100644 test/fixtures/Advisories/advisories/hackage/biscuit-haskell/HSEC-2024-0009.md
 create mode 100644 test/fixtures/Advisories/advisories/hackage/cabal-install/HSEC-2023-0015.md
 create mode 100644 test/fixtures/Advisories/advisories/hackage/hledger-web/HSEC-2023-0008.md
 create mode 100644 test/fixtures/Advisories/advisories/hackage/keter/HSEC-2024-0001.md
 create mode 100644 test/fixtures/Advisories/advisories/hackage/pandoc/HSEC-2023-0014.md
 create mode 100644 test/fixtures/Advisories/advisories/hackage/process/HSEC-2024-0003.md
 create mode 100644 test/fixtures/Advisories/advisories/hackage/tls-extra/HSEC-2023-0005.md
 create mode 120000 test/fixtures/Advisories/advisories/hackage/toml-reader/HSEC-2023-0007.md
 create mode 100644 test/fixtures/Advisories/advisories/reserved/.gitkeep
 create mode 100644 test/fixtures/Advisories/advisories/reserved/HSEC-2024-0004.md
 create mode 100644 test/fixtures/Advisories/advisories/reserved/HSEC-2024-0005.md
 create mode 100644 test/fixtures/Cabal/hackage/aeson-0.4.0.0.cabal
 create mode 100644 test/fixtures/Cabal/hackage/aeson-2.0.1.0.cabal
 create mode 100644 test/fixtures/Cabal/hackage/aeson-2.2.3.0.cabal
 create mode 100644 test/fixtures/Cabal/hackage/base-3.0.3.1.cabal
 create mode 100644 test/fixtures/Cabal/hackage/biscuit-haskell-0.1.0.0.cabal
 create mode 100644 test/fixtures/Cabal/hackage/biscuit-haskell-0.2.0.0.cabal
 create mode 100644 test/fixtures/Cabal/hackage/biscuit-haskell-0.3.0.0.cabal
 create mode 100644 test/fixtures/Cabal/hackage/biscuit-haskell-0.4.0.0.cabal
 create mode 100644 test/fixtures/Cabal/hackage/cabal-install-1.24.0.0.cabal
 create mode 100644 test/fixtures/Cabal/hackage/cabal-install-3.10.2.0.cabal
 create mode 100644 test/fixtures/Cabal/hackage/hledged-web-0.24.cabal
 create mode 100644 test/fixtures/Cabal/hackage/hledged-web-1.23.cabal
 create mode 100644 test/fixtures/Cabal/hackage/keter-0.3.4.cabal
 create mode 100644 test/fixtures/Cabal/hackage/keter-1.8.4.cabal
 create mode 100644 test/fixtures/Cabal/hackage/keter-1.8.cabal
 create mode 100644 test/fixtures/Cabal/hackage/pandoc-1.13.cabal
 create mode 100644 test/fixtures/Cabal/hackage/pandoc-3.1.4.cabal
 create mode 100644 test/fixtures/Cabal/hackage/process-1.0.0.0.cabal
 create mode 100644 test/fixtures/Cabal/hackage/process-1.6.19.0.cabal
 create mode 100644 test/fixtures/Cabal/hackage/process-1.6.23.0.cabal
 create mode 100644 test/fixtures/Cabal/hackage/tls-extra-0.1.0.cabal
 create mode 100644 test/fixtures/Cabal/hackage/tls-extra-0.4.6.1.cabal
 create mode 100644 test/fixtures/Cabal/hackage/toml-reader-0.1.0.0.cabal
 create mode 100644 test/fixtures/Cabal/hackage/toml-reader-0.2.0.0.cabal

diff --git a/cabal.project b/cabal.project
index b4838b9d..ca850a6a 100644
--- a/cabal.project
+++ b/cabal.project
@@ -7,16 +7,18 @@ active-repositories: hackage.haskell.org
 
 tests: True
 
-allow-newer: type-errors-pretty:base
-           , souffle-haskell:text
-           , servant-lucid:text
-           , hashable:filepath
-           , qrcode-juicypixels:text
+allow-newer: hashable:filepath
+           , hsec-sync:lens
+           , hsec-tools:pandoc
+           , qrcode-core:bytestring
            , qrcode-core:text
            , qrcode-juicypixels:bytestring
-           , qrcode-core:bytestring
+           , qrcode-juicypixels:text
+           , servant-lucid:text
+           , souffle-haskell:text
            , tasty-test-reporter:ansi-terminal
            , tasty-test-reporter:tasty
+           , type-errors-pretty:base
 
 allow-older: pg-entity:time
 
@@ -31,6 +33,17 @@ package warp
 package zlib
   flags: -pkg-config
 
+source-repository-package
+    type: git
+    location: https://github.com/haskell/security-advisories/
+    tag: b6eca11d65ab18fce60030d1dba7793789157292
+    subdir:
+      ./code/cvss
+      ./code/osv
+      ./code/hsec-core
+      ./code/hsec-sync
+      ./code/hsec-tools
+
 source-repository-package
     type: git
     location: https://github.com/scrive/tracing
diff --git a/changelog.d/762 b/changelog.d/762
new file mode 100644
index 00000000..7b88573f
--- /dev/null
+++ b/changelog.d/762
@@ -0,0 +1,4 @@
+synopsis: Start the data model for security advisories
+prs: #762
+significance: significant
+
diff --git a/flora.cabal b/flora.cabal
index 83bcbdb3..db79b121 100644
--- a/flora.cabal
+++ b/flora.cabal
@@ -75,6 +75,13 @@ library
   -- cabal-fmt: expand src/core
   -- cabal-fmt: expand src/datatypes
   exposed-modules:
+    Advisories.AffectedVersionRange.Orphans
+    Advisories.CAPEC.Orphans
+    Advisories.CVSS.Orphans
+    Advisories.CWE.Orphans
+    Advisories.HsecId.Orphans
+    Advisories.Keyword.Orphans
+    Advisories.System.Orphans
     Data.Aeson.Orphans
     Data.Positive
     Data.Text.Display.Orphans
@@ -114,6 +121,7 @@ library
     Flora.Model.Job
     Flora.Model.Organisation
     Flora.Model.Package
+    Flora.Model.Package.Guard
     Flora.Model.Package.Orphans
     Flora.Model.Package.Publisher
     Flora.Model.Package.Query
@@ -124,6 +132,7 @@ library
     Flora.Model.PackageIndex.Update
     Flora.Model.PersistentSession
     Flora.Model.Release
+    Flora.Model.Release.Guard
     Flora.Model.Release.Query
     Flora.Model.Release.Types
     Flora.Model.Release.Update
@@ -137,6 +146,8 @@ library
     JSON
     Log.Backend.File
     Lucid.Orphans
+    OSV.Reference.Orphans
+    Pandoc.Orphans
     Servant.API.ContentTypes.GZip
 
   build-depends:
@@ -153,6 +164,7 @@ library
     , cryptohash-sha256
     , cryptonite
     , cryptonite-conduit
+    , cvss
     , dani-servant-lucid2
     , deepseq
     , deriving-aeson
@@ -162,6 +174,8 @@ library
     , effectful-plugin
     , envparse
     , filepath
+    , hsec-core
+    , hsec-sync
     , http-api-data
     , http-media
     , iso8601-time
@@ -175,6 +189,8 @@ library
     , odd-jobs
     , openapi3
     , optics-core
+    , osv
+    , pandoc-types
     , pcre2
     , pg-entity
     , pg-transact
@@ -197,6 +213,7 @@ library
     , text-display
     , time
     , tracing
+    , tracing-effectful
     , unliftio
     , utf8-string
     , uuid
@@ -205,6 +222,47 @@ library
 
   ghc-options:     -fplugin=Effectful.Plugin
 
+library flora-advisories
+  import:          common-extensions
+  import:          common-ghc-options
+  hs-source-dirs:  ./src/advisories
+
+  -- cabal-fmt: expand src/advisories
+  exposed-modules:
+    Advisories.Import
+    Advisories.Import.Error
+    Advisories.Model.Advisory.Query
+    Advisories.Model.Advisory.Types
+    Advisories.Model.Advisory.Update
+    Advisories.Model.Affected.Query
+    Advisories.Model.Affected.Types
+    Advisories.Model.Affected.Update
+
+  build-depends:
+    , aeson
+    , base
+    , Cabal-syntax
+    , containers
+    , cvss
+    , deepseq
+    , effectful
+    , flora
+    , hsec-core
+    , hsec-sync
+    , hsec-tools
+    , osv
+    , pandoc-types
+    , pg-entity
+    , pg-transact-effectful
+    , postgresql-simple
+    , text
+    , time
+    , tracing
+    , tracing-effectful
+    , uuid
+    , validation-selective
+    , vector
+
 library flora-web
   import:          common-extensions
   import:          common-ghc-options
@@ -481,6 +539,7 @@ test-suite flora-test
     , exceptions
     , filepath
     , flora
+    , flora-advisories
     , flora-web
     , hedgehog
     , http-client
@@ -505,6 +564,7 @@ test-suite flora-test
     , tasty-test-reporter
     , text
     , time
+    , tracing-effectful
     , uuid
     , vector
     , vector-algorithms
@@ -512,6 +572,7 @@ test-suite flora-test
 
   ghc-options:    -fplugin=Effectful.Plugin
   other-modules:
+    Flora.AdvisorySpec
     Flora.BlobSpec
     Flora.CabalSpec
     Flora.CategorySpec
diff --git a/hie.yaml b/hie.yaml
index 62f99a0a..be02b134 100644
--- a/hie.yaml
+++ b/hie.yaml
@@ -1,12 +1,15 @@
 cradle:
   cabal:
-    - path: "././src/core"
+    - path: "./src/core"
       component: "lib:flora"
 
-    - path: "././src/datatypes"
+    - path: "./src/datatypes"
       component: "lib:flora"
 
-    - path: "././src/web"
+    - path: "./src/advisories"
+      component: "lib:flora"
+
+    - path: "./src/web"
       component: "flora:lib:flora-web"
 
     - path: "./src/jobs-worker"
diff --git a/migrations/20241011153354_create_security_advisories.sql b/migrations/20241011153354_create_security_advisories.sql
new file mode 100644
index 00000000..12638c21
--- /dev/null
+++ b/migrations/20241011153354_create_security_advisories.sql
@@ -0,0 +1,16 @@
+CREATE TABLE IF NOT EXISTS security_advisories (
+    advisory_id uuid PRIMARY KEY
+  , hsec_id text NOT NULL
+  , modified timestamptz NOT NULL
+  , published timestamptz NOT NULL
+  , capecs integer[] NOT NULL
+  , cwes integer[] NOT NULL
+  , keywords text[] NOT NULL
+  , aliases text[] NOT NULL
+  , related text[] NOT NULL
+  , advisory_references jsonb NOT NULL
+  , pandoc jsonb NOT NULL
+  , html text NOT NULL
+  , summary text NOT NULL
+  , details text NOT NULL
+);
diff --git a/migrations/20241011154110_create_affected_packages.sql b/migrations/20241011154110_create_affected_packages.sql
new file mode 100644
index 00000000..785352e2
--- /dev/null
+++ b/migrations/20241011154110_create_affected_packages.sql
@@ -0,0 +1,15 @@
+CREATE TABLE IF NOT EXISTS affected_packages (
+    affected_package_id uuid PRIMARY KEY
+  , advisory_id uuid REFERENCES security_advisories
+  , package_id uuid REFERENCES packages NOT NULL
+  , cvss text NOT NULL
+  , architectures text[]
+  , operating_systems text[]
+  , declarations text[][]
+);
+
+CREATE INDEX affected_packages_advisory_id_fkey
+  ON affected_packages (advisory_id);
+
+CREATE INDEX affected_packages_package_id_fkey
+  ON affected_packages (package_id);
diff --git a/migrations/20241014081932_create_affected_version_ranges.sql b/migrations/20241014081932_create_affected_version_ranges.sql
new file mode 100644
index 00000000..90f08086
--- /dev/null
+++ b/migrations/20241014081932_create_affected_version_ranges.sql
@@ -0,0 +1,15 @@
+CREATE TABLE IF NOT EXISTS affected_version_ranges (
+    affected_version_id uuid PRIMARY KEY
+  , affected_package_id uuid REFERENCES affected_packages NOT NULL
+  , introduced_version uuid REFERENCES releases (release_id) NOT NULL
+  , fixed_version uuid REFERENCES releases (release_id)
+);
+
+CREATE INDEX affected_version_ranges_affected_package_id_fkey
+  ON affected_version_ranges (affected_package_id);
+
+CREATE INDEX affected_version_ranges_introduced_version_fkey
+  ON affected_version_ranges (introduced_version);
+
+CREATE INDEX affected_version_ranges_fixed_version_fkey
+  ON affected_version_ranges (fixed_version);
diff --git a/src/advisories/Advisories/Import.hs b/src/advisories/Advisories/Import.hs
new file mode 100644
index 00000000..11ec54ce
--- /dev/null
+++ b/src/advisories/Advisories/Import.hs
@@ -0,0 +1,170 @@
+module Advisories.Import where
+
+import Data.Foldable (forM_, traverse_)
+import Data.Function ((&))
+import Data.List.NonEmpty (NonEmpty)
+import Data.List.NonEmpty qualified as NonEmpty
+import Data.Set qualified as Set
+import Data.UUID.V4 qualified as UUID
+import Data.Vector (Vector)
+import Data.Vector qualified as Vector
+import Effectful
+import Effectful.Error.Static
+import Effectful.PostgreSQL.Transact.Effect (DB)
+import Effectful.Trace
+import Monitor.Tracing qualified as Tracing
+import Security.Advisories.Core.Advisory
+import Security.Advisories.Filesystem (listAdvisories)
+import Validation (Validation (..))
+
+import Advisories.Import.Error
+import Advisories.Model.Advisory.Types
+import Advisories.Model.Advisory.Update qualified as Update
+import Advisories.Model.Affected.Types
+import Advisories.Model.Affected.Update qualified as Update
+import Flora.Import.Package
+import Flora.Model.Package.Guard (guardThatPackageExists)
+import Flora.Model.Package.Types
+import Flora.Model.Release.Guard (guardThatReleaseExists)
+import Flora.Model.Release.Types
+import OSV.Reference.Orphans
+
+-- | List deduplicated parsed Advisories
+importAdvisories
+  :: ( DB :> es
+     , Trace :> es
+     , IOE :> es
+     , Error (NonEmpty AdvisoryImportError) :> es
+     )
+  => FilePath
+  -> Eff es ()
+importAdvisories root = Tracing.rootSpan alwaysSampled "import-advisories" $ do
+  result <- Tracing.childSpan "listAdvisories" $ listAdvisories root
+  case result of
+    Failure failures ->
+      let errors = case NonEmpty.nonEmpty failures of
+            Just nonEmptyFailures -> fmap AdvisoryParsingError nonEmptyFailures
+            Nothing -> NonEmpty.singleton FackinHell
+       in throwError errors
+    Success advisoryList -> do
+      forM_ advisoryList $ \advisory -> importAdvisory advisory
+
+importAdvisory
+  :: ( DB :> es
+     , Trace :> es
+     , IOE :> es
+     , Error (NonEmpty AdvisoryImportError) :> es
+     )
+  => Advisory
+  -> Eff es ()
+importAdvisory advisory = do
+  advisoryId <- AdvisoryId <$> liftIO UUID.nextRandom
+  let advisoryAffectedPackages = Vector.fromList advisory.advisoryAffected
+  let advisoryDAO = processAdvisory advisoryId advisory
+  Update.insertAdvisory advisoryDAO
+  processAffectedPackages advisoryId advisoryAffectedPackages
+
+processAdvisory
+  :: AdvisoryId
+  -> Advisory
+  -> AdvisoryDAO
+processAdvisory advisoryId advisory =
+  AdvisoryDAO
+    { advisoryId = advisoryId
+    , hsecId = advisory.advisoryId
+    , modified = advisory.advisoryModified
+    , published = advisory.advisoryPublished
+    , capecs = Vector.fromList advisory.advisoryCAPECs
+    , cwes = Vector.fromList advisory.advisoryCWEs
+    , keywords = Vector.fromList advisory.advisoryKeywords
+    , aliases = Vector.fromList advisory.advisoryAliases
+    , related = Vector.fromList advisory.advisoryRelated
+    , advisoryReferences = References $ Vector.fromList advisory.advisoryReferences
+    , pandoc = advisory.advisoryPandoc
+    , html = advisory.advisoryHtml
+    , summary = advisory.advisorySummary
+    , details = advisory.advisoryDetails
+    }
+
+processAffectedPackages
+  :: ( IOE :> es
+     , DB :> es
+     , Trace :> es
+     , Error (NonEmpty AdvisoryImportError) :> es
+     )
+  => AdvisoryId
+  -> Vector Affected
+  -> Eff es ()
+processAffectedPackages advisoryId affectedPackages = do
+  forM_ affectedPackages (processAffectedPackage advisoryId)
+
+processAffectedPackage
+  :: ( IOE :> es
+     , DB :> es
+     , Error (NonEmpty AdvisoryImportError) :> es
+     , Trace :> es
+     )
+  => AdvisoryId
+  -> Affected
+  -> Eff es ()
+processAffectedPackage advisoryId affected = do
+  affectedPackageId <- AffectedPackageId <$> liftIO UUID.nextRandom
+  let packageName =
+        case affected.affectedComponentIdentifier of
+          Hackage affectedPackageName -> PackageName affectedPackageName
+          GHC _ -> PackageName "ghc"
+  let namespace = chooseNamespace packageName ("hackage", Set.empty)
+  package <- guardThatPackageExists namespace packageName $ \_ _ ->
+    throwError (NonEmpty.singleton $ AffectedPackageNotFound namespace packageName)
+  let declarations =
+        affected.affectedDeclarations
+          & fmap (uncurry AffectedDeclaration)
+          & Vector.fromList
+  let affectedPackageDAO =
+        AffectedPackageDAO
+          { affectedPackageId = affectedPackageId
+          , advisoryId = advisoryId
+          , packageId = package.packageId
+          , cvss = affected.affectedCVSS
+          , architectures = fmap Vector.fromList affected.affectedArchitectures
+          , operatingSystems = fmap Vector.fromList affected.affectedOS
+          , declarations = declarations
+          }
+  Update.insertAffectedPackage affectedPackageDAO
+  processAffectedVersionRanges affectedPackageId package.packageId affected.affectedVersions
+
+processAffectedVersionRanges
+  :: ( IOE :> es
+     , DB :> es
+     , Trace :> es
+     , Error (NonEmpty AdvisoryImportError) :> es
+     )
+  => AffectedPackageId
+  -> PackageId
+  -> [AffectedVersionRange]
+  -> Eff es ()
+processAffectedVersionRanges affectedPackageId packageId affectedVersions = do
+  traverse_
+    ( \affectedVersion -> do
+        affectedVersionId <- AffectedVersionId <$> liftIO UUID.nextRandom
+        introducedReleaseId <- do
+          release <- guardThatReleaseExists packageId affectedVersion.affectedVersionRangeIntroduced $ \version ->
+            throwError (NonEmpty.singleton $ AffectedVersionNotFound packageId version)
+          pure release.releaseId
+        mFixedReleaseId <- case affectedVersion.affectedVersionRangeFixed of
+          Nothing -> pure Nothing
+          Just version -> do
+            release <- guardThatReleaseExists packageId version $ \releaseVersion ->
+              throwError (NonEmpty.singleton $ AffectedVersionNotFound packageId releaseVersion)
+            pure $ Just release.releaseId
+        let versionRangeDAO =
+              AffectedVersionRangeDAO
+                { affectedVersionId = affectedVersionId
+                , affectedPackageId = affectedPackageId
+                , introducedVersion = introducedReleaseId
+                , fixedVersion = mFixedReleaseId
+                }
+
+        Update.insertAffectedVersionRange versionRangeDAO
+    )
+    affectedVersions
diff --git a/src/advisories/Advisories/Import/Error.hs b/src/advisories/Advisories/Import/Error.hs
new file mode 100644
index 00000000..8d761a78
--- /dev/null
+++ b/src/advisories/Advisories/Import/Error.hs
@@ -0,0 +1,14 @@
+module Advisories.Import.Error where
+
+import Distribution.Types.Version (Version)
+import GHC.Generics
+import Security.Advisories.Parse
+
+import Flora.Model.Package.Types
+
+data AdvisoryImportError
+  = AffectedPackageNotFound Namespace PackageName
+  | AdvisoryParsingError (FilePath, ParseAdvisoryError)
+  | AffectedVersionNotFound PackageId Version
+  | FackinHell
+  deriving stock (Eq, Show, Generic)
diff --git a/src/advisories/Advisories/Model/Advisory/Query.hs b/src/advisories/Advisories/Model/Advisory/Query.hs
new file mode 100644
index 00000000..a19f1830
--- /dev/null
+++ b/src/advisories/Advisories/Model/Advisory/Query.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Advisories.Model.Advisory.Query where
+
+import Advisories.Model.Affected.Types
+import Data.Vector (Vector)
+import Database.PostgreSQL.Entity
+import Database.PostgreSQL.Entity.Types
+import Database.PostgreSQL.Simple (Only (Only))
+import Effectful
+import Effectful.PostgreSQL.Transact.Effect
+import Security.Advisories.Core.HsecId
+
+import Advisories.Model.Advisory.Types
+import Flora.Model.Package.Types
+
+getAdvisoryById :: DB :> es => AdvisoryId -> Eff es (Maybe AdvisoryDAO)
+getAdvisoryById advisoryId = dbtToEff $ selectById (Only advisoryId)
+
+getAdvisoryByHsecId :: DB :> es => HsecId -> Eff es (Maybe AdvisoryDAO)
+getAdvisoryByHsecId hsecId = dbtToEff $ selectOneByField [field| hsec_id |] (Only hsecId)
+
+getAdvisoriesByPackageId
+  :: DB :> es
+  => PackageId
+  -> Eff es (Vector AdvisoryDAO)
+getAdvisoriesByPackageId packageId =
+  dbtToEff $
+    joinSelectOneByField @AdvisoryDAO @AffectedPackageDAO
+      [field| advisory_id |]
+      [field| package_id |]
+      packageId
diff --git a/src/advisories/Advisories/Model/Advisory/Types.hs b/src/advisories/Advisories/Model/Advisory/Types.hs
new file mode 100644
index 00000000..92193a21
--- /dev/null
+++ b/src/advisories/Advisories/Model/Advisory/Types.hs
@@ -0,0 +1,49 @@
+module Advisories.Model.Advisory.Types where
+
+import Control.DeepSeq
+import Data.Aeson
+import Data.Text
+import Data.Time
+import Data.UUID
+import Data.Vector
+import Database.PostgreSQL.Entity.Types
+import Database.PostgreSQL.Simple (FromRow, ToRow)
+import Database.PostgreSQL.Simple.FromField (FromField (..))
+import Database.PostgreSQL.Simple.ToField (ToField (..))
+import GHC.Generics
+import Security.Advisories.Core.Advisory
+import Security.Advisories.Core.HsecId
+import Text.Pandoc.Definition
+
+import Advisories.CAPEC.Orphans ()
+import Advisories.CWE.Orphans ()
+import Advisories.HsecId.Orphans ()
+import Advisories.Keyword.Orphans ()
+import OSV.Reference.Orphans (References (..))
+import Pandoc.Orphans ()
+
+newtype AdvisoryId = AdvisoryId {getAdvisoryId :: UUID}
+  deriving stock (Generic, Show)
+  deriving newtype (Eq, Ord, FromJSON, ToJSON, FromField, ToField, NFData)
+
+data AdvisoryDAO = AdvisoryDAO
+  { advisoryId :: AdvisoryId
+  , hsecId :: HsecId
+  , modified :: UTCTime
+  , published :: UTCTime
+  , capecs :: Vector CAPEC
+  , cwes :: Vector CWE
+  , keywords :: Vector Keyword
+  , aliases :: Vector Text
+  , related :: Vector Text
+  , advisoryReferences :: References
+  , pandoc :: Pandoc
+  , html :: Text
+  , summary :: Text
+  , details :: Text
+  }
+  deriving stock (Show, Generic)
+  deriving anyclass (FromRow, ToRow, NFData)
+  deriving
+    (Entity)
+    via (GenericEntity '[TableName "security_advisories"] AdvisoryDAO)
diff --git a/src/advisories/Advisories/Model/Advisory/Update.hs b/src/advisories/Advisories/Model/Advisory/Update.hs
new file mode 100644
index 00000000..28914053
--- /dev/null
+++ b/src/advisories/Advisories/Model/Advisory/Update.hs
@@ -0,0 +1,10 @@
+module Advisories.Model.Advisory.Update where
+
+import Database.PostgreSQL.Entity (insert)
+import Effectful
+import Effectful.PostgreSQL.Transact.Effect (DB, dbtToEff)
+
+import Advisories.Model.Advisory.Types
+
+insertAdvisory :: DB :> es => AdvisoryDAO -> Eff es ()
+insertAdvisory = dbtToEff . insert @AdvisoryDAO
diff --git a/src/advisories/Advisories/Model/Affected/Query.hs b/src/advisories/Advisories/Model/Affected/Query.hs
new file mode 100644
index 00000000..4df13297
--- /dev/null
+++ b/src/advisories/Advisories/Model/Affected/Query.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Advisories.Model.Affected.Query where
+
+import Data.Vector (Vector)
+import Database.PostgreSQL.Entity
+import Database.PostgreSQL.Entity.Types (field)
+import Database.PostgreSQL.Simple (Only (..))
+import Effectful
+import Effectful.PostgreSQL.Transact.Effect (DB, dbtToEff)
+import Security.Advisories.Core.HsecId
+
+import Advisories.HsecId.Orphans ()
+import Advisories.Model.Advisory.Types
+import Advisories.Model.Affected.Types
+
+getAffectedPackageById
+  :: DB :> es
+  => AffectedPackageId
+  -> Eff es (Maybe AffectedPackageDAO)
+getAffectedPackageById affectedPackageId = dbtToEff $ selectById (Only affectedPackageId)
+
+getAffectedPackagesByAdvisoryId
+  :: DB :> es
+  => AdvisoryId
+  -> Eff es (Vector AffectedPackageDAO)
+getAffectedPackagesByAdvisoryId advisoryId =
+  dbtToEff $ selectManyByField @AffectedPackageDAO [field| advisory_id |] (Only advisoryId)
+
+getAffectedPackagesByHsecId
+  :: DB :> es
+  => HsecId
+  -> Eff es (Vector AffectedPackageDAO)
+getAffectedPackagesByHsecId hsecId =
+  dbtToEff $
+    joinSelectOneByField @AffectedPackageDAO @AdvisoryDAO
+      [field| advisory_id |]
+      [field| hsec_id |]
+      hsecId
diff --git a/src/advisories/Advisories/Model/Affected/Types.hs b/src/advisories/Advisories/Model/Affected/Types.hs
new file mode 100644
index 00000000..7350a715
--- /dev/null
+++ b/src/advisories/Advisories/Model/Affected/Types.hs
@@ -0,0 +1,75 @@
+module Advisories.Model.Affected.Types where
+
+import Control.DeepSeq
+import Data.Aeson
+import Data.Text (Text)
+import Data.UUID (UUID)
+import Data.Vector (Vector)
+import Database.PostgreSQL.Entity.Types
+import Database.PostgreSQL.Simple (FromRow, ToRow)
+import Database.PostgreSQL.Simple.FromField
+import Database.PostgreSQL.Simple.Newtypes
+import Database.PostgreSQL.Simple.ToField
+import Distribution.Types.VersionRange (VersionRange)
+import GHC.Generics
+import Security.Advisories.Core.Advisory
+import Security.CVSS (CVSS)
+
+import Advisories.AffectedVersionRange.Orphans ()
+import Advisories.CVSS.Orphans ()
+import Advisories.Model.Advisory.Types
+import Advisories.System.Orphans ()
+import Distribution.Orphans.ConfVar ()
+import Distribution.Orphans.Version ()
+import Flora.Model.Package.Types
+import Flora.Model.Release.Types
+
+newtype AffectedPackageId = AffectedPackageId {getAffectedPackageId :: UUID}
+  deriving stock (Generic, Show)
+  deriving
+    (Eq, Ord, FromJSON, ToJSON, FromField, ToField, NFData)
+    via UUID
+
+data AffectedPackageDAO = AffectedPackageDAO
+  { affectedPackageId :: AffectedPackageId
+  , advisoryId :: AdvisoryId
+  , packageId :: PackageId
+  , cvss :: CVSS
+  , architectures :: Maybe (Vector Architecture)
+  , operatingSystems :: Maybe (Vector OS)
+  , declarations :: Vector AffectedDeclaration
+  }
+  deriving stock (Show, Generic)
+  deriving anyclass (FromRow, ToRow, NFData)
+  deriving
+    (Entity)
+    via (GenericEntity '[TableName "affected_packages"] AffectedPackageDAO)
+
+data AffectedDeclaration = AffectedDeclaration
+  { canonicalPath :: Text
+  , affectedRange :: VersionRange
+  }
+  deriving stock (Show, Generic)
+  deriving anyclass (NFData, ToJSON, FromJSON)
+
+deriving via (Aeson AffectedDeclaration) instance ToField AffectedDeclaration
+
+deriving via (Aeson AffectedDeclaration) instance FromField AffectedDeclaration
+
+newtype AffectedVersionId = AffectedVersionId {getAffectedVersionId :: UUID}
+  deriving stock (Generic, Show)
+  deriving
+    (Eq, Ord, FromJSON, ToJSON, FromField, ToField, NFData)
+    via UUID
+
+data AffectedVersionRangeDAO = AffectedVersionRangeDAO
+  { affectedVersionId :: AffectedVersionId
+  , affectedPackageId :: AffectedPackageId
+  , introducedVersion :: ReleaseId
+  , fixedVersion :: Maybe ReleaseId
+  }
+  deriving stock (Show, Generic)
+  deriving anyclass (FromRow, ToRow, NFData)
+  deriving
+    (Entity)
+    via (GenericEntity '[TableName "affected_version_ranges"] AffectedVersionRangeDAO)
diff --git a/src/advisories/Advisories/Model/Affected/Update.hs b/src/advisories/Advisories/Model/Affected/Update.hs
new file mode 100644
index 00000000..d7166e20
--- /dev/null
+++ b/src/advisories/Advisories/Model/Affected/Update.hs
@@ -0,0 +1,19 @@
+module Advisories.Model.Affected.Update where
+
+import Database.PostgreSQL.Entity (insert)
+import Effectful
+import Effectful.PostgreSQL.Transact.Effect (DB, dbtToEff)
+
+import Advisories.Model.Affected.Types
+
+insertAffectedPackage
+  :: DB :> es
+  => AffectedPackageDAO
+  -> Eff es ()
+insertAffectedPackage = dbtToEff . insert @AffectedPackageDAO
+
+insertAffectedVersionRange
+  :: DB :> es
+  => AffectedVersionRangeDAO
+  -> Eff es ()
+insertAffectedVersionRange = dbtToEff . insert @AffectedVersionRangeDAO
diff --git a/src/core/Flora/Model/Package/Guard.hs b/src/core/Flora/Model/Package/Guard.hs
new file mode 100644
index 00000000..bfc475ca
--- /dev/null
+++ b/src/core/Flora/Model/Package/Guard.hs
@@ -0,0 +1,27 @@
+module Flora.Model.Package.Guard where
+
+import Effectful
+import Effectful.PostgreSQL.Transact.Effect
+import Effectful.Trace
+import Monitor.Tracing qualified as Tracing
+
+import Flora.Model.Package
+import Flora.Model.Package.Query qualified as Query
+
+guardThatPackageExists
+  :: (DB :> es, Trace :> es)
+  => Namespace
+  -> PackageName
+  -> (Namespace -> PackageName -> Eff es Package)
+  -- ^ Action to run if the package does not exist
+  -> Eff es Package
+guardThatPackageExists namespace packageName action = do
+  result <-
+    Tracing.childSpan "Query.getPackageByNamespaceAndName " $
+      Query.getPackageByNamespaceAndName namespace packageName
+  case result of
+    Nothing -> action namespace packageName
+    Just package ->
+      case package.status of
+        FullyImportedPackage -> pure package
+        UnknownPackage -> action namespace packageName
diff --git a/src/core/Flora/Model/Package/Query.hs b/src/core/Flora/Model/Package/Query.hs
index ac77335f..fe9b82ab 100644
--- a/src/core/Flora/Model/Package/Query.hs
+++ b/src/core/Flora/Model/Package/Query.hs
@@ -406,7 +406,6 @@ getRequirementsQuery singleComponentType =
       SELECT DISTINCT dependency.namespace
                     , dependency.name
                     , req.requirement
-              
       |]
     tablesSingleType =
       [sql|
@@ -544,7 +543,7 @@ searchPackageByNamespace (offset, limit) namespace searchString =
               , lv."uploaded_at"
               , lv."revised_at"
         FROM latest_versions as lv
-        WHERE 
+        WHERE
         ? <% lv."name"
         AND lv."namespace" = ?
         GROUP BY
diff --git a/src/core/Flora/Model/PersistentSession.hs b/src/core/Flora/Model/PersistentSession.hs
index 7f3f6ed7..3d5baea3 100644
--- a/src/core/Flora/Model/PersistentSession.hs
+++ b/src/core/Flora/Model/PersistentSession.hs
@@ -69,7 +69,7 @@ persistSession
 persistSession persistentSessionId userId = do
   persistentSession <- newPersistentSession userId persistentSessionId
   insertSession persistentSession
-  pure $ persistentSession.persistentSessionId
+  pure persistentSession.persistentSessionId
 
 insertSession :: DB :> es => PersistentSession -> Eff es ()
 insertSession = dbtToEff . insert @PersistentSession
diff --git a/src/core/Flora/Model/Release/Guard.hs b/src/core/Flora/Model/Release/Guard.hs
new file mode 100644
index 00000000..07ae1543
--- /dev/null
+++ b/src/core/Flora/Model/Release/Guard.hs
@@ -0,0 +1,26 @@
+module Flora.Model.Release.Guard where
+
+import Distribution.Types.Version (Version)
+import Effectful
+import Effectful.PostgreSQL.Transact.Effect
+import Effectful.Trace
+import Monitor.Tracing qualified as Tracing
+
+import Flora.Model.Package.Types
+import Flora.Model.Release.Query qualified as Query
+import Flora.Model.Release.Types
+
+guardThatReleaseExists
+  :: (DB :> es, Trace :> es)
+  => PackageId
+  -> Version
+  -> (Version -> Eff es Release)
+  -- ^ Action to run if the package does not exist
+  -> Eff es Release
+guardThatReleaseExists packageId version action = do
+  result <-
+    Tracing.childSpan "Query.getReleaseByVersion" $
+      Query.getReleaseByVersion packageId version
+  case result of
+    Just release -> pure release
+    Nothing -> action version
diff --git a/src/datatypes/Advisories/AffectedVersionRange/Orphans.hs b/src/datatypes/Advisories/AffectedVersionRange/Orphans.hs
new file mode 100644
index 00000000..28bb7d8a
--- /dev/null
+++ b/src/datatypes/Advisories/AffectedVersionRange/Orphans.hs
@@ -0,0 +1,29 @@
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Advisories.AffectedVersionRange.Orphans where
+
+import Control.DeepSeq
+import Data.Aeson
+import Data.Text.Display
+import Security.Advisories.Core.Advisory
+
+import Distribution.Orphans.Version ()
+
+instance Display AffectedVersionRange where
+  displayBuilder = displayBuilder . show
+
+instance ToJSON AffectedVersionRange where
+  toJSON o =
+    object
+      [ "introduced" .= o.affectedVersionRangeIntroduced
+      , "fixed" .= o.affectedVersionRangeFixed
+      ]
+
+instance FromJSON AffectedVersionRange where
+  parseJSON = withObject "AffectedVersionRange" $ \o -> do
+    affectedVersionRangeIntroduced <- o .: "introduced"
+    affectedVersionRangeFixed <- o .:? "fixed"
+    pure AffectedVersionRange{..}
+
+instance NFData AffectedVersionRange where
+  rnf a = seq a ()
diff --git a/src/datatypes/Advisories/CAPEC/Orphans.hs b/src/datatypes/Advisories/CAPEC/Orphans.hs
new file mode 100644
index 00000000..cab7af1a
--- /dev/null
+++ b/src/datatypes/Advisories/CAPEC/Orphans.hs
@@ -0,0 +1,14 @@
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Advisories.CAPEC.Orphans where
+
+import Control.DeepSeq
+import Database.PostgreSQL.Simple.FromField
+import Database.PostgreSQL.Simple.ToField
+import Security.Advisories.Core.Advisory
+
+deriving via Integer instance FromField CAPEC
+
+deriving via Integer instance ToField CAPEC
+
+deriving via Integer instance NFData CAPEC
diff --git a/src/datatypes/Advisories/CVSS/Orphans.hs b/src/datatypes/Advisories/CVSS/Orphans.hs
new file mode 100644
index 00000000..ba112ae3
--- /dev/null
+++ b/src/datatypes/Advisories/CVSS/Orphans.hs
@@ -0,0 +1,26 @@
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Advisories.CVSS.Orphans where
+
+import Control.DeepSeq
+import Data.Text.Encoding qualified as Text
+import Database.PostgreSQL.Simple.FromField
+import Database.PostgreSQL.Simple.ToField
+import Security.CVSS (CVSS)
+import Security.CVSS qualified as CVSS
+
+instance ToField CVSS where
+  toField = toField . CVSS.cvssVectorStringOrdered
+
+instance FromField CVSS where
+  fromField f Nothing = returnError UnexpectedNull f ""
+  fromField f (Just bs) =
+    let field = Text.decodeUtf8 bs
+     in case CVSS.parseCVSS field of
+          Right cvss -> pure cvss
+          Left err ->
+            returnError ConversionFailed f $
+              "Conversion error. Could not parse CVSS: " <> show err
+
+instance NFData CVSS where
+  rnf a = seq a ()
diff --git a/src/datatypes/Advisories/CWE/Orphans.hs b/src/datatypes/Advisories/CWE/Orphans.hs
new file mode 100644
index 00000000..440a07be
--- /dev/null
+++ b/src/datatypes/Advisories/CWE/Orphans.hs
@@ -0,0 +1,14 @@
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Advisories.CWE.Orphans where
+
+import Control.DeepSeq
+import Database.PostgreSQL.Simple.FromField
+import Database.PostgreSQL.Simple.ToField
+import Security.Advisories.Core.Advisory
+
+deriving via Integer instance FromField CWE
+
+deriving via Integer instance ToField CWE
+
+deriving via Integer instance NFData CWE
diff --git a/src/datatypes/Advisories/HsecId/Orphans.hs b/src/datatypes/Advisories/HsecId/Orphans.hs
new file mode 100644
index 00000000..52913d21
--- /dev/null
+++ b/src/datatypes/Advisories/HsecId/Orphans.hs
@@ -0,0 +1,28 @@
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Advisories.HsecId.Orphans where
+
+import Control.DeepSeq
+import Data.Text qualified as Text
+import Data.Text.Display
+import Data.Text.Encoding qualified as Text
+import Database.PostgreSQL.Simple.FromField (FromField (..), ResultError (..), returnError)
+import Database.PostgreSQL.Simple.ToField (Action (..), ToField (..))
+import Security.Advisories.Core.HsecId
+
+deriving via ShowInstance HsecId instance Display HsecId
+
+instance ToField HsecId where
+  toField = Escape . Text.encodeUtf8 . display
+
+instance FromField HsecId where
+  fromField f Nothing = returnError UnexpectedNull f ""
+  fromField _ (Just bs) | Just hsecId <- parseHsecId (Text.unpack $ Text.decodeUtf8 bs) = pure hsecId
+  fromField f (Just bs) =
+    returnError ConversionFailed f $
+      Text.unpack $
+        "Conversion error: Expected parseable HsecId but instead got "
+          <> Text.decodeUtf8 bs
+
+instance NFData HsecId where
+  rnf a = seq a ()
diff --git a/src/datatypes/Advisories/Keyword/Orphans.hs b/src/datatypes/Advisories/Keyword/Orphans.hs
new file mode 100644
index 00000000..6e65dc47
--- /dev/null
+++ b/src/datatypes/Advisories/Keyword/Orphans.hs
@@ -0,0 +1,15 @@
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Advisories.Keyword.Orphans where
+
+import Control.DeepSeq
+import Data.Text
+import Database.PostgreSQL.Simple.FromField
+import Database.PostgreSQL.Simple.ToField
+import Security.Advisories.Core.Advisory
+
+deriving via Text instance FromField Keyword
+
+deriving via Text instance ToField Keyword
+
+deriving via Text instance NFData Keyword
diff --git a/src/datatypes/Advisories/System/Orphans.hs b/src/datatypes/Advisories/System/Orphans.hs
new file mode 100644
index 00000000..a57f05cf
--- /dev/null
+++ b/src/datatypes/Advisories/System/Orphans.hs
@@ -0,0 +1,29 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Advisories.System.Orphans where
+
+import Control.DeepSeq
+import Data.Aeson (camelTo2)
+import Data.Aeson.TH
+import Database.PostgreSQL.Simple.FromField
+import Database.PostgreSQL.Simple.Newtypes
+import Database.PostgreSQL.Simple.ToField
+import Security.Advisories.Core.Advisory
+
+$(deriveJSON defaultOptions{fieldLabelModifier = camelTo2 '_'} ''OS)
+$(deriveJSON defaultOptions{fieldLabelModifier = camelTo2 '_'} ''Architecture)
+
+deriving via (Aeson OS) instance ToField OS
+
+deriving via (Aeson OS) instance FromField OS
+
+deriving via (Aeson Architecture) instance ToField Architecture
+
+deriving via (Aeson Architecture) instance FromField Architecture
+
+instance NFData Architecture where
+  rnf a = seq a ()
+
+instance NFData OS where
+  rnf a = seq a ()
diff --git a/src/datatypes/Distribution/Orphans/ConfVar.hs b/src/datatypes/Distribution/Orphans/ConfVar.hs
index d55e0d2d..de4154fb 100644
--- a/src/datatypes/Distribution/Orphans/ConfVar.hs
+++ b/src/datatypes/Distribution/Orphans/ConfVar.hs
@@ -3,13 +3,12 @@
 
 module Distribution.Orphans.ConfVar where
 
+import Data.Aeson (camelTo2)
 import Data.Aeson.TH
 import Distribution.System (Arch, OS)
 import Distribution.Types.Condition
 import Distribution.Types.ConfVar
 
-import Data.Aeson (camelTo2)
-
 import Distribution.Orphans.CompilerFlavor ()
 import Distribution.Orphans.PackageFlag ()
 import Distribution.Orphans.Version ()
diff --git a/src/datatypes/Distribution/Orphans/Version.hs b/src/datatypes/Distribution/Orphans/Version.hs
index 7d470ae6..ef131b4c 100644
--- a/src/datatypes/Distribution/Orphans/Version.hs
+++ b/src/datatypes/Distribution/Orphans/Version.hs
@@ -11,6 +11,7 @@ import Data.Text qualified as Text
 import Data.Text.Display
 import Data.Text.Lazy.Builder qualified as Builder
 import Database.PostgreSQL.Simple.FromField
+import Database.PostgreSQL.Simple.Newtypes
 import Database.PostgreSQL.Simple.ToField
 import Database.PostgreSQL.Simple.Types (PGArray (..))
 import Distribution.Parsec
@@ -66,3 +67,7 @@ instance FromJSON VersionRange where
       ( \s ->
           maybe (fail "Invalid version range") pure (simpleParsec $ Text.unpack s)
       )
+
+deriving via (Aeson VersionRange) instance ToField VersionRange
+
+deriving via (Aeson VersionRange) instance FromField VersionRange
diff --git a/src/datatypes/OSV/Reference/Orphans.hs b/src/datatypes/OSV/Reference/Orphans.hs
new file mode 100644
index 00000000..3696481c
--- /dev/null
+++ b/src/datatypes/OSV/Reference/Orphans.hs
@@ -0,0 +1,26 @@
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module OSV.Reference.Orphans where
+
+import Control.DeepSeq
+import Data.Aeson
+import Data.Vector (Vector)
+import Database.PostgreSQL.Simple.FromField
+import Database.PostgreSQL.Simple.Newtypes (Aeson (..))
+import Database.PostgreSQL.Simple.ToField
+import GHC.Generics (Generic)
+import Security.OSV
+
+deriving via (Aeson Reference) instance ToField Reference
+
+deriving via (Aeson Reference) instance FromField Reference
+
+instance NFData Reference where
+  rnf a = seq a ()
+
+newtype References = References (Vector Reference)
+  deriving stock (Eq, Show, Generic)
+  deriving newtype (FromJSON, ToJSON, NFData)
+  deriving
+    (FromField, ToField)
+    via (Aeson References)
diff --git a/src/datatypes/Pandoc/Orphans.hs b/src/datatypes/Pandoc/Orphans.hs
new file mode 100644
index 00000000..6ef6e889
--- /dev/null
+++ b/src/datatypes/Pandoc/Orphans.hs
@@ -0,0 +1,12 @@
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Pandoc.Orphans where
+
+import Database.PostgreSQL.Simple.FromField
+import Database.PostgreSQL.Simple.Newtypes (Aeson (..))
+import Database.PostgreSQL.Simple.ToField
+import Text.Pandoc.Definition
+
+deriving via (Aeson Pandoc) instance ToField Pandoc
+
+deriving via (Aeson Pandoc) instance FromField Pandoc
diff --git a/src/web/FloraWeb/API/Server/Packages.hs b/src/web/FloraWeb/API/Server/Packages.hs
index 37654f7e..2fc7b909 100644
--- a/src/web/FloraWeb/API/Server/Packages.hs
+++ b/src/web/FloraWeb/API/Server/Packages.hs
@@ -11,13 +11,14 @@ import Effectful.Trace
 import Servant hiding ((:>))
 
 import Flora.Model.Component.Query qualified as Query
+import Flora.Model.Package.Guard
 import Flora.Model.Package.Types
+import Flora.Model.Release.Guard (guardThatReleaseExists)
 import Flora.Model.Release.Query qualified as Query
 import Flora.Model.Release.Types
 import FloraWeb.API.Errors
 import FloraWeb.API.Routes.Packages qualified as Packages
 import FloraWeb.API.Routes.Packages.Types (PackageDTO, toPackageDTO)
-import FloraWeb.Common.Guards
 import FloraWeb.Types
 
 packagesServer :: ServerT Packages.API FloraEff
diff --git a/src/web/FloraWeb/Common/Guards.hs b/src/web/FloraWeb/Common/Guards.hs
index 459154d0..ac0d1d86 100644
--- a/src/web/FloraWeb/Common/Guards.hs
+++ b/src/web/FloraWeb/Common/Guards.hs
@@ -3,62 +3,23 @@
 module FloraWeb.Common.Guards where
 
 import Data.Text (Text)
-import Distribution.Types.Version (Version)
 import Effectful
 import Effectful.PostgreSQL.Transact.Effect
-import Effectful.Trace
 import FloraWeb.Pages.Templates
 import Log qualified
-import Monitor.Tracing qualified as Tracing
 import Optics.Core
 import Servant (respond)
 import Servant.API.UVerb
 
 import Flora.Model.Package
-import Flora.Model.Package.Query qualified as Query
 import Flora.Model.PackageIndex.Query as Query
 import Flora.Model.PackageIndex.Types (PackageIndex)
-import Flora.Model.Release.Query qualified as Query
-import Flora.Model.Release.Types (Release)
 import Flora.Model.User (User)
 import FloraWeb.Pages.Routes.Sessions (CreateSessionResponses)
 import FloraWeb.Pages.Templates.Screens.Sessions qualified as Sessions
 import FloraWeb.Session (Session)
 import FloraWeb.Types (FloraEff)
 
-guardThatPackageExists
-  :: (DB :> es, Trace :> es)
-  => Namespace
-  -> PackageName
-  -> (Namespace -> PackageName -> Eff es Package)
-  -- ^ Action to run if the package does not exist
-  -> Eff es Package
-guardThatPackageExists namespace packageName action = do
-  result <-
-    Tracing.childSpan "Query.getPackageByNamespaceAndName " $
-      Query.getPackageByNamespaceAndName namespace packageName
-  case result of
-    Nothing -> action namespace packageName
-    Just package ->
-      case package.status of
-        FullyImportedPackage -> pure package
-        UnknownPackage -> action namespace packageName
-
-guardThatReleaseExists
-  :: (DB :> es, Trace :> es)
-  => PackageId
-  -> Version
-  -> (Version -> Eff es Release)
-  -- ^ Action to run if the package does not exist
-  -> Eff es Release
-guardThatReleaseExists packageId version action = do
-  result <-
-    Tracing.childSpan "Query.getReleaseByVersion" $
-      Query.getReleaseByVersion packageId version
-  case result of
-    Just release -> pure release
-    Nothing -> action version
-
 guardThatPackageIndexExists
   :: DB :> es
   => Namespace
diff --git a/src/web/FloraWeb/Pages/Server/Packages.hs b/src/web/FloraWeb/Pages/Server/Packages.hs
index 05c85a58..f7d0886d 100644
--- a/src/web/FloraWeb/Pages/Server/Packages.hs
+++ b/src/web/FloraWeb/Pages/Server/Packages.hs
@@ -34,9 +34,11 @@ import Flora.Environment (FeatureEnv (..))
 import Flora.Model.BlobIndex.Query qualified as Query
 import Flora.Model.BlobStore.API (BlobStoreAPI)
 import Flora.Model.Package
+import Flora.Model.Package.Guard
 import Flora.Model.Package.Query qualified as Query
 import Flora.Model.PackageIndex.Query qualified as Query
 import Flora.Model.PackageIndex.Types (PackageIndex (..))
+import Flora.Model.Release.Guard
 import Flora.Model.Release.Query qualified as Query
 import Flora.Model.Release.Types
 import Flora.Model.User (User)
diff --git a/test/Flora/AdvisorySpec.hs b/test/Flora/AdvisorySpec.hs
new file mode 100644
index 00000000..c41810e4
--- /dev/null
+++ b/test/Flora/AdvisorySpec.hs
@@ -0,0 +1,24 @@
+module Flora.AdvisorySpec where
+
+import Advisories.Model.Advisory.Query qualified as Query
+import Data.Vector qualified as Vector
+import Flora.Model.Package.Query qualified as Query
+import Flora.Model.Package.Types
+import Flora.TestUtils
+
+spec :: TestEff TestTree
+spec =
+  testThese
+    "Advisory tests"
+    [ testThis "Fetching specific advisories by package id" testFetchingAllBiscuitHaskellAdvisories
+    ]
+
+testFetchingAllBiscuitHaskellAdvisories :: TestEff ()
+testFetchingAllBiscuitHaskellAdvisories = do
+  package <-
+    assertJust
+      =<< Query.getPackageByNamespaceAndName
+        (Namespace "hackage")
+        (PackageName "biscuit-haskell")
+  advisories <- Query.getAdvisoriesByPackageId package.packageId
+  assertEqual (Vector.length advisories) 2
diff --git a/test/Flora/PackageSpec.hs b/test/Flora/PackageSpec.hs
index bd79ab76..70289179 100644
--- a/test/Flora/PackageSpec.hs
+++ b/test/Flora/PackageSpec.hs
@@ -203,7 +203,7 @@ testGetNonDeprecatedPackages = do
 testReleaseDeprecation :: TestEff ()
 testReleaseDeprecation = do
   result <- Query.getHackagePackagesWithoutReleaseDeprecationInformation
-  assertEqual 71 (length result)
+  assertEqual 79 (length result)
 
   binary <- fromJust <$> Query.getPackageByNamespaceAndName (Namespace "haskell") (PackageName "binary")
   deprecatedBinaryVersion' <- assertJust =<< Query.getReleaseByVersion binary.packageId (mkVersion [0, 10, 0, 0])
diff --git a/test/Flora/TestUtils.hs b/test/Flora/TestUtils.hs
index a4e9792c..cc7b9a2e 100644
--- a/test/Flora/TestUtils.hs
+++ b/test/Flora/TestUtils.hs
@@ -104,6 +104,8 @@ import Effectful.Poolboy
 import Effectful.PostgreSQL.Transact.Effect
 import Effectful.Reader.Static
 import Effectful.Time
+import Effectful.Trace (Trace)
+import Effectful.Trace qualified as Trace
 import GHC.Generics
 import GHC.IO (mkUserError)
 import GHC.Stack
@@ -158,7 +160,7 @@ import Flora.Model.User
 import Flora.Model.User.Query qualified as Query
 import Flora.Model.User.Update qualified as Update
 
-type TestEff = Eff '[FileSystem, Poolboy, Fail, BlobStoreAPI, Reader TestEnv, DB, Log, Time, State (Set (Namespace, PackageName, Version)), IOE]
+type TestEff = Eff '[Trace, FileSystem, Poolboy, Fail, BlobStoreAPI, Reader TestEnv, DB, Log, Time, State (Set (Namespace, PackageName, Version)), IOE]
 
 data Fixtures = Fixtures
   { hackageUser :: User
@@ -197,6 +199,7 @@ runTestEff comp env = runEff $ do
       . runFailIO
       . runPoolboy (poolboySettingsWith env.dbConfig.connections)
       . runFileSystem
+      . Trace.runNoTrace
       $ comp
 
 testThis :: String -> TestEff () -> TestEff TestTree
diff --git a/test/Main.hs b/test/Main.hs
index 18f81bac..c31ce510 100644
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,14 +1,20 @@
 module Main where
 
 import Control.Monad (void)
+import Data.List.NonEmpty
 import Database.PostgreSQL.Entity.DBT (QueryNature (Delete), execute)
 import Effectful
+import Effectful.Error.Static
 import Effectful.PostgreSQL.Transact.Effect (DB, dbtToEff)
 import Sel.Hashing.Password qualified as Sel
+import System.Exit
 import System.IO
 import Test.Tasty
 import Test.Tasty.Runners.Reporter qualified as Reporter
 
+import Advisories.Import qualified as Advisories
+import Advisories.Import.Error
+import Flora.AdvisorySpec qualified as AdvisorySpec
 import Flora.BlobSpec qualified as BlobSpec
 import Flora.CabalSpec qualified as CabalSpec
 import Flora.CategorySpec qualified as CategorySpec
@@ -42,7 +48,12 @@ main = do
           Update.insertUser templateUser
           f' <- getFixtures
           importAllPackages f'
-          pure f'
+          result <- runErrorNoCallStack @(NonEmpty AdvisoryImportError) (Advisories.importAdvisories "./test/fixtures/Advisories")
+          case result of
+            Left errors -> do
+              liftIO $ print errors
+              liftIO exitFailure
+            Right _ -> pure f'
       )
       env
   spec <- traverse (\comp -> runTestEff comp env) (specs fixtures)
@@ -60,6 +71,7 @@ specs fixtures =
   , ImportSpec.spec fixtures
   , BlobSpec.spec
   , SearchSpec.spec fixtures
+  , AdvisorySpec.spec
   ]
 
 cleanUp :: DB :> es => Eff es ()
@@ -72,6 +84,9 @@ cleanUp = dbtToEff $ do
   void $ execute Delete "DELETE FROM downloads" ()
   void $ execute Delete "DELETE FROM requirements" ()
   void $ execute Delete "DELETE FROM package_components" ()
+  void $ execute Delete "DELETE FROM affected_version_ranges" ()
+  void $ execute Delete "DELETE FROM affected_packages" ()
+  void $ execute Delete "DELETE FROM security_advisories" ()
   void $ execute Delete "DELETE FROM releases" ()
   void $ execute Delete "DELETE FROM packages" ()
   void $ execute Delete "DELETE FROM package_indexes" ()
diff --git a/test/fixtures/Advisories/advisories/hackage/aeson/HSEC-2023-0001.md b/test/fixtures/Advisories/advisories/hackage/aeson/HSEC-2023-0001.md
new file mode 100644
index 00000000..b8aa88a2
--- /dev/null
+++ b/test/fixtures/Advisories/advisories/hackage/aeson/HSEC-2023-0001.md
@@ -0,0 +1,34 @@
+```toml
+[advisory]
+id = "HSEC-2023-0001"
+cwe = [328, 400]
+keywords = ["json", "dos", "historical"]
+aliases = ["CVE-2022-3433"]
+
+[[affected]]
+package = "aeson"
+cvss = "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H"
+
+[[affected.versions]]
+introduced = "0.4.0.0"
+fixed = "2.0.1.0"
+
+[[references]]
+type = "ARTICLE"
+url = "https://cs-syd.eu/posts/2021-09-11-json-vulnerability"
+[[references]]
+type = "ARTICLE"
+url = "https://frasertweedale.github.io/blog-fp/posts/2021-10-12-aeson-hash-flooding-protection.html"
+[[references]]
+type = "DISCUSSION"
+url = "https://github.com/haskell/aeson/issues/864"
+```
+
+# Hash flooding vulnerability in aeson
+
+*aeson* was vulnerable to hash flooding (a.k.a. hash DoS).  The
+issue is a consequence of the HashMap implementation from
+*unordered-containers*.  It results in a denial of service through
+CPU consumption.  This technique has been used in real-world attacks
+against a variety of languages, libraries and frameworks over the
+years.
diff --git a/test/fixtures/Advisories/advisories/hackage/base/HSEC-2023-0007.md b/test/fixtures/Advisories/advisories/hackage/base/HSEC-2023-0007.md
new file mode 100644
index 00000000..0987d8c8
--- /dev/null
+++ b/test/fixtures/Advisories/advisories/hackage/base/HSEC-2023-0007.md
@@ -0,0 +1,78 @@
+```toml
+[advisory]
+id = "HSEC-2023-0007"
+cwe = [1284, 789]
+keywords = ["toml", "parser", "dos"]
+
+[[affected]]
+package = "base"
+cvss = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
+[[affected.versions]]
+# it was introduced earlier, but this is the earliest version on Hackage
+introduced = "3.0.3.1"
+
+[[affected]]
+package = "toml-reader"
+cvss = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
+[[affected.versions]]
+introduced = "0.1.0.0"
+fixed = "0.2.0.0"
+
+[[references]]
+type = "REPORT"
+url = "https://gitlab.haskell.org/ghc/ghc/-/issues/23538"
+[[references]]
+type = "REPORT"
+url = "https://github.com/brandonchinn178/toml-reader/issues/8"
+[[references]]
+type = "FIX"
+url = "https://github.com/brandonchinn178/toml-reader/pull/9"
+
+```
+
+# `readFloat`: memory exhaustion with large exponent
+
+`Numeric.readFloat` takes time and memory linear in the size of the
+number _denoted_ by the input string.  In particular, processing a
+number expressed in scientific notation with a very large exponent
+could cause a denial of service.  The slowdown is observable on a
+modern machine running GHC 9.4.4:
+
+```
+ghci> import qualified Numeric
+ghci> Numeric.readFloat "1e1000000"    -- near instantaneous
+[(Infinity,"")]
+ghci> Numeric.readFloat "1e10000000"   -- perceptible pause
+[(Infinity,"")]
+ghci> Numeric.readFloat "1e100000000"  -- ~ 3 seconds
+[(Infinity,"")]
+ghci> Numeric.readFloat "1e1000000000" -- ~ 35 seconds
+[(Infinity,"")]
+```
+
+## In *base*
+
+`Numeric.readFloat` is defined for all `RealFrac a => a`:
+
+```haskell
+readFloat :: RealFrac a => ReadS a
+```
+
+The `RealFrac` type class does not express any bounds on the size of
+values representable in the types for which instances exist, so
+bounds checking is not possible (in this *generic* function).
+`readFloat` uses to `Text.Read.Lex.numberToRational` which, among
+other things, calculates `10 ^ exponent`, which seems to take linear
+time and memory.
+
+**Mitigation:** use `read`.  The `Read` instances for `Float` and
+`Double` perform bounds checks on the exponent, via
+`Text.Read.Lex.numberToRangedRational`.
+
+
+## In *toml-reader*
+
+The issue was detected in *toml-reader* version 0.1.0.0, and
+mitigated in version 0.2.0.0 by immediately returning `Infinity`
+when the exponent is large enough that there's no reason to process
+it.
diff --git a/test/fixtures/Advisories/advisories/hackage/biscuit-haskell/HSEC-2023-0002.md b/test/fixtures/Advisories/advisories/hackage/biscuit-haskell/HSEC-2023-0002.md
new file mode 100644
index 00000000..9fba4bd7
--- /dev/null
+++ b/test/fixtures/Advisories/advisories/hackage/biscuit-haskell/HSEC-2023-0002.md
@@ -0,0 +1,31 @@
+```toml
+[advisory]
+id = "HSEC-2023-0002"
+cwe = [347]
+keywords = ["crypto", "historical"]
+aliases = ["CVE-2022-31053"]
+related = ["GHSA-75rw-34q6-72cr"]
+
+[[affected]]
+package = "biscuit-haskell"
+cvss = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
+[[affected.versions]]
+introduced = "0.1.0.0"
+fixed = "0.2.0.0"
+
+[[references]]
+type = "REPORT"
+url = "https://eprint.iacr.org/2020/1484"
+[[references]]
+type = "ADVISORY"
+url = "https://github.com/biscuit-auth/biscuit/security/advisories/GHSA-75rw-34q6-72cr"
+
+```
+
+# Improper Verification of Cryptographic Signature
+
+The Biscuit specification version 1 contains a vulnerable algorithm that allows
+malicious actors to forge valid Γ-signatures. Such an attack would allow an
+attacker to create a token with any access level. The version 2 of the
+specification mandates a different algorithm than gamma signatures and as such
+is not affected by this vulnerability.
diff --git a/test/fixtures/Advisories/advisories/hackage/biscuit-haskell/HSEC-2024-0009.md b/test/fixtures/Advisories/advisories/hackage/biscuit-haskell/HSEC-2024-0009.md
new file mode 100644
index 00000000..38b2f33b
--- /dev/null
+++ b/test/fixtures/Advisories/advisories/hackage/biscuit-haskell/HSEC-2024-0009.md
@@ -0,0 +1,30 @@
+```toml
+[advisory]
+id = "HSEC-2024-0009"
+keywords = ["biscuit"]
+aliases = ["CVE-2024-41949", "GHSA-rgqv-mwc3-c78m", "GHSA-47cq-pc2v-3rmp"]
+
+[[references]]
+type = "ADVISORY"
+url = "https://github.com/biscuit-auth/biscuit-haskell/security/advisories/GHSA-47cq-pc2v-3rmp"
+[[references]]
+type = "FIX"
+url = "https://github.com/biscuit-auth/biscuit-haskell/pull/93"
+
+[[affected]]
+package = "biscuit-haskell"
+cvss = "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:N/I:L/A:N"
+
+[[affected.versions]]
+introduced = "0.3.0.0"
+fixed = "0.4.0.0"
+```
+
+# Public key confusion in third-party blocks
+
+Third-party blocks can be generated without transferring the whole token to the third-party authority. Instead, a `ThirdPartyBlock` request can be sent, providing only the necessary info to generate a third-party block and to sign it:
+
+- the public key of the previous block (used in the signature);
+- the public keys part of the token symbol table (for public key interning in datalog expressions).
+
+A third-party block request forged by a malicious user can trick the third-party authority into generating datalog trusting the wrong keypair.
diff --git a/test/fixtures/Advisories/advisories/hackage/cabal-install/HSEC-2023-0015.md b/test/fixtures/Advisories/advisories/hackage/cabal-install/HSEC-2023-0015.md
new file mode 100644
index 00000000..529845b9
--- /dev/null
+++ b/test/fixtures/Advisories/advisories/hackage/cabal-install/HSEC-2023-0015.md
@@ -0,0 +1,95 @@
+```toml
+[advisory]
+id = "HSEC-2023-0015"
+cwe = [672]
+keywords = ["hackage", "mitm", "supply-chain"]
+
+[[affected]]
+package = "cabal-install"
+cvss = "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N"
+[[affected.versions]]
+introduced = "1.24.0.0"
+fixed = "3.10.2.0"
+
+[[references]]
+type = "REPORT"
+url = "https://github.com/haskell/cabal/issues/8918#issuecomment-1521096581"
+[[references]]
+type = "FIX"
+url = "https://github.com/haskell/cabal/commit/dcfdc9cffd74cade4e8cf3df37c5993413ffd30f"
+```
+
+# cabal-install uses expired key policies
+
+A problem was recently discovered in `cabal-install`'s
+implementation of the Hackage Security protocol that would allow an
+attacker who was in possession of a revoked private key and who
+could perform a man-in-the-middle attack against Hackage to use the
+revoked key to deliver malicious packages. At this time, this is
+only a theoretical attack - no keys have been revoked. Release
+3.10.2.0 of `cabal-install` contains a fix for this bug, and we have
+contacted distributors of older versions (such as Linux
+distributions) with a patch that they can apply.
+
+## Background
+
+Hackage Security is an implementation of [The Update Framework][],
+which is a design for a package repository that allows untrusted
+mirrors without undermining software supply-chain security. In
+particular, Hackage Security cryptographically guarantees the
+following properties:
+
+ * Mirrors of Hackage cannot change the contents of packages. This
+   prevents the insertion of malicious code.
+
+ * Mirrors cannot omit newer packages for more than a few days
+   without clients noticing. This ensures both that mirrors cannot
+   maliciously deny security updates, and that mistakes in their
+   configuration will be noticed.
+
+Hackage has a [key policy file][] that delegates authority to a
+number of private keys for various purposes. Most of the keys are
+kept securely offline by trusted community members who annually
+re-sign the various files to indicate that they still have
+confidence in Hackage's policies. However, to prevent clients from
+being denied updates, Hackage has an automated process that
+periodically re-signs a timestamp file. This signature has a short
+expiry. Additionally, a snapshot file contains signed hashes of the
+Hackage index that is updated on each package upload. The timestamp
+and snapshot private keys are held in memory on the Hackage server.
+These are called the operational keys. If an operational key is ever
+compromised, then it will be revoked by having the Hackage root
+keyholders sign a new key policy file. To prevent replay attacks,
+clients that connect to Hackage after this update will reject older
+policy files, based on a monotonically increasing file version
+number.
+
+If a client has not yet received the updated policy file (for
+example, because they have a fresh install of `cabal-install` or
+because they have not run `cabal update` in some time), the built-in
+expiration date in the file limits the window of exposure in which
+the revoked operational keys would be expected. As long as the root
+keys have not been compromised, the compromised operational keys can
+only be used until the policy file expires. In addition to
+compromising a Hackage operational key, an attacker would
+additionally need to either compromise a Hackage mirror or perform a
+man-in-the-middle attack against the target in order to serve a
+malicious or obsolete package index.
+
+[key policy file]: https://hackage.haskell.org/root.json
+[The Update Framework]: https://theupdateframework.io/
+
+## The Issue
+
+A bug in `cabal-install` caused it to skip the verification of the
+key policy file's expiration timestamp. This means that users of
+older, unpatched versions of `cabal-install` could be vulnerable to
+a malicious mirror or man-in-the-middle attack against Hackage if
+they have not connected to Hackage in a long time, even after the
+policy file has expired.
+
+We do not believe that it has been possible to exploit this
+vulnerability, because no operational keys have been revoked.
+However, in case key revocation occurs, we strongly advise all users
+of `cabal-install` to ensure that they have version 3.10.2.0 or
+newer, which contain the fix.
diff --git a/test/fixtures/Advisories/advisories/hackage/hledger-web/HSEC-2023-0008.md b/test/fixtures/Advisories/advisories/hackage/hledger-web/HSEC-2023-0008.md
new file mode 100644
index 00000000..9746784c
--- /dev/null
+++ b/test/fixtures/Advisories/advisories/hackage/hledger-web/HSEC-2023-0008.md
@@ -0,0 +1,47 @@
+```toml
+[advisory]
+id = "HSEC-2023-0008"
+cwe = [87]
+keywords = ["web", "xss", "historical"]
+aliases = ["CVE-2021-46888"]
+
+[[affected]]
+package = "hledger-web"
+cvss = "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N"
+[[affected.versions]]
+introduced = "0.24"
+fixed = "1.23"
+
+[[references]]
+type = "REPORT"
+url = "https://github.com/simonmichael/hledger/issues/1525"
+[[references]]
+type = "INTRODUCED"
+url = "https://github.com/simonmichael/hledger/commit/ec51d28839b2910eea360b1b8c72904b51cf7821"
+[[references]]
+type = "EVIDENCE"
+url = "https://www.youtube.com/watch?v=QnRO-VkfIic"
+[[references]]
+type = "FIX"
+url = "https://github.com/simonmichael/hledger/pull/1663"
+
+```
+
+# Stored XSS in *hledger-web*
+
+An issue was discovered in *hledger-web* < 1.23. A Stored Cross-Site
+Scripting (XSS) vulnerability exists in `toBloodhoundJson` that
+allows an attacker to execute JavaScript by encoding user-controlled
+values in a payload with base64 and parsing them with the `atob`
+function.
+
+*hledger-web* forms sanitise obvious JavaScript, but not obfuscated
+JavaScript (see [OWASP Filter Evasion Cheat Sheet][cheatsheet]).
+This means *hledger-web* instances, especially anonymously-writable
+ones like `demo.hledger.org`, could be loaded with malicious
+JavaScript to be executed by subsequent visitors.
+
+[cheatsheet]: https://owasp.org/www-community/xss-filter-evasion-cheatsheet
+
+Reported by Gaspard Baye and Hamidullah Muslih.  Fix by Arsen
+Arsenović.
diff --git a/test/fixtures/Advisories/advisories/hackage/keter/HSEC-2024-0001.md b/test/fixtures/Advisories/advisories/hackage/keter/HSEC-2024-0001.md
new file mode 100644
index 00000000..324c8fdc
--- /dev/null
+++ b/test/fixtures/Advisories/advisories/hackage/keter/HSEC-2024-0001.md
@@ -0,0 +1,30 @@
+```toml
+[advisory]
+id = "HSEC-2024-0001"
+cwe = [79]
+keywords = ["http", "xss", "rxss", "historical"]
+
+[[references]]
+type = "FIX"
+url = "https://github.com/snoyberg/keter/pull/246"
+
+[[affected]]
+package = "keter"
+cvss = "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N"
+declarations."Keter.Proxy.toResponse" = ">= 0.3.4 && < 1.0.1"
+declarations."Keter.Proxy.unknownHostResponse" = ">= 1.0.1 && < 1.8.4"
+
+[[affected.versions]]
+introduced = "0.3.4"
+fixed = "1.8.4"
+```
+
+# Reflected XSS vulnerability in keter
+
+Keter is an app-server/reverse-proxy often used with webapps build on Yesod web-framework.
+
+In the logic handling VHost dispatch, Keter was echoing back `Host` header value, unescaped,
+as part of an HTML error page. This constitutes a reflected-XSS vulnerability. Although
+not readily exploitable directly from a browser (where `Host` header can't generally assume
+arbitrary values), it may become such in presence of further weaknesses in components
+upstream of Keter in the http proxying chain. Therefore, AC:High in CVSS evaluation.
diff --git a/test/fixtures/Advisories/advisories/hackage/pandoc/HSEC-2023-0014.md b/test/fixtures/Advisories/advisories/hackage/pandoc/HSEC-2023-0014.md
new file mode 100644
index 00000000..4fe6be14
--- /dev/null
+++ b/test/fixtures/Advisories/advisories/hackage/pandoc/HSEC-2023-0014.md
@@ -0,0 +1,27 @@
+```toml
+[advisory]
+id = "HSEC-2023-0014"
+keywords = ["file write"]
+aliases = ["CVE-2023-35936", "GHSA-xj5q-fv23-575g"]
+cwe = [20]
+
+[[references]]
+type = "REPORT"
+url = "https://github.com/jgm/pandoc/security/advisories/GHSA-xj5q-fv23-575g"
+
+[[affected]]
+package = "pandoc"
+cvss = "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:N/I:H/A:L"
+
+[[affected.versions]]
+introduced = "1.13"
+fixed = "3.1.4"
+
+```
+# Arbitrary file write is possible when using PDF output or --extract-media with untrusted input
+
+Pandoc is susceptible to an arbitrary file write vulnerability, which can be triggered by providing a specially crafted image element in the input when generating files using the --extract-media option or outputting to PDF format. This vulnerability allows an attacker to create or overwrite arbitrary files on the system (depending on the privileges of the process running pandoc).
+
+This vulnerability only affects systems that (a) pass untrusted user input to pandoc and (b) allow pandoc to be used to produce a PDF or with the --extract-media option.
+
+The vulnerability is patched in pandoc 3.1.4.
diff --git a/test/fixtures/Advisories/advisories/hackage/process/HSEC-2024-0003.md b/test/fixtures/Advisories/advisories/hackage/process/HSEC-2024-0003.md
new file mode 100644
index 00000000..e366fe14
--- /dev/null
+++ b/test/fixtures/Advisories/advisories/hackage/process/HSEC-2024-0003.md
@@ -0,0 +1,175 @@
+```toml
+[advisory]
+id = "HSEC-2024-0003"
+cwe = [150]
+keywords = ["windows"]
+aliases = ["CVE-2024-3566", "VU#123335"]
+related = ["CVE-2024-1874", "CVE-2024-24576", "CVE-2024-22423"]
+
+[[references]]
+type = "ARTICLE"
+url = "https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/"
+
+[[references]]
+type = "ADVISORY"
+url = "https://kb.cert.org/vuls/id/123335"
+
+[[references]]
+type = "FIX"
+url = "https://github.com/haskell/process/commit/3c419f9eeedac024c9dccce544e5a6fb587179a5"
+
+[[references]]
+type = "FIX"
+url = "https://github.com/haskell/process/commit/951b02dd95559b1a26f2456bfb97cf740ea40934"
+
+[[references]]
+type = "FIX"
+url = "https://github.com/haskell/process/commit/5fc91f5f36ed4479be2b95f04f264bb78ac8089d"
+
+[[affected]]
+package = "process"
+os = ["mingw32"]
+cvss = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H"
+
+[[affected.versions]]
+introduced = "1.0.0.0"
+fixed = "1.6.23.0"
+```
+
+# process: command injection via argument list on Windows
+
+The *process* library on Windows is vulnerable to a command injection
+vulnerability, via `cmd.exe`'s interpretation of arguments.  Programs that
+invoke batch files (`.bat`, `.cmd`) and pass arguments whose values are
+affected by program inputs may be affected.
+
+This issue was discovered in many programming languages' Windows process
+execution behaviour.  It was tracked by CERT/CC as **VU#123335** and a
+coordinated disclosure was made on 2024-04-09 17:00 UTC.
+
+A fix was released in *process-1.6.19.0*.
+
+
+## Background
+
+Unlike POSIX systems, Windows does not have a mechanism for passing multiple
+arguments.Command line parsing is up to individual programs.
+
+The *process* library defines the `RawCommand` constructor for specifying an
+executable and its arguments:
+
+```haskell
+data CmdSpec
+  = ShellCommand String
+  | RawCommand FilePath [String]
+```
+
+On Windows, the `RawCommand` executable name and arguments are serialised into
+a single *command line* string, with separate arguments quoted separately.
+*process* then invokes the Windows [`CreateProcess`][doc-CreateProcess]
+routine with this command line string is given as the `lpCommandLine`
+argument.
+
+[doc-CreateProcess]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
+
+
+## Issue
+
+When executing `.bat` or `.cmd` files, [`CreateProcess`][doc-CreateProcess]
+implicitly spawns `cmd.exe`.  The `System.Process` command line construction
+does not escape characters with special meaning to `cmd.exe`.  As a
+consequence, a command injection vulnerability arises when the following
+conditions are satisfied:
+
+- Program running on Windows
+- Program executes a `.bat` or `.cmd` file
+- The argument values include or are influenced by program input
+
+
+## Demonstration
+
+The following batch file, `test.bat`, merely prints the executable name the
+first two arguments (as interpreted by `cmd.exe`):
+
+```
+@ECHO OFF
+ECHO 0: %0
+ECHO 1: %1
+ECHO 2: %2
+PAUSE
+```
+
+The following Haskell program executes `test.bat` with basic string arguments.
+The output is as expected:
+
+```
+λ> readProcess "test.bat" ["a","b"] [] >>= putStrLn
+0: "test.bat"
+1: "a"
+2: "b"
+```
+
+However, we can use a close quote and the `&` character to induce `cmd.exe` to
+execute a program named in the argument:
+
+```
+λ> readProcess "test.bat" ["\"&calc.exe"] [] >>= putStrLn
+0: "test.bat"
+1: "\"
+2:
+```
+
+In addition to producing the above output, `calc.exe` is executed.
+
+
+## Mitigation
+
+The lack of a general mechanism on Windows for safely conveying command line
+arguments to programs increases the risk of this kind of security issue.  The
+fact that `cmd.exe` command line parsing is complex and poorly documented
+exacerbates this issue, and also heightens the risk that the fix is
+incomplete, or causes other issues.
+
+If possible, avoid executing batch files where arguments include or are
+influenced by untrusted program inputs.  If it must be done, reject arguments
+that include special characters including `&` and `"`.
+
+
+## Fix versions
+
+*process* was modified to perform additional escaping and quoting
+when executing `.bat` and `.cmd` files on Windows (ignoring
+character case).  The behaviour is unchanged in all other cases.
+
+The fix was released in ***process-1.6.19.0***.  The following GHC
+releases were the first in their series to include a fixed version
+of the *process* library:
+
+- **GHC 9.10.1-alpha3** (released 2024-04-15)
+- GHC 9.8.x (**no release with fix yet**)
+- **GHC 9.6.5** (released 2024-04-16)
+
+Such a change in semantics should normally result in a major version
+bump.  Because we expect very few (if any) users will be impacted by
+the behavioural change, the GHC team made a pragmatic decision to
+avoid the disruption that a major version bump would cause.
+
+A follow-up fix was released in ***process-1.6.23.0*** to handle batch
+scripts with paths ending in whitespace and periods and
+unescaped `%` expansions.
+
+
+## Acknowledgements
+
+Security researcher **RyotaK** discovered and responsibly disclosed
+this vulnerability, coordinating the response across the many
+affected langauges and ecosystems.
+
+Ben Gamari commited and released the fix, which was based on a
+proposal by Fraser Tweedale.  Fraser also improved the
+`System.Process` module documentation to better explain the Windows
+semantics.
+
+Security researcher **Kainan Zhang** (@4xpl0r3r) discovered and
+responsibly disclosing the issue in the first fix and the Rust
+Security Response WG coordinated the response.
diff --git a/test/fixtures/Advisories/advisories/hackage/tls-extra/HSEC-2023-0005.md b/test/fixtures/Advisories/advisories/hackage/tls-extra/HSEC-2023-0005.md
new file mode 100644
index 00000000..acb61ec1
--- /dev/null
+++ b/test/fixtures/Advisories/advisories/hackage/tls-extra/HSEC-2023-0005.md
@@ -0,0 +1,34 @@
+```toml
+[advisory]
+id = "HSEC-2023-0005"
+cwe = [295]
+keywords = ["x509", "pki", "mitm", "historical"]
+aliases = ["CVE-2013-0243"]
+
+[[affected]]
+package = "tls-extra"
+cvss = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N"
+
+[[affected.versions]]
+introduced = "0.1.0"
+fixed = "0.4.6.1"
+
+[[references]]
+type = "DISCUSSION"
+url = "https://www.openwall.com/lists/oss-security/2013/01/30/6"
+[[references]]
+type = "REPORT"
+url = "https://github.com/haskell-tls/hs-tls/issues/29"
+[[references]]
+type = "FIX"
+url = "https://github.com/haskell-tls/hs-tls/commit/15885c0649ceabd2f4d2913df8ac6dc63d6b3b37"
+```
+
+# tls-extra: certificate validation does not check Basic Constraints
+
+*tls-extra* does not check the Basic Constraints extension of a
+certificate in certificate chain processing.  Any certificate is
+treated as a CA certificate.  As a consequence, anyone who has a
+valid certificate can use it to sign another one (with an arbitrary
+subject DN/domain name embedded into it) and have it accepted by
+*tls*.  This allows MITM attacks on TLS connections.
diff --git a/test/fixtures/Advisories/advisories/hackage/toml-reader/HSEC-2023-0007.md b/test/fixtures/Advisories/advisories/hackage/toml-reader/HSEC-2023-0007.md
new file mode 120000
index 00000000..68d51e39
--- /dev/null
+++ b/test/fixtures/Advisories/advisories/hackage/toml-reader/HSEC-2023-0007.md
@@ -0,0 +1 @@
+../base/HSEC-2023-0007.md
\ No newline at end of file
diff --git a/test/fixtures/Advisories/advisories/reserved/.gitkeep b/test/fixtures/Advisories/advisories/reserved/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/test/fixtures/Advisories/advisories/reserved/HSEC-2024-0004.md b/test/fixtures/Advisories/advisories/reserved/HSEC-2024-0004.md
new file mode 100644
index 00000000..e69de29b
diff --git a/test/fixtures/Advisories/advisories/reserved/HSEC-2024-0005.md b/test/fixtures/Advisories/advisories/reserved/HSEC-2024-0005.md
new file mode 100644
index 00000000..e69de29b
diff --git a/test/fixtures/Cabal/hackage/aeson-0.4.0.0.cabal b/test/fixtures/Cabal/hackage/aeson-0.4.0.0.cabal
new file mode 100644
index 00000000..afc4d979
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/aeson-0.4.0.0.cabal
@@ -0,0 +1,171 @@
+name:            aeson
+version:         0.4.0.0
+x-revision: 3
+license:         BSD3
+license-file:    LICENSE
+category:        Text, Web, JSON
+copyright:       (c) 2011 Bryan O'Sullivan
+                 (c) 2011 MailRank, Inc.
+author:          Bryan O'Sullivan <bos@serpentine.com>
+maintainer:      Bryan O'Sullivan <bos@serpentine.com>
+stability:       experimental
+tested-with:     GHC == 6.12.3, GHC == 7.0.4, GHC == 7.2.2
+synopsis:        Fast JSON parsing and encoding
+cabal-version:   >= 1.8
+homepage:        https://github.com/bos/aeson
+bug-reports:     https://github.com/bos/aeson/issues
+build-type:      Simple
+description:
+    A JSON parsing and encoding library optimized for ease of use
+    and high performance.
+    .
+    To get started, see the documentation for the @Data.Aeson@ module
+    below.
+    .
+    For release notes, see
+    <https://github.com/bos/aeson/blob/master/release-notes.markdown>
+    .
+    /Note/: if you use GHCi or Template Haskell, please see the
+    @README@ file for important details about building this package,
+    and other packages that depend on it:
+    <https://github.com/bos/aeson#readme>
+    .
+    Parsing performance on a late 2010 MacBook Pro (2.66GHz Core i7),
+    for mostly-English tweets from Twitter's JSON search API:
+    .
+    * 0.8 KB, 32-bit GHC 6.12.3: 30538 msg\/sec (24.9 MB\/sec)
+    .
+    * 0.8 KB, 64-bit GHC 7.0.3: 31204 msg\/sec (25.4 MB\/sec)
+    .
+    * 6.4 KB, 32-bit GHC 6.12.3: 6731 msg\/sec (42.3 MB\/sec)
+    .
+    * 6.4 KB, 64-bit GHC 7.0.3: 6627 msg\/sec (41.7 MB\/sec)
+    .
+    * 11.8 KB, 32-bit GHC 6.12.3: 3751 msg\/sec (43.2 MB\/sec)
+    .
+    * 11.8 KB, 64-bit GHC 7.0.3: 3381 msg\/sec (38.9 MB\/sec)
+    .
+    * 31.2 KB, 32-bit GHC 6.12.3: 1306 msg\/sec (39.8 MB\/sec)
+    .
+    * 31.2 KB, 64-bit GHC 7.0.3: 1132 msg\/sec (34.5 MB\/sec)
+    .
+    * 61.5 KB, 32-bit GHC 6.12.3: 616 msg\/sec (37.0 MB\/sec)
+    .
+    * 61.5 KB, 64-bit GHC 7.0.3: 534 msg\/sec (32.1 MB\/sec)
+    .
+    Handling heavily-escaped text is a little more work.  Here is
+    parsing performance with Japanese tweets, where much of the text
+    is entirely Unicode-escaped.
+    .
+    * 14.6 KB, 32-bit GHC 6.12.3: 2315 msg\/sec (33.1 MB\/sec)
+    .
+    * 14.6 KB, 64-bit GHC 7.0.3: 1986 msg\/sec (28.4 MB\/sec)
+    .
+    * 44.1 KB, 32-bit GHC 6.12.3: 712 msg\/sec (30.7 MB\/sec)
+    .
+    * 44.1 KB, 64-bit GHC 7.0.3: 634 msg\/sec (27.3 MB\/sec)
+    .
+    * 82.9 KB, 32-bit GHC 6.12.3: 377 msg\/sec (30.5 MB\/sec)
+    .
+    * 82.9 KB, 64-bit GHC 7.0.3: 332 msg\/sec (26.9 MB\/sec)
+    .
+    Encoding performance on the same machine and data:
+    .
+    * English, 854 bytes: 43439 msg\/sec (35.4 MB/sec)
+    .
+    * English, 6.4 KB: 7127 msg\/sec (44.8 MB/sec)
+    .
+    * Engish, 61.5 KB: 765 msg\/sec (46.0 MB/sec)
+    .
+    * Japanese, 14.6 KB: 4727 msg\/sec (67.5 MB/sec)
+    .
+    * Japanese, 44.1 KB: 1505 msg\/sec (64.8 MB/sec)
+    .
+    (A note on naming: in Greek mythology, Aeson was the father of Jason.)
+
+extra-source-files:
+    README.markdown
+    benchmarks/*.hs
+    benchmarks/*.py
+    benchmarks/Makefile
+    benchmarks/json-data/*.json
+    examples/*.hs
+    release-notes.markdown
+    tests/Properties.hs
+
+flag developer
+  description: operate in developer mode
+  default: False
+
+library
+  exposed-modules:
+    Data.Aeson
+    Data.Aeson.Encode
+    Data.Aeson.Generic
+    Data.Aeson.Parser
+    Data.Aeson.Types
+    Data.Aeson.TH
+
+  other-modules:
+    Data.Aeson.Functions
+    Data.Aeson.Parser.Internal
+    Data.Aeson.Types.Class
+    Data.Aeson.Types.Internal
+
+  if impl(ghc >= 7.2.1)
+    cpp-options: -DGENERICS
+    build-depends: ghc-prim >= 0.2, dlist >= 0.2
+    other-modules:
+      Data.Aeson.Types.Generic
+
+  build-depends:
+    attoparsec >= 0.8.6.1,
+    base == 4.*,
+    blaze-builder >= 0.2.1.4,
+    blaze-textual >= 0.2.0.2,
+    bytestring,
+    containers,
+    deepseq,
+    hashable >= 1.1.2.0 && < 1.2,
+    mtl,
+    old-locale,
+    syb,
+    template-haskell >= 2.4 && < 2.10,
+    text >= 0.11.0.2,
+    time >= 1.1.3 && < 1.5,
+    unordered-containers >= 0.1.3.0,
+    vector >= 0.7.1
+
+  if flag(developer)
+    ghc-options: -Werror
+    ghc-prof-options: -auto-all
+
+  ghc-options:      -Wall
+
+test-suite tests
+  type:           exitcode-stdio-1.0
+  hs-source-dirs: tests
+  main-is:        Properties.hs
+
+  ghc-options:
+    -Wall -threaded -rtsopts
+
+  build-depends:
+    QuickCheck,
+    aeson,
+    attoparsec,
+    base,
+    containers,
+    bytestring,
+    template-haskell,
+    test-framework,
+    test-framework-quickcheck2,
+    text
+
+source-repository head
+  type:     git
+  location: git://github.com/bos/aeson.git
+
+source-repository head
+  type:     mercurial
+  location: https://bitbucket.org/bos/aeson
diff --git a/test/fixtures/Cabal/hackage/aeson-2.0.1.0.cabal b/test/fixtures/Cabal/hackage/aeson-2.0.1.0.cabal
new file mode 100644
index 00000000..04e19792
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/aeson-2.0.1.0.cabal
@@ -0,0 +1,241 @@
+name:               aeson
+version:            2.0.1.0
+x-revision: 1
+license:            BSD3
+license-file:       LICENSE
+category:           Text, Web, JSON
+copyright:
+  (c) 2011-2016 Bryan O'Sullivan
+  (c) 2011 MailRank, Inc.
+
+author:             Bryan O'Sullivan <bos@serpentine.com>
+maintainer:         Adam Bergmark <adam@bergmark.nl>
+stability:          experimental
+tested-with:
+  GHC ==8.0.2
+   || ==8.2.2
+   || ==8.4.4
+   || ==8.6.5
+   || ==8.8.4
+   || ==8.10.4
+   || ==9.0.1
+
+synopsis:           Fast JSON parsing and encoding
+cabal-version:      >=1.10
+homepage:           https://github.com/haskell/aeson
+bug-reports:        https://github.com/haskell/aeson/issues
+build-type:         Simple
+description:
+  A JSON parsing and encoding library optimized for ease of use
+  and high performance.
+  .
+  To get started, see the documentation for the @Data.Aeson@ module
+  below.
+  .
+  (A note on naming: in Greek mythology, Aeson was the father of Jason.)
+
+extra-source-files:
+  *.yaml
+  benchmarks/json-data/*.json
+  cbits/*.c
+  changelog.md
+  README.markdown
+  src-ffi/Data/Aeson/Parser/*.hs
+  src-pure/Data/Aeson/Parser/*.hs
+  tests/golden/*.expected
+  tests/JSONTestSuite/test_parsing/*.json
+  tests/JSONTestSuite/test_transform/*.json
+
+flag bytestring-builder
+  description:
+    Depend on the bytestring-builder package for backwards compatibility.
+
+  default:     False
+  manual:      False
+
+flag cffi
+  description:
+    Controls whether to include c-ffi bits or pure haskell. Default to False for security.
+
+  default:     False
+  manual:      True
+
+flag ordered-keymap
+  description:
+    Use ordered @Data.Map.Strict@ for KeyMap implementation.
+
+  default:     True
+  manual:      True
+
+library
+  default-language: Haskell2010
+  hs-source-dirs:   src attoparsec-iso8601/src
+  exposed-modules:
+    Data.Aeson
+    Data.Aeson.Encoding
+    Data.Aeson.Encoding.Internal
+    Data.Aeson.Internal
+    Data.Aeson.Internal.Time
+    Data.Aeson.Key
+    Data.Aeson.KeyMap
+    Data.Aeson.Parser
+    Data.Aeson.Parser.Internal
+    Data.Aeson.QQ.Simple
+    Data.Aeson.Text
+    Data.Aeson.TH
+    Data.Aeson.Types
+
+  other-modules:
+    Data.Aeson.Encoding.Builder
+    Data.Aeson.Internal.Functions
+    Data.Aeson.Parser.Time
+    Data.Aeson.Parser.Unescape
+    Data.Aeson.Types.Class
+    Data.Aeson.Types.FromJSON
+    Data.Aeson.Types.Generic
+    Data.Aeson.Types.Internal
+    Data.Aeson.Types.ToJSON
+    Data.Attoparsec.Time
+    Data.Attoparsec.Time.Internal
+
+  -- GHC bundled libs
+  build-depends:
+      base              >=4.9.0.0  && <5
+    , bytestring        >=0.10.8.1 && <0.11.2
+    , containers        >=0.5.7.1  && <0.7
+    , deepseq           >=1.4.2.0  && <1.5
+    , ghc-prim          >=0.5.0.0  && <0.8
+    , template-haskell  >=2.11.0.0 && <2.18
+    , text              >=1.2.3.0  && <1.3
+    , time              >=1.6.0.1  && <1.12
+
+  -- Compat
+  build-depends:
+      base-compat-batteries  >=0.10.0 && <0.13
+    , time-compat            >=1.9.6  && <1.10
+
+  if !impl(ghc >=8.6)
+    build-depends: contravariant >=1.4.1 && <1.6
+
+  -- Other dependencies
+  build-depends:
+      attoparsec            >=0.13.2.2 && <0.15
+    , data-fix              >=0.3      && <0.4
+    , dlist                 >=0.8.0.4  && <1.1
+    , hashable              >=1.2.7.0  && <1.4
+    , indexed-traversable   >=0.1.1    && <0.2
+    , primitive             >=0.7.0.1  && <0.8
+    , scientific            >=0.3.7.0  && <0.4
+    , semialign             >=1.2      && <1.3
+    , strict                >=0.4      && <0.5
+    , tagged                >=0.8.6    && <0.9
+    , th-abstraction        >=0.2.8.0  && <0.5
+    , these                 >=1.1      && <1.2
+    , unordered-containers  >=0.2.10.0 && <0.3
+    , uuid-types            >=1.0.3    && <1.1
+    , vector                >=0.12.0.1 && <0.13
+    , witherable            >=0.4.1    && <0.5
+
+  ghc-options:      -Wall
+
+  if (impl(ghcjs) || !flag(cffi))
+    hs-source-dirs: src-pure
+    other-modules:  Data.Aeson.Parser.UnescapePure
+
+  else
+    c-sources:      cbits/unescape_string.c
+    cpp-options:    -DCFFI
+    hs-source-dirs: src-ffi
+    other-modules:  Data.Aeson.Parser.UnescapeFFI
+
+  if flag(ordered-keymap)
+    cpp-options: -DUSE_ORDEREDMAP=1
+
+test-suite aeson-tests
+  default-language: Haskell2010
+  type:             exitcode-stdio-1.0
+  hs-source-dirs:   tests src-ffi src-pure
+  main-is:          Tests.hs
+  c-sources:        cbits/unescape_string.c
+  ghc-options:      -Wall -threaded -rtsopts
+  other-modules:
+    Data.Aeson.Parser.UnescapeFFI
+    Data.Aeson.Parser.UnescapePure
+    DataFamilies.Encoders
+    DataFamilies.Instances
+    DataFamilies.Properties
+    DataFamilies.Types
+    Encoders
+    ErrorMessages
+    Functions
+    Instances
+    Options
+    Properties
+    PropertyGeneric
+    PropertyKeys
+    PropertyRoundTrip
+    PropertyRTFunctors
+    PropertyTH
+    PropUtils
+    SerializationFormatSpec
+    Types
+    UnitTests
+    UnitTests.NullaryConstructors
+
+  build-depends:
+      aeson
+    , attoparsec
+    , base
+    , base-compat
+    , base-orphans          >=0.5.3    && <0.9
+    , base16-bytestring
+    , containers
+    , data-fix
+    , Diff                  >=0.4      && <0.5
+    , directory
+    , dlist
+    , filepath
+    , generic-deriving      >=1.10     && <1.15
+    , ghc-prim              >=0.2
+    , hashable              >=1.2.4.0
+    , integer-logarithms    >=1        && <1.1
+    , QuickCheck            >=2.14.2   && <2.15
+    , quickcheck-instances  >=0.3.25.2 && <0.4
+    , scientific
+    , strict
+    , tagged
+    , tasty
+    , tasty-golden
+    , tasty-hunit
+    , tasty-quickcheck
+    , template-haskell
+    , text
+    , these
+    , time
+    , time-compat
+    , unordered-containers
+    , uuid-types
+    , vector
+
+  if flag(bytestring-builder)
+    build-depends:
+        bytestring          >=0.9    && <0.10.4
+      , bytestring-builder  >=0.10.4 && <1
+
+  else
+    build-depends: bytestring >=0.10.4
+
+  if !impl(ghc >=8.0)
+    build-depends:
+        semigroups           >=0.18.2  && <0.20
+      , transformers         >=0.2.2.0
+      , transformers-compat  >=0.3
+
+  if !impl(ghc >=7.10)
+    build-depends:
+        nats  >=1     && <1.2
+      , void  >=0.7.2 && <0.8
+
+source-repository head
+  type:     git
+  location: git://github.com/haskell/aeson.git
diff --git a/test/fixtures/Cabal/hackage/aeson-2.2.3.0.cabal b/test/fixtures/Cabal/hackage/aeson-2.2.3.0.cabal
new file mode 100644
index 00000000..b2f4618d
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/aeson-2.2.3.0.cabal
@@ -0,0 +1,246 @@
+cabal-version:      2.2
+name:               aeson
+version:            2.2.3.0
+x-revision: 2
+license:            BSD-3-Clause
+license-file:       LICENSE
+category:           Text, Web, JSON
+copyright:
+  (c) 2011-2016 Bryan O'Sullivan
+  (c) 2011 MailRank, Inc.
+
+author:             Bryan O'Sullivan <bos@serpentine.com>
+maintainer:         Adam Bergmark <adam@bergmark.nl>
+stability:          experimental
+tested-with:
+  GHC ==8.6.5
+   || ==8.8.4
+   || ==8.10.7
+   || ==9.0.2
+   || ==9.2.8
+   || ==9.4.8
+   || ==9.6.5
+   || ==9.8.2
+   || ==9.10.1
+
+synopsis:           Fast JSON parsing and encoding
+homepage:           https://github.com/haskell/aeson
+bug-reports:        https://github.com/haskell/aeson/issues
+build-type:         Simple
+description:
+  A JSON parsing and encoding library optimized for ease of use
+  and high performance.
+  .
+  To get started, see the documentation for the @Data.Aeson@ module
+  below.
+  .
+  (A note on naming: in Greek mythology, Aeson was the father of Jason.)
+
+extra-source-files:
+  *.yaml
+  benchmarks/json-data/*.json
+  changelog.md
+  README.markdown
+  tests/golden/*.expected
+  tests/JSONTestSuite/results/*.tok
+  tests/JSONTestSuite/results/*.txt
+  tests/JSONTestSuite/test_parsing/*.json
+  tests/JSONTestSuite/test_transform/*.json
+
+flag ordered-keymap
+  description: Use ordered @Data.Map.Strict@ for KeyMap implementation.
+  default:     True
+  manual:      True
+
+library
+  default-language: Haskell2010
+  hs-source-dirs:   src
+  exposed-modules:
+    Data.Aeson
+    Data.Aeson.Decoding
+    Data.Aeson.Decoding.ByteString
+    Data.Aeson.Decoding.ByteString.Lazy
+    Data.Aeson.Decoding.Text
+    Data.Aeson.Decoding.Tokens
+    Data.Aeson.Encoding
+    Data.Aeson.Encoding.Internal
+    Data.Aeson.Key
+    Data.Aeson.KeyMap
+    Data.Aeson.QQ.Simple
+    Data.Aeson.RFC8785
+    Data.Aeson.Text
+    Data.Aeson.TH
+    Data.Aeson.Types
+
+  other-modules:
+    Data.Aeson.Decoding.Conversion
+    Data.Aeson.Decoding.Internal
+    Data.Aeson.Encoding.Builder
+    Data.Aeson.Internal.ByteString
+    Data.Aeson.Internal.Functions
+    Data.Aeson.Internal.Prelude
+    Data.Aeson.Internal.Scientific
+    Data.Aeson.Internal.Text
+    Data.Aeson.Internal.TH
+    Data.Aeson.Internal.Unescape
+    Data.Aeson.Internal.UnescapeFromText
+    Data.Aeson.Parser.Time
+    Data.Aeson.Types.Class
+    Data.Aeson.Types.FromJSON
+    Data.Aeson.Types.Generic
+    Data.Aeson.Types.Internal
+    Data.Aeson.Types.ToJSON
+
+  -- GHC bundled libs
+  build-depends:
+    , base              >=4.12.0.0 && <5
+    , bytestring        >=0.10.8.2 && <0.13
+    , containers        >=0.6.0.1  && <0.8
+    , deepseq           >=1.4.4.0  && <1.6
+    , exceptions        >=0.10.4   && <0.11
+    , ghc-prim          >=0.5.0.0  && <0.12
+    , template-haskell  >=2.14.0.0 && <2.23
+    , text              >=1.2.3.0  && <1.3  || >=2.0 && <2.2
+    , time              >=1.8.0.2  && <1.15
+
+  -- Compat
+  build-depends:
+    , generically  >=0.1   && <0.2
+    , time-compat  >=1.9.6 && <1.10
+
+  if !impl(ghc >=9.0)
+    build-depends: integer-gmp
+
+  -- Other dependencies
+  build-depends:
+    , character-ps          ^>=0.1
+    , data-fix              ^>=0.3.2
+    , dlist                 ^>=1.0
+    , hashable              ^>=1.4.6.0  || ^>=1.5.0.0
+    , indexed-traversable   ^>=0.1.2
+    , integer-conversion    ^>=0.1
+    , integer-logarithms    ^>=1.0.3.1
+    , network-uri           ^>=2.6.4.1
+    , OneTuple              ^>=0.4.1.1
+    , primitive             ^>=0.8.0.0  || ^>=0.9.0.0
+    , QuickCheck            ^>=2.14.3   || ^>=2.15
+    , scientific            ^>=0.3.7.0
+    , semialign             ^>=1.3
+    , strict                ^>=0.5
+    , tagged                ^>=0.8.7
+    , text-iso8601          ^>=0.1.1
+    , text-short            ^>=0.1.5
+    , th-abstraction        ^>=0.5.0.0  || ^>=0.6.0.0 || ^>=0.7.0.0
+    , these                 ^>=1.2
+    , unordered-containers  ^>=0.2.10.0
+    , uuid-types            ^>=1.0.5
+    , vector                ^>=0.13.0.0
+    , witherable            ^>=0.4.2    || ^>=0.5
+
+  ghc-options:      -Wall
+
+  -- String unescaping
+
+  if flag(ordered-keymap)
+    cpp-options: -DUSE_ORDEREDMAP=1
+
+test-suite aeson-tests
+  default-language: Haskell2010
+  type:             exitcode-stdio-1.0
+  hs-source-dirs:   tests
+  main-is:          Tests.hs
+  ghc-options:      -Wall -threaded -rtsopts
+  other-modules:
+    CastFloat
+    DataFamilies.Encoders
+    DataFamilies.Instances
+    DataFamilies.Properties
+    DataFamilies.Types
+    DoubleToScientific
+    Encoders
+    ErrorMessages
+    Functions
+    Instances
+    JSONTestSuite
+    Options
+    Properties
+    PropertyGeneric
+    PropertyKeys
+    PropertyQC
+    PropertyRoundTrip
+    PropertyRTFunctors
+    PropertyTH
+    PropUtils
+    Regression.Issue351
+    Regression.Issue571
+    Regression.Issue687
+    Regression.Issue967
+    RFC8785
+    SerializationFormatSpec
+    Types
+    UnitTests
+    UnitTests.FromJSONKey
+    UnitTests.Hashable
+    UnitTests.KeyMapInsertWith
+    UnitTests.MonadFix
+    UnitTests.NoThunks
+    UnitTests.NullaryConstructors
+    UnitTests.OmitNothingFieldsNote
+    UnitTests.OptionalFields
+    UnitTests.OptionalFields.Common
+    UnitTests.OptionalFields.Generics
+    UnitTests.OptionalFields.Manual
+    UnitTests.OptionalFields.TH
+    UnitTests.UTCTime
+
+  build-depends:
+    , aeson
+    , base
+    , base-compat
+    , base-orphans          >=0.5.3  && <0.10
+    , base16-bytestring
+    , bytestring
+    , containers
+    , data-fix
+    , deepseq
+    , Diff                  >=0.4    && <0.6
+    , directory
+    , dlist
+    , filepath
+    , generic-deriving      >=1.10   && <1.15
+    , generically
+    , ghc-prim              >=0.2
+    , hashable
+    , indexed-traversable
+    , integer-logarithms    >=1      && <1.1
+    , network-uri
+    , OneTuple
+    , primitive
+    , QuickCheck            >=2.14.2 && <2.16
+    , quickcheck-instances  >=0.3.29 && <0.4
+    , scientific
+    , strict
+    , tagged
+    , tasty
+    , tasty-golden
+    , tasty-hunit
+    , tasty-quickcheck
+    , template-haskell
+    , text
+    , text-short
+    , these
+    , time
+    , time-compat
+    , unordered-containers
+    , uuid-types
+    , vector
+
+  if !impl(ghc >=9.0)
+    build-depends: integer-gmp
+
+  if impl(ghc >=9.2 && <9.7)
+    build-depends: nothunks >=0.1.4 && <0.3
+
+source-repository head
+  type:     git
+  location: git://github.com/haskell/aeson.git
diff --git a/test/fixtures/Cabal/hackage/base-3.0.3.1.cabal b/test/fixtures/Cabal/hackage/base-3.0.3.1.cabal
new file mode 100644
index 00000000..d6ffc75f
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/base-3.0.3.1.cabal
@@ -0,0 +1,154 @@
+name:           base
+version:        3.0.3.1
+license:        BSD3
+license-file:   LICENSE
+maintainer:     libraries@haskell.org
+synopsis:       Basic libraries (backwards-compatibility version)
+description:
+    This is a backwards-compatible version of the base package.
+    It depends on a later version of base, and was probably supplied
+    with your compiler when it was installed.
+    
+cabal-version:  >=1.2
+build-type: Simple
+
+Library {
+    build-depends: base       >= 4.0 && < 4.2,
+                   syb        >= 0.1 && < 0.2
+    extensions: PackageImports
+
+    if impl(ghc < 6.9) {
+        buildable: False
+    }
+
+    if impl(ghc) {
+        exposed-modules:
+            Data.Generics,
+            Data.Generics.Aliases,
+            Data.Generics.Basics,
+            Data.Generics.Instances,
+            Data.Generics.Schemes,
+            Data.Generics.Text,
+            Data.Generics.Twins,
+            Foreign.Concurrent,
+            GHC.Arr,
+            GHC.Base,
+            GHC.Conc,
+            GHC.ConsoleHandler,
+            GHC.Desugar,
+            GHC.Dotnet,
+            GHC.Enum,
+            GHC.Environment,
+            GHC.Err,
+            GHC.Exception,
+            GHC.Exts,
+            GHC.Float,
+            GHC.ForeignPtr,
+            GHC.Handle,
+            GHC.IO,
+            GHC.IOBase,
+            GHC.Int,
+            GHC.List,
+            GHC.Num,
+            GHC.PArr,
+            GHC.Pack,
+            GHC.Ptr,
+            GHC.Read,
+            GHC.Real,
+            GHC.ST,
+            GHC.STRef,
+            GHC.Show,
+            GHC.Stable,
+            GHC.Storable,
+            GHC.TopHandler,
+            GHC.Unicode,
+            GHC.Weak,
+            GHC.Word,
+            System.Timeout
+    }
+    exposed-modules:
+        Control.Applicative,
+        Control.Arrow,
+        Control.Category,
+        Control.Concurrent,
+        Control.Concurrent.Chan,
+        Control.Concurrent.MVar,
+        Control.Concurrent.QSem,
+        Control.Concurrent.QSemN,
+        Control.Concurrent.SampleVar,
+        Control.Exception,
+        Control.Monad,
+        Control.Monad.Fix,
+        Control.Monad.Instances,
+        Control.Monad.ST,
+        Control.Monad.ST.Lazy,
+        Control.Monad.ST.Strict,
+        Data.Bits,
+        Data.Bool,
+        Data.Char,
+        Data.Complex,
+        Data.Dynamic,
+        Data.Either,
+        Data.Eq,
+        Data.Fixed,
+        Data.Foldable
+        Data.Function,
+        Data.HashTable,
+        Data.IORef,
+        Data.Int,
+        Data.Ix,
+        Data.List,
+        Data.Maybe,
+        Data.Monoid,
+        Data.Ord,
+        Data.Ratio,
+        Data.STRef,
+        Data.STRef.Lazy,
+        Data.STRef.Strict,
+        Data.String,
+        Data.Traversable
+        Data.Tuple,
+        Data.Typeable,
+        Data.Unique,
+        Data.Version,
+        Data.Word,
+        Debug.Trace,
+        Foreign,
+        Foreign.C,
+        Foreign.C.Error,
+        Foreign.C.String,
+        Foreign.C.Types,
+        Foreign.ForeignPtr,
+        Foreign.Marshal,
+        Foreign.Marshal.Alloc,
+        Foreign.Marshal.Array,
+        Foreign.Marshal.Error,
+        Foreign.Marshal.Pool,
+        Foreign.Marshal.Utils,
+        Foreign.Ptr,
+        Foreign.StablePtr,
+        Foreign.Storable,
+        Numeric,
+        Prelude,
+        System.Console.GetOpt,
+        System.CPUTime,
+        System.Environment,
+        System.Exit,
+        System.IO,
+        System.IO.Error,
+        System.IO.Unsafe,
+        System.Info,
+        System.Mem,
+        System.Mem.StableName,
+        System.Mem.Weak,
+        System.Posix.Internals,
+        System.Posix.Types,
+        Text.ParserCombinators.ReadP,
+        Text.ParserCombinators.ReadPrec,
+        Text.Printf,
+        Text.Read,
+        Text.Read.Lex,
+        Text.Show,
+        Text.Show.Functions
+        Unsafe.Coerce
+}
diff --git a/test/fixtures/Cabal/hackage/biscuit-haskell-0.1.0.0.cabal b/test/fixtures/Cabal/hackage/biscuit-haskell-0.1.0.0.cabal
new file mode 100644
index 00000000..780b3a2a
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/biscuit-haskell-0.1.0.0.cabal
@@ -0,0 +1,136 @@
+cabal-version: 2.0
+
+name:           biscuit-haskell
+version:        0.1.0.0
+category:       Security
+synopsis:       Library support for the Biscuit security token
+description:    Please see the README on GitHub at <https://github.com/divarvel/biscuit-haskell#readme>
+homepage:       https://github.com/divarvel/biscuit-haskell#readme
+bug-reports:    https://github.com/divarvel/biscuit-haskell/issues
+author:         Clément Delafargue
+maintainer:     clement@delafargue.name
+copyright:      2021 Clément Delafargue
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    README.md
+    ChangeLog.md
+
+source-repository head
+  type: git
+  location: https://github.com/divarvel/biscuit-haskell
+
+library
+  exposed-modules:
+      Auth.Biscuit
+      Auth.Biscuit.Utils
+      Auth.Biscuit.Datalog.AST
+      Auth.Biscuit.Datalog.Executor
+      Auth.Biscuit.Datalog.Parser
+      Auth.Biscuit.Example
+      Auth.Biscuit.Proto
+      Auth.Biscuit.ProtoBufAdapter
+      Auth.Biscuit.Sel
+      Auth.Biscuit.Timer
+      Auth.Biscuit.Token
+  other-modules:
+      Paths_biscuit_haskell
+  autogen-modules:
+      Paths_biscuit_haskell
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  build-depends:
+    base                 >= 4.7 && <5,
+    async                ^>= 2.2,
+    base16-bytestring    ^>= 0.1.0,
+    bytestring           ^>= 0.10,
+    text                 ^>= 1.2,
+    containers           ^>= 0.6,
+    template-haskell     ^>= 2.16,
+    attoparsec           ^>= 0.13,
+    primitive            ^>= 0.7,
+    base64               ^>= 0.4,
+    cereal               ^>= 0.5,
+    libsodium            ^>= 1.0,
+    mtl                  ^>= 2.2,
+    parser-combinators   ^>= 1.2,
+    protobuf             ^>= 0.2,
+    random               ^>= 1.1,
+    regex-tdfa           ^>= 1.3,
+    th-lift-instances    ^>= 0.1,
+    time                 ^>= 1.9,
+    validation-selective ^>= 0.1
+  default-language: Haskell2010
+
+executable biscuit-haskell-exe
+  main-is: Main.hs
+  other-modules:
+      Paths_biscuit_haskell
+  hs-source-dirs:
+      app
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
+  build-depends:
+      async
+    , attoparsec
+    , base >=4.7 && <5
+    , base16-bytestring ^>=0.1.0
+    , base64
+    , biscuit-haskell
+    , bytestring
+    , cereal
+    , containers
+    , libsodium
+    , mtl
+    , parser-combinators
+    , primitive
+    , protobuf
+    , random
+    , template-haskell
+    , text
+    , th-lift-instances
+    , time
+    , validation-selective
+  default-language: Haskell2010
+
+test-suite biscuit-haskell-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Spec.Crypto
+      Spec.Executor
+      Spec.Parser
+      Spec.Quasiquoter
+      Spec.RevocationIds
+      Spec.Roundtrip
+      Spec.Samples
+      Spec.Verification
+      Paths_biscuit_haskell
+  hs-source-dirs:
+      test
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      async
+    , attoparsec
+    , base >=4.7 && <5
+    , base16-bytestring ^>=0.1
+    , base64
+    , biscuit-haskell
+    , bytestring
+    , cereal
+    , containers
+    , libsodium
+    , mtl
+    , parser-combinators
+    , primitive
+    , protobuf
+    , random
+    , tasty
+    , tasty-hunit
+    , template-haskell
+    , text
+    , th-lift-instances
+    , time
+    , validation-selective
+  default-language: Haskell2010
diff --git a/test/fixtures/Cabal/hackage/biscuit-haskell-0.2.0.0.cabal b/test/fixtures/Cabal/hackage/biscuit-haskell-0.2.0.0.cabal
new file mode 100644
index 00000000..0327088c
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/biscuit-haskell-0.2.0.0.cabal
@@ -0,0 +1,146 @@
+cabal-version: 2.0
+
+name:           biscuit-haskell
+version:        0.2.0.0
+category:       Security
+synopsis:       Library support for the Biscuit security token
+description:    Please see the README on GitHub at <https://github.com/divarvel/biscuit-haskell#readme>
+homepage:       https://github.com/divarvel/biscuit-haskell#readme
+bug-reports:    https://github.com/divarvel/biscuit-haskell/issues
+author:         Clément Delafargue
+maintainer:     clement@delafargue.name
+copyright:      2021 Clément Delafargue
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    README.md
+    ChangeLog.md
+
+source-repository head
+  type: git
+  location: https://github.com/divarvel/biscuit-haskell
+
+library
+  exposed-modules:
+      Auth.Biscuit
+      Auth.Biscuit.Utils
+      Auth.Biscuit.Crypto
+      Auth.Biscuit.Datalog.AST
+      Auth.Biscuit.Datalog.Executor
+      Auth.Biscuit.Datalog.Parser
+      Auth.Biscuit.Datalog.ScopedExecutor
+      Auth.Biscuit.Example
+      Auth.Biscuit.Proto
+      Auth.Biscuit.ProtoBufAdapter
+      Auth.Biscuit.Timer
+      Auth.Biscuit.Token
+  other-modules:
+      Paths_biscuit_haskell
+  autogen-modules:
+      Paths_biscuit_haskell
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  build-depends:
+    base                 >= 4.7 && <5,
+    async                ^>= 2.2,
+    base16-bytestring    ^>= 1.0,
+    bytestring           ^>= 0.10,
+    text                 ^>= 1.2,
+    containers           ^>= 0.6,
+    cryptonite           >= 0.27 && < 0.30,
+    memory               ^>= 0.15,
+    template-haskell     ^>= 2.16,
+    attoparsec           ^>= 0.13,
+    base64               ^>= 0.4,
+    cereal               ^>= 0.5,
+    mtl                  ^>= 2.2,
+    parser-combinators   ^>= 1.2,
+    protobuf             ^>= 0.2,
+    random               >= 1.0 && < 1.3,
+    regex-tdfa           ^>= 1.3,
+    th-lift-instances    ^>= 0.1,
+    time                 ^>= 1.9,
+    validation-selective ^>= 0.1
+  default-language: Haskell2010
+
+executable biscuit-haskell-exe
+  main-is: Main.hs
+  other-modules:
+      Paths_biscuit_haskell
+  hs-source-dirs:
+      app
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
+  build-depends:
+      async
+    , attoparsec
+    , base >=4.7 && <5
+    , base16-bytestring ^>=1.0
+    , base64
+    , biscuit-haskell
+    , bytestring
+    , cereal
+    , containers
+    , mtl
+    , parser-combinators
+    , protobuf
+    , random
+    , template-haskell
+    , text
+    , th-lift-instances
+    , time
+    , validation-selective
+  default-language: Haskell2010
+
+test-suite biscuit-haskell-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Spec.NewCrypto
+      Spec.Executor
+      Spec.Parser
+      Spec.Quasiquoter
+      Spec.RevocationIds
+      Spec.Roundtrip
+      Spec.SampleReader
+      Spec.ScopedExecutor
+      Spec.Verification
+      Paths_biscuit_haskell
+  hs-source-dirs:
+      test
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      async
+    , aeson
+    , attoparsec
+    , base >=4.7 && <5
+    , base16-bytestring ^>=1.0
+    , base64
+    , biscuit-haskell
+    , bytestring
+    , cereal
+    , containers
+    , cryptonite
+    , mtl
+    , parser-combinators
+    , protobuf
+    , random
+    , tasty
+    , tasty-hunit
+    , template-haskell
+    , text
+    , th-lift-instances
+    , time
+    , validation-selective
+  default-language: Haskell2010
+
+benchmark biscuit-bench
+  type:                exitcode-stdio-1.0
+  main-is:             Bench.hs
+  hs-source-dirs:      benchmarks
+  default-language:    Haskell2010
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -with-rtsopts=-T
+  build-depends:       base
+                     , criterion
+                     , biscuit-haskell
diff --git a/test/fixtures/Cabal/hackage/biscuit-haskell-0.3.0.0.cabal b/test/fixtures/Cabal/hackage/biscuit-haskell-0.3.0.0.cabal
new file mode 100644
index 00000000..8881375f
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/biscuit-haskell-0.3.0.0.cabal
@@ -0,0 +1,123 @@
+cabal-version: 2.0
+
+name:           biscuit-haskell
+version:        0.3.0.0
+category:       Security
+synopsis:       Library support for the Biscuit security token
+description:    Please see the README on GitHub at <https://github.com/biscuit-auth/biscuit-haskell#readme>
+homepage:       https://github.com/biscuit-auth/biscuit-haskell#readme
+bug-reports:    https://github.com/biscuit-auth/biscuit-haskell/issues
+author:         Clément Delafargue
+maintainer:     clement@delafargue.name
+copyright:      2021 Clément Delafargue
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+tested-with:    GHC ==8.10.7 || == 9.0.2 || ==9.2.4
+extra-source-files:
+    README.md
+    ChangeLog.md
+    test/samples/current/samples.json
+    test/samples/current/*.bc
+
+source-repository head
+  type: git
+  location: https://github.com/biscuit-auth/biscuit-haskell
+
+library
+  exposed-modules:
+      Auth.Biscuit
+      Auth.Biscuit.Symbols
+      Auth.Biscuit.Utils
+      Auth.Biscuit.Crypto
+      Auth.Biscuit.Datalog.AST
+      Auth.Biscuit.Datalog.Executor
+      Auth.Biscuit.Datalog.Parser
+      Auth.Biscuit.Datalog.ScopedExecutor
+      Auth.Biscuit.Example
+      Auth.Biscuit.Proto
+      Auth.Biscuit.ProtoBufAdapter
+      Auth.Biscuit.Timer
+      Auth.Biscuit.Token
+  other-modules:
+      Paths_biscuit_haskell
+  autogen-modules:
+      Paths_biscuit_haskell
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  build-depends:
+    base                 >= 4.7 && <5,
+    async                ^>= 2.2,
+    base16               ^>= 0.3,
+    bytestring           >= 0.10 && <0.12,
+    text                 >= 1.2 && <3,
+    containers           ^>= 0.6,
+    cryptonite           >= 0.27 && < 0.31,
+    memory               >= 0.15 && < 0.19,
+    template-haskell     >= 2.16 && < 2.19,
+    base64               ^>= 0.4,
+    cereal               ^>= 0.5,
+    mtl                  ^>= 2.2,
+    parser-combinators   >= 1.2 && < 1.4,
+    protobuf             ^>= 0.2,
+    random               >= 1.0 && < 1.3,
+    regex-tdfa           ^>= 1.3,
+    th-lift-instances    ^>= 0.1,
+    time                 ^>= 1.9,
+    validation-selective ^>= 0.1,
+    megaparsec           ^>= 9.2
+  default-language: Haskell2010
+
+test-suite biscuit-haskell-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Spec.NewCrypto
+      Spec.Executor
+      Spec.Parser
+      Spec.Quasiquoter
+      Spec.Roundtrip
+      Spec.SampleReader
+      Spec.ScopedExecutor
+      Spec.Verification
+      Paths_biscuit_haskell
+  hs-source-dirs:
+      test
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      async
+    , aeson
+    , base >=4.7 && <5
+    , base16 ^>=0.3
+    , base64
+    , biscuit-haskell
+    , bytestring
+    , cereal
+    , containers
+    , cryptonite
+    , lens
+    , lens-aeson
+    , megaparsec
+    , mtl
+    , parser-combinators
+    , protobuf
+    , random
+    , tasty
+    , tasty-hunit
+    , template-haskell
+    , text
+    , th-lift-instances
+    , time
+    , validation-selective
+  default-language: Haskell2010
+
+benchmark biscuit-bench
+  type:                exitcode-stdio-1.0
+  main-is:             Bench.hs
+  hs-source-dirs:      benchmarks
+  default-language:    Haskell2010
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -with-rtsopts=-T
+  build-depends:       base
+                     , criterion
+                     , biscuit-haskell
diff --git a/test/fixtures/Cabal/hackage/biscuit-haskell-0.4.0.0.cabal b/test/fixtures/Cabal/hackage/biscuit-haskell-0.4.0.0.cabal
new file mode 100644
index 00000000..b8123d73
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/biscuit-haskell-0.4.0.0.cabal
@@ -0,0 +1,123 @@
+cabal-version: 2.0
+
+name:           biscuit-haskell
+version:        0.4.0.0
+category:       Security
+synopsis:       Library support for the Biscuit security token
+description:    Please see the README on GitHub at <https://github.com/biscuit-auth/biscuit-haskell#readme>
+homepage:       https://github.com/biscuit-auth/biscuit-haskell#readme
+bug-reports:    https://github.com/biscuit-auth/biscuit-haskell/issues
+author:         Clément Delafargue
+maintainer:     clement@delafargue.name
+copyright:      2021 Clément Delafargue
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+tested-with:    GHC ==9.0.2 || ==9.2.4 || ==9.6.5 || ==9.8.2
+extra-source-files:
+    README.md
+    ChangeLog.md
+    test/samples/current/samples.json
+    test/samples/current/*.bc
+
+source-repository head
+  type: git
+  location: https://github.com/biscuit-auth/biscuit-haskell
+
+library
+  exposed-modules:
+      Auth.Biscuit
+      Auth.Biscuit.Symbols
+      Auth.Biscuit.Utils
+      Auth.Biscuit.Crypto
+      Auth.Biscuit.Datalog.AST
+      Auth.Biscuit.Datalog.Executor
+      Auth.Biscuit.Datalog.Parser
+      Auth.Biscuit.Datalog.ScopedExecutor
+      Auth.Biscuit.Example
+      Auth.Biscuit.Proto
+      Auth.Biscuit.ProtoBufAdapter
+      Auth.Biscuit.Timer
+      Auth.Biscuit.Token
+  other-modules:
+      Paths_biscuit_haskell
+  autogen-modules:
+      Paths_biscuit_haskell
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  build-depends:
+    base                 >= 4.7 && <5,
+    async                ^>= 2.2,
+    base16               >= 0.3 && <2.0,
+    bytestring           >= 0.10 && <0.12,
+    text                 >= 1.2 && <3,
+    containers           ^>= 0.6,
+    cryptonite           >= 0.27 && < 0.31,
+    memory               >= 0.15 && < 0.19,
+    template-haskell     >= 2.16 && < 2.22,
+    base64               ^>= 0.4,
+    cereal               ^>= 0.5,
+    mtl                  >= 2.2 && < 2.4,
+    parser-combinators   >= 1.2 && < 1.4,
+    protobuf             ^>= 0.2,
+    random               >= 1.0 && < 1.3,
+    regex-tdfa           ^>= 1.3,
+    th-lift-instances    ^>= 0.1,
+    time                 ^>= 1.9,
+    validation-selective >= 0.1 && < 0.3,
+    megaparsec           >= 9.2 && < 9.7
+  default-language: Haskell2010
+
+test-suite biscuit-haskell-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Spec.NewCrypto
+      Spec.Executor
+      Spec.Parser
+      Spec.Quasiquoter
+      Spec.Roundtrip
+      Spec.SampleReader
+      Spec.ScopedExecutor
+      Spec.Verification
+      Paths_biscuit_haskell
+  hs-source-dirs:
+      test
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      async
+    , aeson
+    , base >=4.7 && <5
+    , base16 >=0.3 && <2.0
+    , base64
+    , biscuit-haskell
+    , bytestring
+    , cereal
+    , containers
+    , cryptonite
+    , lens
+    , lens-aeson
+    , megaparsec
+    , mtl
+    , parser-combinators
+    , protobuf
+    , random
+    , tasty
+    , tasty-hunit
+    , template-haskell
+    , text
+    , th-lift-instances
+    , time
+    , validation-selective
+  default-language: Haskell2010
+
+benchmark biscuit-bench
+  type:                exitcode-stdio-1.0
+  main-is:             Bench.hs
+  hs-source-dirs:      benchmarks
+  default-language:    Haskell2010
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -with-rtsopts=-T
+  build-depends:       base
+                     , criterion
+                     , biscuit-haskell
diff --git a/test/fixtures/Cabal/hackage/cabal-install-1.24.0.0.cabal b/test/fixtures/Cabal/hackage/cabal-install-1.24.0.0.cabal
new file mode 100644
index 00000000..3ddb7da6
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/cabal-install-1.24.0.0.cabal
@@ -0,0 +1,409 @@
+Name:               cabal-install
+Version:            1.24.0.0
+x-revision: 2
+Synopsis:           The command-line interface for Cabal and Hackage.
+Description:
+    The \'cabal\' command-line program simplifies the process of managing
+    Haskell software by automating the fetching, configuration, compilation
+    and installation of Haskell libraries and programs.
+homepage:           http://www.haskell.org/cabal/
+bug-reports:        https://github.com/haskell/cabal/issues
+License:            BSD3
+License-File:       LICENSE
+Author:             Lemmih <lemmih@gmail.com>
+                    Paolo Martini <paolo@nemail.it>
+                    Bjorn Bringert <bjorn@bringert.net>
+                    Isaac Potoczny-Jones <ijones@syntaxpolice.org>
+                    Duncan Coutts <duncan@community.haskell.org>
+Maintainer:         cabal-devel@haskell.org
+Copyright:          2005 Lemmih <lemmih@gmail.com>
+                    2006 Paolo Martini <paolo@nemail.it>
+                    2007 Bjorn Bringert <bjorn@bringert.net>
+                    2007 Isaac Potoczny-Jones <ijones@syntaxpolice.org>
+                    2007-2012 Duncan Coutts <duncan@community.haskell.org>
+Category:           Distribution
+Build-type:         Custom
+Cabal-Version:      >= 1.10
+Extra-Source-Files:
+  README.md bash-completion/cabal bootstrap.sh changelog
+  tests/README.md
+
+  -- Generated with '../Cabal/misc/gen-extra-source-files.sh'
+  -- Do NOT edit this section manually; instead, run the script.
+  -- BEGIN gen-extra-source-files
+  tests/IntegrationTests/custom/common.sh
+  tests/IntegrationTests/custom/should_run/plain.err
+  tests/IntegrationTests/custom/should_run/plain.sh
+  tests/IntegrationTests/custom/should_run/plain/A.hs
+  tests/IntegrationTests/custom/should_run/plain/Setup.hs
+  tests/IntegrationTests/custom/should_run/plain/plain.cabal
+  tests/IntegrationTests/exec/common.sh
+  tests/IntegrationTests/exec/should_fail/exit_with_failure_without_args.err
+  tests/IntegrationTests/exec/should_fail/exit_with_failure_without_args.sh
+  tests/IntegrationTests/exec/should_run/Foo.hs
+  tests/IntegrationTests/exec/should_run/My.hs
+  tests/IntegrationTests/exec/should_run/adds_sandbox_bin_directory_to_path.out
+  tests/IntegrationTests/exec/should_run/adds_sandbox_bin_directory_to_path.sh
+  tests/IntegrationTests/exec/should_run/auto_configures_on_exec.out
+  tests/IntegrationTests/exec/should_run/auto_configures_on_exec.sh
+  tests/IntegrationTests/exec/should_run/can_run_executables_installed_in_sandbox.out
+  tests/IntegrationTests/exec/should_run/can_run_executables_installed_in_sandbox.sh
+  tests/IntegrationTests/exec/should_run/configures_cabal_to_use_sandbox.sh
+  tests/IntegrationTests/exec/should_run/configures_ghc_to_use_sandbox.sh
+  tests/IntegrationTests/exec/should_run/my.cabal
+  tests/IntegrationTests/exec/should_run/runs_given_command.out
+  tests/IntegrationTests/exec/should_run/runs_given_command.sh
+  tests/IntegrationTests/freeze/common.sh
+  tests/IntegrationTests/freeze/should_run/disable_benchmarks_freezes_bench_deps.sh
+  tests/IntegrationTests/freeze/should_run/disable_tests_freezes_test_deps.sh
+  tests/IntegrationTests/freeze/should_run/does_not_freeze_nondeps.sh
+  tests/IntegrationTests/freeze/should_run/does_not_freeze_self.sh
+  tests/IntegrationTests/freeze/should_run/dry_run_does_not_create_config.sh
+  tests/IntegrationTests/freeze/should_run/enable_benchmarks_freezes_bench_deps.sh
+  tests/IntegrationTests/freeze/should_run/enable_tests_freezes_test_deps.sh
+  tests/IntegrationTests/freeze/should_run/freezes_direct_dependencies.sh
+  tests/IntegrationTests/freeze/should_run/freezes_transitive_dependencies.sh
+  tests/IntegrationTests/freeze/should_run/my.cabal
+  tests/IntegrationTests/freeze/should_run/runs_without_error.sh
+  tests/IntegrationTests/manpage/common.sh
+  tests/IntegrationTests/manpage/should_run/outputs_manpage.sh
+  tests/IntegrationTests/multiple-source/common.sh
+  tests/IntegrationTests/multiple-source/should_run/finds_second_source_of_multiple_source.sh
+  tests/IntegrationTests/multiple-source/should_run/p/Setup.hs
+  tests/IntegrationTests/multiple-source/should_run/p/p.cabal
+  tests/IntegrationTests/multiple-source/should_run/q/Setup.hs
+  tests/IntegrationTests/multiple-source/should_run/q/q.cabal
+  tests/IntegrationTests/new-build/monitor_cabal_files.sh
+  tests/IntegrationTests/new-build/monitor_cabal_files/p/P.hs
+  tests/IntegrationTests/new-build/monitor_cabal_files/p/Setup.hs
+  tests/IntegrationTests/new-build/monitor_cabal_files/p/p.cabal
+  tests/IntegrationTests/new-build/monitor_cabal_files/q/Main.hs
+  tests/IntegrationTests/new-build/monitor_cabal_files/q/Setup.hs
+  tests/IntegrationTests/new-build/monitor_cabal_files/q/q-broken.cabal.in
+  tests/IntegrationTests/new-build/monitor_cabal_files/q/q-fixed.cabal.in
+  tests/IntegrationTests/regression/common.sh
+  tests/IntegrationTests/regression/t3199.sh
+  tests/IntegrationTests/regression/t3199/Main.hs
+  tests/IntegrationTests/regression/t3199/Setup.hs
+  tests/IntegrationTests/regression/t3199/test-3199.cabal
+  tests/IntegrationTests/sandbox-sources/common.sh
+  tests/IntegrationTests/sandbox-sources/should_fail/fail_removing_source_thats_not_registered.err
+  tests/IntegrationTests/sandbox-sources/should_fail/fail_removing_source_thats_not_registered.sh
+  tests/IntegrationTests/sandbox-sources/should_fail/p/Setup.hs
+  tests/IntegrationTests/sandbox-sources/should_fail/p/p.cabal
+  tests/IntegrationTests/sandbox-sources/should_fail/q/Setup.hs
+  tests/IntegrationTests/sandbox-sources/should_fail/q/q.cabal
+  tests/IntegrationTests/sandbox-sources/should_run/p/Setup.hs
+  tests/IntegrationTests/sandbox-sources/should_run/p/p.cabal
+  tests/IntegrationTests/sandbox-sources/should_run/q/Setup.hs
+  tests/IntegrationTests/sandbox-sources/should_run/q/q.cabal
+  tests/IntegrationTests/sandbox-sources/should_run/remove_nonexistent_source.sh
+  tests/IntegrationTests/sandbox-sources/should_run/report_success_removing_source.out
+  tests/IntegrationTests/sandbox-sources/should_run/report_success_removing_source.sh
+  tests/IntegrationTests/user-config/common.sh
+  tests/IntegrationTests/user-config/should_fail/doesnt_overwrite_without_f.err
+  tests/IntegrationTests/user-config/should_fail/doesnt_overwrite_without_f.sh
+  tests/IntegrationTests/user-config/should_run/overwrites_with_f.out
+  tests/IntegrationTests/user-config/should_run/overwrites_with_f.sh
+  tests/IntegrationTests/user-config/should_run/runs_without_error.out
+  tests/IntegrationTests/user-config/should_run/runs_without_error.sh
+  tests/IntegrationTests/user-config/should_run/uses_CABAL_CONFIG.out
+  tests/IntegrationTests/user-config/should_run/uses_CABAL_CONFIG.sh
+  -- END gen-extra-source-files
+
+source-repository head
+  type:     git
+  location: https://github.com/haskell/cabal/
+  subdir:   cabal-install
+
+Flag old-bytestring
+  description:  Use bytestring < 0.10.2 and bytestring-builder
+  default: False
+
+Flag old-directory
+  description:  Use directory < 1.2 and old-time
+  default:      False
+
+Flag network-uri
+  description:  Get Network.URI from the network-uri package
+  default:      True
+
+executable cabal
+    -- https://github.com/haskell/cabal/issues/4123
+    build-depends: Cabal < 1.24.1.0 || > 1.24.1.0
+
+
+    main-is:        Main.hs
+    ghc-options:    -Wall -fwarn-tabs
+    if impl(ghc >= 8.0)
+        ghc-options: -Wcompat
+                     -Wnoncanonical-monad-instances
+                     -Wnoncanonical-monadfail-instances
+
+    other-modules:
+        Distribution.Client.BuildTarget
+        Distribution.Client.BuildReports.Anonymous
+        Distribution.Client.BuildReports.Storage
+        Distribution.Client.BuildReports.Types
+        Distribution.Client.BuildReports.Upload
+        Distribution.Client.Check
+        Distribution.Client.CmdBuild
+        Distribution.Client.CmdConfigure
+        Distribution.Client.CmdRepl
+        Distribution.Client.ComponentDeps
+        Distribution.Client.Config
+        Distribution.Client.Configure
+        Distribution.Client.Dependency
+        Distribution.Client.Dependency.TopDown
+        Distribution.Client.Dependency.TopDown.Constraints
+        Distribution.Client.Dependency.TopDown.Types
+        Distribution.Client.Dependency.Types
+        Distribution.Client.Dependency.Modular
+        Distribution.Client.Dependency.Modular.Assignment
+        Distribution.Client.Dependency.Modular.Builder
+        Distribution.Client.Dependency.Modular.Configured
+        Distribution.Client.Dependency.Modular.ConfiguredConversion
+        Distribution.Client.Dependency.Modular.ConflictSet
+        Distribution.Client.Dependency.Modular.Cycles
+        Distribution.Client.Dependency.Modular.Dependency
+        Distribution.Client.Dependency.Modular.Explore
+        Distribution.Client.Dependency.Modular.Flag
+        Distribution.Client.Dependency.Modular.Index
+        Distribution.Client.Dependency.Modular.IndexConversion
+        Distribution.Client.Dependency.Modular.Linking
+        Distribution.Client.Dependency.Modular.Log
+        Distribution.Client.Dependency.Modular.Message
+        Distribution.Client.Dependency.Modular.Package
+        Distribution.Client.Dependency.Modular.Preference
+        Distribution.Client.Dependency.Modular.PSQ
+        Distribution.Client.Dependency.Modular.Solver
+        Distribution.Client.Dependency.Modular.Tree
+        Distribution.Client.Dependency.Modular.Validate
+        Distribution.Client.Dependency.Modular.Var
+        Distribution.Client.Dependency.Modular.Version
+        Distribution.Client.DistDirLayout
+        Distribution.Client.Exec
+        Distribution.Client.Fetch
+        Distribution.Client.FetchUtils
+        Distribution.Client.FileMonitor
+        Distribution.Client.Freeze
+        Distribution.Client.GenBounds
+        Distribution.Client.Get
+        Distribution.Client.Glob
+        Distribution.Client.GlobalFlags
+        Distribution.Client.GZipUtils
+        Distribution.Client.Haddock
+        Distribution.Client.HttpUtils
+        Distribution.Client.IndexUtils
+        Distribution.Client.Init
+        Distribution.Client.Init.Heuristics
+        Distribution.Client.Init.Licenses
+        Distribution.Client.Init.Types
+        Distribution.Client.Install
+        Distribution.Client.InstallPlan
+        Distribution.Client.InstallSymlink
+        Distribution.Client.JobControl
+        Distribution.Client.List
+        Distribution.Client.Manpage
+        Distribution.Client.PackageHash
+        Distribution.Client.PackageIndex
+        Distribution.Client.PackageUtils
+        Distribution.Client.ParseUtils
+        Distribution.Client.PkgConfigDb
+        Distribution.Client.PlanIndex
+        Distribution.Client.ProjectBuilding
+        Distribution.Client.ProjectConfig
+        Distribution.Client.ProjectConfig.Types
+        Distribution.Client.ProjectConfig.Legacy
+        Distribution.Client.ProjectOrchestration
+        Distribution.Client.ProjectPlanning
+        Distribution.Client.ProjectPlanning.Types
+        Distribution.Client.ProjectPlanOutput
+        Distribution.Client.Run
+        Distribution.Client.RebuildMonad
+        Distribution.Client.Sandbox
+        Distribution.Client.Sandbox.Index
+        Distribution.Client.Sandbox.PackageEnvironment
+        Distribution.Client.Sandbox.Timestamp
+        Distribution.Client.Sandbox.Types
+        Distribution.Client.Security.HTTP
+        Distribution.Client.Setup
+        Distribution.Client.SetupWrapper
+        Distribution.Client.SrcDist
+        Distribution.Client.Tar
+        Distribution.Client.Targets
+        Distribution.Client.Types
+        Distribution.Client.Update
+        Distribution.Client.Upload
+        Distribution.Client.Utils
+        Distribution.Client.Utils.LabeledGraph
+        Distribution.Client.Utils.Json
+        Distribution.Client.World
+        Distribution.Client.Win32SelfUpgrade
+        Distribution.Client.Compat.ExecutablePath
+        Distribution.Client.Compat.FilePerms
+        Distribution.Client.Compat.Process
+        Distribution.Client.Compat.Semaphore
+        Distribution.Client.Compat.Time
+        Paths_cabal_install
+
+    -- NOTE: when updating build-depends, don't forget to update version regexps
+    -- in bootstrap.sh.
+    build-depends:
+        async      >= 2.0      && < 3,
+        array      >= 0.4      && < 0.6,
+        base       >= 4.5      && < 5,
+        base16-bytestring >= 0.1.1 && < 0.2,
+        binary     >= 0.5      && < 0.9,
+        bytestring >= 0.9      && < 1,
+        Cabal      >= 1.24     && < 1.25,
+        containers >= 0.4      && < 0.6,
+        cryptohash-sha256 >= 0.11 && < 0.12,
+        filepath   >= 1.3      && < 1.5,
+        hashable   >= 1.0      && < 2,
+        HTTP       >= 4000.1.5 && < 4000.4,
+        mtl        >= 2.0      && < 3,
+        pretty     >= 1.1      && < 1.2,
+        random     >= 1        && < 1.2,
+        stm        >= 2.0      && < 3,
+        tar        >= 0.5.0.3  && < 0.6,
+        time       >= 1.4      && < 1.7,
+        zlib       >= 0.5.3    && < 0.7,
+        hackage-security >= 0.5.1 && < 0.6
+
+    if flag(old-bytestring)
+      build-depends: bytestring <  0.10.2, bytestring-builder >= 0.10 && < 1
+    else
+      build-depends: bytestring >= 0.10.2
+
+    if flag(old-directory)
+      build-depends: directory >= 1.1 && < 1.2, old-time >= 1 && < 1.2,
+                     process   >= 1.0.1.1  && < 1.1.0.2
+    else
+      build-depends: directory >= 1.2 && < 1.3,
+                     process   >= 1.1.0.2  && < 1.5
+
+    -- NOTE: you MUST include the network dependency even when network-uri
+    -- is pulled in, otherwise the constraint solver doesn't have enough
+    -- information
+    if flag(network-uri)
+      build-depends: network-uri >= 2.6 && < 2.7, network >= 2.6 && < 2.7
+    else
+      build-depends: network     >= 2.4 && < 2.6
+
+    -- Needed for GHC.Generics before GHC 7.6
+    if impl(ghc < 7.6)
+      build-depends: ghc-prim >= 0.2 && < 0.3
+
+    if os(windows)
+      build-depends: Win32 >= 2 && < 3
+    else
+      build-depends: unix >= 2.5 && < 2.8
+
+    if arch(arm) && impl(ghc < 7.6)
+       -- older ghc on arm does not support -threaded
+       cc-options:  -DCABAL_NO_THREADED
+    else
+       ghc-options: -threaded
+
+    c-sources: cbits/getnumcores.c
+    default-language: Haskell2010
+
+-- Small, fast running tests.
+Test-Suite unit-tests
+  type: exitcode-stdio-1.0
+  main-is: UnitTests.hs
+  hs-source-dirs: tests, .
+  ghc-options: -Wall -fwarn-tabs
+  other-modules:
+    UnitTests.Distribution.Client.ArbitraryInstances
+    UnitTests.Distribution.Client.Targets
+    UnitTests.Distribution.Client.Compat.Time
+    UnitTests.Distribution.Client.Dependency.Modular.PSQ
+    UnitTests.Distribution.Client.Dependency.Modular.Solver
+    UnitTests.Distribution.Client.Dependency.Modular.DSL
+    UnitTests.Distribution.Client.FileMonitor
+    UnitTests.Distribution.Client.Glob
+    UnitTests.Distribution.Client.GZipUtils
+    UnitTests.Distribution.Client.Sandbox
+    UnitTests.Distribution.Client.Sandbox.Timestamp
+    UnitTests.Distribution.Client.Tar
+    UnitTests.Distribution.Client.UserConfig
+    UnitTests.Distribution.Client.ProjectConfig
+    UnitTests.Options
+  build-depends:
+        base,
+        array,
+        bytestring,
+        Cabal,
+        containers,
+        mtl,
+        pretty,
+        process,
+        directory,
+        filepath,
+        hashable,
+        stm,
+        tar,
+        time,
+        HTTP,
+        zlib,
+        binary,
+        random,
+        hackage-security,
+        tasty,
+        tasty-hunit,
+        tasty-quickcheck,
+        tagged,
+        QuickCheck >= 2.8.2
+
+  if flag(old-directory)
+    build-depends: old-time
+
+  if flag(network-uri)
+    build-depends: network-uri >= 2.6, network >= 2.6
+  else
+    build-depends: network-uri < 2.6, network < 2.6
+
+  if impl(ghc < 7.6)
+    build-depends: ghc-prim >= 0.2 && < 0.3
+
+  if os(windows)
+    build-depends: Win32
+  else
+    build-depends: unix
+
+  if arch(arm)
+    cc-options:  -DCABAL_NO_THREADED
+  else
+    ghc-options: -threaded
+  default-language: Haskell2010
+
+test-suite integration-tests
+  type: exitcode-stdio-1.0
+  hs-source-dirs: tests
+  main-is: IntegrationTests.hs
+  build-depends:
+    Cabal,
+    async,
+    base,
+    bytestring,
+    directory,
+    filepath,
+    process,
+    regex-posix,
+    tasty,
+    tasty-hunit
+
+  if os(windows)
+    build-depends: Win32 >= 2 && < 3
+  else
+    build-depends: unix >= 2.5 && < 2.8
+
+  if arch(arm)
+    cc-options:  -DCABAL_NO_THREADED
+  else
+    ghc-options: -threaded
+
+  ghc-options: -Wall
+  default-language: Haskell2010
diff --git a/test/fixtures/Cabal/hackage/cabal-install-3.10.2.0.cabal b/test/fixtures/Cabal/hackage/cabal-install-3.10.2.0.cabal
new file mode 100644
index 00000000..ec3bf325
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/cabal-install-3.10.2.0.cabal
@@ -0,0 +1,425 @@
+Cabal-Version:      2.2
+
+Name:               cabal-install
+Version:            3.10.2.0
+x-revision: 1
+Synopsis:           The command-line interface for Cabal and Hackage.
+Description:
+    The \'cabal\' command-line program simplifies the process of managing
+    Haskell software by automating the fetching, configuration, compilation
+    and installation of Haskell libraries and programs.
+homepage:           http://www.haskell.org/cabal/
+bug-reports:        https://github.com/haskell/cabal/issues
+License:            BSD-3-Clause
+License-File:       LICENSE
+Author:             Cabal Development Team (see AUTHORS file)
+Maintainer:         Cabal Development Team <cabal-devel@haskell.org>
+Copyright:          2003-2023, Cabal Development Team
+Category:           Distribution
+Build-type:         Simple
+Extra-Source-Files:
+  README.md
+  bash-completion/cabal
+  changelog
+
+source-repository head
+  type:     git
+  location: https://github.com/haskell/cabal/
+  subdir:   cabal-install
+
+Flag native-dns
+  description:
+    Enable use of the [resolv](https://hackage.haskell.org/package/resolv)
+    & [windns](https://hackage.haskell.org/package/windns) packages for performing DNS lookups
+  default:      True
+  manual:       True
+
+Flag lukko
+  description:  Use @lukko@ for file-locking
+  default:      True
+  manual:       True
+
+common warnings
+    ghc-options: -Wall -Wcompat -Wnoncanonical-monad-instances -Wincomplete-uni-patterns -Wincomplete-record-updates
+    if impl(ghc < 8.8)
+      ghc-options: -Wnoncanonical-monadfail-instances
+    if impl(ghc >=8.10)
+      ghc-options: -Wunused-packages
+
+common base-dep
+    build-depends: base >=4.10 && <4.19
+
+common cabal-dep
+    build-depends: Cabal >=3.10 && < 3.10.3
+
+common cabal-syntax-dep
+    build-depends: Cabal-syntax ^>=3.10
+
+common cabal-install-solver-dep
+    build-depends: cabal-install-solver ^>=3.10
+
+library
+    import: warnings, base-dep, cabal-dep, cabal-syntax-dep, cabal-install-solver-dep
+    default-language: Haskell2010
+    default-extensions: TypeOperators
+
+    hs-source-dirs:   src
+    exposed-modules:
+        -- this modules are moved from Cabal
+        -- they are needed for as long until cabal-install moves to parsec parser
+        Distribution.Deprecated.ParseUtils
+        Distribution.Deprecated.ReadP
+        Distribution.Deprecated.ViewAsFieldDescr
+
+        Distribution.Client.BuildReports.Anonymous
+        Distribution.Client.BuildReports.Lens
+        Distribution.Client.BuildReports.Storage
+        Distribution.Client.BuildReports.Types
+        Distribution.Client.BuildReports.Upload
+        Distribution.Client.Check
+        Distribution.Client.CmdBench
+        Distribution.Client.CmdBuild
+        Distribution.Client.CmdClean
+        Distribution.Client.CmdConfigure
+        Distribution.Client.CmdErrorMessages
+        Distribution.Client.CmdExec
+        Distribution.Client.CmdFreeze
+        Distribution.Client.CmdHaddock
+        Distribution.Client.CmdHaddockProject
+        Distribution.Client.CmdInstall
+        Distribution.Client.CmdInstall.ClientInstallFlags
+        Distribution.Client.CmdInstall.ClientInstallTargetSelector
+        Distribution.Client.CmdLegacy
+        Distribution.Client.CmdListBin
+        Distribution.Client.CmdOutdated
+        Distribution.Client.CmdRepl
+        Distribution.Client.CmdRun
+        Distribution.Client.CmdSdist
+        Distribution.Client.CmdTest
+        Distribution.Client.CmdUpdate
+        Distribution.Client.Compat.Directory
+        Distribution.Client.Compat.ExecutablePath
+        Distribution.Client.Compat.Orphans
+        Distribution.Client.Compat.Prelude
+        Distribution.Client.Compat.Semaphore
+        Distribution.Client.Config
+        Distribution.Client.Configure
+        Distribution.Client.Dependency
+        Distribution.Client.Dependency.Types
+        Distribution.Client.DistDirLayout
+        Distribution.Client.Fetch
+        Distribution.Client.FetchUtils
+        Distribution.Client.FileMonitor
+        Distribution.Client.Freeze
+        Distribution.Client.GZipUtils
+        Distribution.Client.GenBounds
+        Distribution.Client.Get
+        Distribution.Client.Glob
+        Distribution.Client.GlobalFlags
+        Distribution.Client.Haddock
+        Distribution.Client.HashValue
+        Distribution.Client.HttpUtils
+        Distribution.Client.IndexUtils
+        Distribution.Client.IndexUtils.ActiveRepos
+        Distribution.Client.IndexUtils.IndexState
+        Distribution.Client.IndexUtils.Timestamp
+        Distribution.Client.Init
+        Distribution.Client.Init.Defaults
+        Distribution.Client.Init.FileCreators
+        Distribution.Client.Init.FlagExtractors
+        Distribution.Client.Init.Format
+        Distribution.Client.Init.Interactive.Command
+        Distribution.Client.Init.NonInteractive.Command
+        Distribution.Client.Init.NonInteractive.Heuristics
+        Distribution.Client.Init.Licenses
+        Distribution.Client.Init.Prompt
+        Distribution.Client.Init.Simple
+        Distribution.Client.Init.Types
+        Distribution.Client.Init.Utils
+        Distribution.Client.Install
+        Distribution.Client.InstallPlan
+        Distribution.Client.InstallSymlink
+        Distribution.Client.JobControl
+        Distribution.Client.List
+        Distribution.Client.Main
+        Distribution.Client.Manpage
+        Distribution.Client.ManpageFlags
+        Distribution.Client.Nix
+        Distribution.Client.NixStyleOptions
+        Distribution.Client.PackageHash
+        Distribution.Client.ParseUtils
+        Distribution.Client.ProjectBuilding
+        Distribution.Client.ProjectBuilding.Types
+        Distribution.Client.ProjectConfig
+        Distribution.Client.ProjectConfig.Legacy
+        Distribution.Client.ProjectConfig.Types
+        Distribution.Client.ProjectFlags
+        Distribution.Client.ProjectOrchestration
+        Distribution.Client.ProjectPlanOutput
+        Distribution.Client.ProjectPlanning
+        Distribution.Client.ProjectPlanning.Types
+        Distribution.Client.RebuildMonad
+        Distribution.Client.Reconfigure
+        Distribution.Client.Run
+        Distribution.Client.Sandbox
+        Distribution.Client.Sandbox.PackageEnvironment
+        Distribution.Client.SavedFlags
+        Distribution.Client.ScriptUtils
+        Distribution.Client.Security.DNS
+        Distribution.Client.Security.HTTP
+        Distribution.Client.Setup
+        Distribution.Client.SetupWrapper
+        Distribution.Client.Signal
+        Distribution.Client.SolverInstallPlan
+        Distribution.Client.SourceFiles
+        Distribution.Client.SrcDist
+        Distribution.Client.Store
+        Distribution.Client.Tar
+        Distribution.Client.TargetProblem
+        Distribution.Client.TargetSelector
+        Distribution.Client.Targets
+        Distribution.Client.Types
+        Distribution.Client.Types.AllowNewer
+        Distribution.Client.Types.BuildResults
+        Distribution.Client.Types.ConfiguredId
+        Distribution.Client.Types.ConfiguredPackage
+        Distribution.Client.Types.Credentials
+        Distribution.Client.Types.InstallMethod
+        Distribution.Client.Types.OverwritePolicy
+        Distribution.Client.Types.PackageLocation
+        Distribution.Client.Types.PackageSpecifier
+        Distribution.Client.Types.ReadyPackage
+        Distribution.Client.Types.Repo
+        Distribution.Client.Types.RepoName
+        Distribution.Client.Types.SourcePackageDb
+        Distribution.Client.Types.SourceRepo
+        Distribution.Client.Types.WriteGhcEnvironmentFilesPolicy
+        Distribution.Client.Upload
+        Distribution.Client.Utils
+        Distribution.Client.Utils.Json
+        Distribution.Client.Utils.Parsec
+        Distribution.Client.VCS
+        Distribution.Client.Version
+        Distribution.Client.Win32SelfUpgrade
+
+    build-depends:
+        async      >= 2.0      && < 2.3,
+        array      >= 0.4      && < 0.6,
+        base16-bytestring >= 0.1.1 && < 1.1.0.0,
+        base64-bytestring >= 1.0 && < 1.3,
+        binary     >= 0.7.3    && < 0.9,
+        bytestring >= 0.10.6.0 && < 0.12,
+        containers >= 0.5.6.2  && < 0.7,
+        cryptohash-sha256 >= 0.11 && < 0.12,
+        directory  >= 1.3.7.0  && < 1.4,
+        echo       >= 0.1.3    && < 0.2,
+        edit-distance >= 0.2.2 && < 0.3,
+        exceptions >= 0.10.4   && < 0.11,
+        filepath   >= 1.4.0.0  && < 1.5,
+        hashable   >= 1.0      && < 1.5,
+        HTTP       >= 4000.1.5 && < 4000.5,
+        mtl        >= 2.0      && < 2.4,
+        network-uri >= 2.6.0.2 && < 2.7,
+        pretty     >= 1.1      && < 1.2,
+        process    >= 1.2.3.0  && < 1.7,
+        random     >= 1.2      && < 1.3,
+        stm        >= 2.0      && < 2.6,
+        tar        >= 0.5.0.3  && < 0.6,
+        time       >= 1.5.0.1  && < 1.13,
+        zlib       >= 0.5.3    && < 0.7,
+        hackage-security >= 0.6.2.0 && < 0.7,
+        text       >= 1.2.3    && < 1.3 || >= 2.0 && < 2.1,
+        parsec     >= 3.1.13.0 && < 3.2,
+        regex-base  >= 0.94.0.0 && <0.95,
+        regex-posix >= 0.96.0.0 && <0.97,
+        safe-exceptions >= 0.1.7.0 && < 0.2
+
+    if flag(native-dns)
+      if os(windows)
+        build-depends: windns      >= 0.1.0 && < 0.2
+      else
+        build-depends: resolv      >= 0.1.1 && < 0.3
+
+    if os(windows)
+      -- newer directory for symlinks
+      build-depends: Win32 >= 2.8 && < 3, directory >=1.3.1.0
+    else
+      build-depends: unix >= 2.5 && < 2.9
+
+    if flag(lukko)
+      build-depends: lukko >= 0.1 && <0.2
+
+   -- pull in process version with fixed waitForProcess error
+   if impl(ghc >=8.2)
+     build-depends: process >= 1.6.15.0
+
+
+executable cabal
+    import: warnings, base-dep
+    main-is: Main.hs
+    hs-source-dirs: main
+    default-language: Haskell2010
+
+    ghc-options: -rtsopts -threaded
+
+    -- On AIX, some legacy BSD operations such as flock(2) are provided by libbsd.a
+    if os(aix)
+        extra-libraries: bsd
+
+    build-depends:
+        cabal-install
+
+-- Small, fast running tests.
+--
+test-suite unit-tests
+    import: warnings, base-dep, cabal-dep, cabal-syntax-dep, cabal-install-solver-dep
+    default-language: Haskell2010
+    default-extensions: TypeOperators
+    ghc-options: -rtsopts -threaded
+
+    type: exitcode-stdio-1.0
+    main-is: UnitTests.hs
+    hs-source-dirs: tests
+    other-modules:
+      UnitTests.Distribution.Client.ArbitraryInstances
+      UnitTests.Distribution.Client.BuildReport
+      UnitTests.Distribution.Client.Configure
+      UnitTests.Distribution.Client.FetchUtils
+      UnitTests.Distribution.Client.Get
+      UnitTests.Distribution.Client.Glob
+      UnitTests.Distribution.Client.GZipUtils
+      UnitTests.Distribution.Client.IndexUtils
+      UnitTests.Distribution.Client.IndexUtils.Timestamp
+      UnitTests.Distribution.Client.Init
+      UnitTests.Distribution.Client.Init.Golden
+      UnitTests.Distribution.Client.Init.Interactive
+      UnitTests.Distribution.Client.Init.NonInteractive
+      UnitTests.Distribution.Client.Init.Simple
+      UnitTests.Distribution.Client.Init.Utils
+      UnitTests.Distribution.Client.Init.FileCreators
+      UnitTests.Distribution.Client.InstallPlan
+      UnitTests.Distribution.Client.JobControl
+      UnitTests.Distribution.Client.ProjectConfig
+      UnitTests.Distribution.Client.ProjectPlanning
+      UnitTests.Distribution.Client.Store
+      UnitTests.Distribution.Client.Tar
+      UnitTests.Distribution.Client.Targets
+      UnitTests.Distribution.Client.TreeDiffInstances
+      UnitTests.Distribution.Client.UserConfig
+      UnitTests.Distribution.Solver.Modular.Builder
+      UnitTests.Distribution.Solver.Modular.RetryLog
+      UnitTests.Distribution.Solver.Modular.Solver
+      UnitTests.Distribution.Solver.Modular.DSL
+      UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils
+      UnitTests.Distribution.Solver.Modular.WeightedPSQ
+      UnitTests.Distribution.Solver.Types.OptionalStanza
+      UnitTests.Options
+      UnitTests.TempTestDir
+
+    build-depends:
+          array,
+          bytestring,
+          cabal-install,
+          Cabal-tree-diff,
+          Cabal-QuickCheck,
+          containers,
+          directory,
+          filepath,
+          mtl,
+          network-uri >= 2.6.2.0 && <2.7,
+          random,
+          tar,
+          time,
+          zlib,
+          tasty >= 1.2.3 && <1.5,
+          tasty-golden >=2.3.1.1 && <2.4,
+          tasty-quickcheck,
+          tasty-hunit >= 0.10,
+          tree-diff,
+          QuickCheck >= 2.14.3 && <2.15
+
+
+-- Tests to run with a limited stack and heap size
+-- The test suite name must be keep short cause a longer one
+-- could make the build generating paths which exceeds the windows
+-- max path limit (still a problem for some ghc versions)
+test-suite mem-use-tests
+  import: warnings, base-dep, cabal-dep, cabal-syntax-dep, cabal-install-solver-dep
+  type: exitcode-stdio-1.0
+  main-is: MemoryUsageTests.hs
+  hs-source-dirs: tests
+  default-language: Haskell2010
+
+  ghc-options: -threaded -rtsopts "-with-rtsopts=-M16M -K1K"
+
+  other-modules:
+    UnitTests.Distribution.Solver.Modular.DSL
+    UnitTests.Distribution.Solver.Modular.DSL.TestCaseUtils
+    UnitTests.Distribution.Solver.Modular.MemoryUsage
+    UnitTests.Options
+
+  build-depends:
+        cabal-install,
+        containers,
+        tasty >= 1.2.3 && <1.5,
+        tasty-hunit >= 0.10
+
+
+-- Integration tests that use the cabal-install code directly
+-- but still build whole projects
+test-suite integration-tests2
+  import: warnings, base-dep, cabal-dep, cabal-syntax-dep, cabal-install-solver-dep
+  ghc-options: -rtsopts -threaded
+  type: exitcode-stdio-1.0
+  main-is: IntegrationTests2.hs
+  hs-source-dirs: tests
+  default-language: Haskell2010
+
+  build-depends:
+        bytestring,
+        cabal-install,
+        containers,
+        directory,
+        filepath,
+        tasty >= 1.2.3 && <1.5,
+        tasty-hunit >= 0.10,
+        tagged
+
+test-suite long-tests
+  import: warnings, base-dep, cabal-dep, cabal-syntax-dep, cabal-install-solver-dep
+  ghc-options: -rtsopts -threaded
+  type: exitcode-stdio-1.0
+  hs-source-dirs: tests
+  main-is: LongTests.hs
+  default-language: Haskell2010
+
+  other-modules:
+    UnitTests.Distribution.Client.ArbitraryInstances
+    UnitTests.Distribution.Client.Described
+    UnitTests.Distribution.Client.DescribedInstances
+    UnitTests.Distribution.Client.FileMonitor
+    UnitTests.Distribution.Client.VCS
+    UnitTests.Distribution.Solver.Modular.DSL
+    UnitTests.Distribution.Solver.Modular.QuickCheck
+    UnitTests.Distribution.Solver.Modular.QuickCheck.Utils
+    UnitTests.Options
+    UnitTests.TempTestDir
+
+  build-depends:
+        Cabal-QuickCheck,
+        Cabal-described,
+        cabal-install,
+        containers,
+        directory,
+        filepath,
+        hashable,
+        mtl,
+        network-uri >= 2.6.2.0 && <2.7,
+        random,
+        tagged,
+        tasty >= 1.2.3 && <1.5,
+        tasty-expected-failure,
+        tasty-hunit >= 0.10,
+        tasty-quickcheck,
+        QuickCheck >= 2.14 && <2.15,
+        pretty-show >= 1.6.15
diff --git a/test/fixtures/Cabal/hackage/hledged-web-0.24.cabal b/test/fixtures/Cabal/hackage/hledged-web-0.24.cabal
new file mode 100644
index 00000000..561deb9e
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/hledged-web-0.24.cabal
@@ -0,0 +1,279 @@
+name:           hledger-web
+version: 0.24
+stability:      stable
+category:       Finance
+synopsis:       A web interface for the hledger accounting tool.
+description:    
+                hledger is a library and set of user tools for working
+                with financial data (or anything that can be tracked in a
+                double-entry accounting ledger.) It is a haskell port and
+                friendly fork of John Wiegley's Ledger. hledger provides
+                command-line, curses and web interfaces, and aims to be a
+                reliable, practical tool for daily use.
+
+license:        GPL
+license-file:   LICENSE
+author:         Simon Michael <simon@joyful.com>
+maintainer:     Simon Michael <simon@joyful.com>
+homepage:       http://hledger.org
+bug-reports:    http://hledger.org/bugs
+tested-with:    GHC==7.6.3, GHC==7.8.2
+cabal-version:  >= 1.8
+build-type:     Simple
+extra-tmp-files:
+extra-source-files:
+                messages/en.msg
+                config/favicon.ico
+                config/keter.yaml
+                config/robots.txt
+                config/routes
+                config/settings.yml
+                static/css/bootstrap-theme.css
+                static/css/bootstrap-theme.css.map
+                static/css/bootstrap-theme.min.css
+                static/css/bootstrap.css
+                static/css/bootstrap.css.map
+                static/css/bootstrap.min.css
+                static/fonts/glyphicons-halflings-regular.eot
+                static/fonts/glyphicons-halflings-regular.svg
+                static/fonts/glyphicons-halflings-regular.ttf
+                static/fonts/glyphicons-halflings-regular.woff
+                static/js/bootstrap.js
+                static/js/bootstrap.min.js
+                static/js/excanvas.js
+                static/js/excanvas.min.js
+                static/js/jquery.colorhelpers.js
+                static/js/jquery.colorhelpers.min.js
+                static/js/jquery.cookie.js
+                static/js/jquery.flot.canvas.js
+                static/js/jquery.flot.canvas.min.js
+                static/js/jquery.flot.categories.js
+                static/js/jquery.flot.categories.min.js
+                static/js/jquery.flot.crosshair.js
+                static/js/jquery.flot.crosshair.min.js
+                static/js/jquery.flot.errorbars.js
+                static/js/jquery.flot.errorbars.min.js
+                static/js/jquery.flot.fillbetween.js
+                static/js/jquery.flot.fillbetween.min.js
+                static/js/jquery.flot.image.js
+                static/js/jquery.flot.image.min.js
+                static/js/jquery.flot.js
+                static/js/jquery.flot.min.js
+                static/js/jquery.flot.navigate.js
+                static/js/jquery.flot.navigate.min.js
+                static/js/jquery.flot.pie.js
+                static/js/jquery.flot.pie.min.js
+                static/js/jquery.flot.resize.js
+                static/js/jquery.flot.resize.min.js
+                static/js/jquery.flot.selection.js
+                static/js/jquery.flot.selection.min.js
+                static/js/jquery.flot.stack.js
+                static/js/jquery.flot.stack.min.js
+                static/js/jquery.flot.symbol.js
+                static/js/jquery.flot.symbol.min.js
+                static/js/jquery.flot.threshold.js
+                static/js/jquery.flot.threshold.min.js
+                static/js/jquery.flot.time.js
+                static/js/jquery.flot.time.min.js
+                static/js/jquery.flot.tooltip.js
+                static/js/jquery.flot.tooltip.min.js
+                static/js/jquery.hotkeys.js
+                static/js/jquery.min.js
+                static/js/jquery.url.js
+                static/js/typeahead.bundle.js
+                static/js/typeahead.bundle.min.js
+                static/hledger.css
+                static/hledger.js
+                templates/default-layout-wrapper.hamlet
+                templates/default-layout.hamlet
+                templates/homepage.hamlet
+                templates/homepage.julius
+                templates/homepage.lucius
+                templates/normalize.lucius
+                CHANGES
+
+source-repository head
+    type:     git
+    location: https://github.com/simonmichael/hledger
+
+flag threaded
+    Description:   Build with support for multithreaded execution.
+    Default:       True
+
+flag dev
+    Description:   Turn on development settings, like auto-reload templates.
+    Default:       False
+
+flag library-only
+    Description:   Build for use with "yesod devel"
+    Default:       False
+
+
+library
+    cpp-options:   -DVERSION="0.24"
+    if flag(dev) || flag(library-only)
+        cpp-options: -DDEVELOPMENT
+ 
+    ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures
+    ghc-options: -fno-warn-type-defaults -fno-warn-orphans
+ 
+    extensions:
+                CPP
+                MultiParamTypeClasses
+                NoImplicitPrelude
+                OverloadedStrings
+                QuasiQuotes
+                RecordWildCards
+                TemplateHaskell
+                TypeFamilies
+                -- seem to not be needed at present:
+                -- GADTs
+                -- GeneralizedNewtypeDeriving
+                -- FlexibleContexts
+                -- EmptyDataDecls
+                -- NoMonomorphismRestriction
+
+    exposed-modules: Application
+                     Foundation
+                     Import
+                     Settings
+                     Settings.StaticFiles
+                     Settings.Development
+                     Handler.Common
+                     Handler.JournalEditR
+                     Handler.JournalEntriesR
+                     Handler.JournalR
+                     Handler.Post
+                     Handler.RegisterR
+                     Handler.RootR
+                     Handler.SidebarR
+                     Handler.Utils
+    -- other-modules:
+                     Hledger.Web
+                     Hledger.Web.Main
+                     Hledger.Web.Options
+                     -- Setup -- stops yesod devel complaining, requires build-depends: Cabal
+    build-depends:
+                     hledger              == 0.24
+                   , hledger-lib          == 0.24
+                   , base                 >= 4 && < 5
+                   , blaze-html
+                   , blaze-markup
+                   , bytestring
+                   , clientsession
+                   , cmdargs              >= 0.10 && < 0.11
+                   , data-default
+                   , directory
+                   , filepath
+                   , hjsmin
+                   , http-conduit
+                   , http-client
+                   , HUnit
+                   , network-conduit
+                   , conduit-extra
+                   , old-locale
+                   , parsec               >= 3
+                   , regexpr              >= 0.5.1
+                   , safe                 >= 0.2
+                   , shakespeare          >= 2.0
+                   , template-haskell
+                   , text
+                   , time
+                   , transformers
+                   , wai
+                   , wai-extra
+                   , wai-handler-launch   >= 1.3
+                   , warp
+                   , yaml
+                   , yesod                >= 1.4 && < 1.5
+                   , yesod-core
+                   , yesod-static
+                   , json
+                   -- required by extra ghci utilities:
+                   -- , fsnotify
+                   -- , hsdev
+                   -- , mtl
+
+
+executable         hledger-web
+    if flag(library-only)
+        Buildable: False
+
+    cpp-options:   -DVERSION="0.24"
+    if flag(dev)
+        cpp-options: -DDEVELOPMENT
+
+    ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures
+    ghc-options: -fno-warn-type-defaults -fno-warn-orphans
+    if flag(threaded)
+        ghc-options: -threaded
+    if flag(dev)
+        ghc-options:   -O0
+
+    extensions: 
+                CPP
+                MultiParamTypeClasses
+                NoImplicitPrelude
+                OverloadedStrings
+                QuasiQuotes
+                RecordWildCards
+                TemplateHaskell
+                TypeFamilies
+
+    hs-source-dirs:  app
+    main-is:         main.hs
+
+    build-depends:
+                     hledger-lib          == 0.24
+                   , hledger              == 0.24
+                   , hledger-web          == 0.24
+                   , base                 >= 4 && < 5
+                   , blaze-html
+                   , blaze-markup
+                   , bytestring
+                   , clientsession
+                   , cmdargs              >= 0.10 && < 0.11
+                   , data-default
+                   , directory
+                   , filepath
+                   , hjsmin
+                   , http-conduit
+                   , http-client
+                   , HUnit
+                   , network-conduit
+                   , conduit-extra
+                   , old-locale
+                   , parsec               >= 3
+                   , regexpr              >= 0.5.1
+                   , safe                 >= 0.2
+                   , shakespeare          >= 2.0 && < 2.1
+                   , template-haskell
+                   , text
+                   , time
+                   , transformers
+                   , wai
+                   , wai-extra
+                   , wai-handler-launch   >= 1.3
+                   , warp
+                   , yaml
+                   , yesod                >= 1.4 && < 1.5
+                   , yesod-core
+                   , yesod-static
+                   , json
+                   -- required by extra ghci utilities:
+                   -- , fsnotify
+                   -- , hsdev
+                   -- , mtl
+
+test-suite test
+    type:              exitcode-stdio-1.0
+    ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures
+    ghc-options: -fno-warn-type-defaults -fno-warn-orphans
+    hs-source-dirs:    tests
+    main-is:           main.hs
+    build-depends: 
+                     hledger-web          == 0.24
+                   , base
+                   , hspec
+                   , yesod
+                   , yesod-test
diff --git a/test/fixtures/Cabal/hackage/hledged-web-1.23.cabal b/test/fixtures/Cabal/hackage/hledged-web-1.23.cabal
new file mode 100644
index 00000000..36319dc6
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/hledged-web-1.23.cabal
@@ -0,0 +1,250 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.34.4.
+--
+-- see: https://github.com/sol/hpack
+
+name:           hledger-web
+version:        1.23
+x-revision: 1
+synopsis:       Web-based user interface for the hledger accounting system
+description:    A simple web-based user interface for the hledger accounting system,
+                providing a more modern UI than the command-line or terminal interfaces.
+                It can be used as a local single-user UI, or as a multi-user UI for
+                viewing\/adding\/editing on the web.
+                .
+                hledger is a robust, cross-platform set of tools for tracking money,
+                time, or any other commodity, using double-entry accounting and a
+                simple, editable file format, with command-line, terminal and web
+                interfaces. It is a Haskell rewrite of Ledger, and one of the leading
+                implementations of Plain Text Accounting. Read more at:
+                <https://hledger.org>
+category:       Finance
+stability:      stable
+homepage:       http://hledger.org
+bug-reports:    http://bugs.hledger.org
+author:         Simon Michael <simon@joyful.com>
+maintainer:     Simon Michael <simon@joyful.com>
+license:        GPL-3
+license-file:   LICENSE
+build-type:     Simple
+tested-with:
+    GHC==8.8.4, GHC==8.10.4, GHC==9.0.1
+extra-source-files:
+    CHANGES.md
+    README.md
+    config/favicon.ico
+    config/keter.yaml
+    config/robots.txt
+    config/routes
+    config/settings.yml
+    static/css/bootstrap-datepicker.standalone.min.css
+    static/css/bootstrap-theme.css
+    static/css/bootstrap-theme.min.css
+    static/css/bootstrap.css
+    static/css/bootstrap.min.css
+    static/css/bootstrap-theme.css.map
+    static/css/bootstrap.css.map
+    static/fonts/glyphicons-halflings-regular.eot
+    static/fonts/glyphicons-halflings-regular.svg
+    static/fonts/glyphicons-halflings-regular.ttf
+    static/fonts/glyphicons-halflings-regular.woff
+    static/hledger.css
+    static/hledger.js
+    static/js/bootstrap-datepicker.min.js
+    static/js/bootstrap.js
+    static/js/bootstrap.min.js
+    static/js/excanvas.js
+    static/js/excanvas.min.js
+    static/js/jquery.cookie.js
+    static/js/jquery.flot.canvas.js
+    static/js/jquery.flot.canvas.min.js
+    static/js/jquery.flot.categories.js
+    static/js/jquery.flot.categories.min.js
+    static/js/jquery.flot.crosshair.js
+    static/js/jquery.flot.crosshair.min.js
+    static/js/jquery.flot.errorbars.js
+    static/js/jquery.flot.errorbars.min.js
+    static/js/jquery.flot.fillbetween.js
+    static/js/jquery.flot.fillbetween.min.js
+    static/js/jquery.flot.image.js
+    static/js/jquery.flot.image.min.js
+    static/js/jquery.flot.js
+    static/js/jquery.flot.min.js
+    static/js/jquery.flot.navigate.js
+    static/js/jquery.flot.navigate.min.js
+    static/js/jquery.flot.pie.js
+    static/js/jquery.flot.pie.min.js
+    static/js/jquery.flot.resize.js
+    static/js/jquery.flot.resize.min.js
+    static/js/jquery.flot.selection.js
+    static/js/jquery.flot.selection.min.js
+    static/js/jquery.flot.stack.js
+    static/js/jquery.flot.stack.min.js
+    static/js/jquery.flot.symbol.js
+    static/js/jquery.flot.symbol.min.js
+    static/js/jquery.flot.threshold.js
+    static/js/jquery.flot.threshold.min.js
+    static/js/jquery.flot.time.js
+    static/js/jquery.flot.time.min.js
+    static/js/jquery.flot.tooltip.js
+    static/js/jquery.flot.tooltip.min.js
+    static/js/jquery.hotkeys.js
+    static/js/jquery.js
+    static/js/jquery.min.js
+    static/js/jquery.url.js
+    static/js/typeahead.bundle.js
+    static/js/typeahead.bundle.min.js
+    templates/add-form.hamlet
+    templates/balance-report.hamlet
+    templates/chart.hamlet
+    templates/default-layout-wrapper.hamlet
+    templates/default-layout.hamlet
+    templates/edit-form.hamlet
+    templates/journal.hamlet
+    templates/manage.hamlet
+    templates/register.hamlet
+    templates/upload-form.hamlet
+    hledger-web.1
+    hledger-web.txt
+    hledger-web.info
+
+source-repository head
+  type: git
+  location: https://github.com/simonmichael/hledger
+
+flag dev
+  description: Turn on development settings, like auto-reload templates.
+  manual: False
+  default: False
+
+flag library-only
+  description: Build for use with "yesod devel"
+  manual: False
+  default: False
+
+flag threaded
+  description: Build with support for multithreaded execution.
+  manual: False
+  default: True
+
+library
+  exposed-modules:
+      Hledger.Web
+      Hledger.Web.Application
+      Hledger.Web.Foundation
+      Hledger.Web.Handler.AddR
+      Hledger.Web.Handler.EditR
+      Hledger.Web.Handler.JournalR
+      Hledger.Web.Handler.MiscR
+      Hledger.Web.Handler.RegisterR
+      Hledger.Web.Handler.UploadR
+      Hledger.Web.Import
+      Hledger.Web.Main
+      Hledger.Web.Settings
+      Hledger.Web.Settings.StaticFiles
+      Hledger.Web.Test
+      Hledger.Web.WebOptions
+      Hledger.Web.Widget.AddForm
+      Hledger.Web.Widget.Common
+  other-modules:
+      Paths_hledger_web
+  hs-source-dirs:
+      ./
+  ghc-options: -Wall -fwarn-tabs -Wcompat -Wincomplete-uni-patterns -Wincomplete-record-updates -Wredundant-constraints
+  cpp-options: -DVERSION="1.23"
+  build-depends:
+      Decimal >=0.5.1
+    , aeson >=1
+    , base >=4.11 && <4.16
+    , base64
+    , blaze-html
+    , blaze-markup
+    , bytestring
+    , case-insensitive
+    , clientsession
+    , cmdargs >=0.10
+    , conduit
+    , conduit-extra >=1.1
+    , containers >=0.5.9
+    , data-default
+    , directory >=1.2.3.0
+    , extra >=1.6.3
+    , filepath
+    , hjsmin
+    , hledger ==1.23.*
+    , hledger-lib ==1.23.*
+    , hspec
+    , http-client
+    , http-conduit
+    , http-types
+    , megaparsec >=7.0.0 && <9.3
+    , mtl >=2.2.1
+    , network
+    , shakespeare >=2.0.2.2
+    , template-haskell
+    , text >=1.2
+    , time >=1.5
+    , transformers
+    , unix-compat
+    , unordered-containers
+    , utf8-string
+    , wai
+    , wai-cors
+    , wai-extra
+    , wai-handler-launch >=3.0.3
+    , warp
+    , yaml
+    , yesod >=1.4 && <1.7
+    , yesod-core >=1.4 && <1.7
+    , yesod-form >=1.4 && <1.8
+    , yesod-static >=1.4 && <1.7
+    , yesod-test
+  if (flag(dev)) || (flag(library-only))
+    cpp-options: -DDEVELOPMENT
+  if flag(dev)
+    ghc-options: -O0
+  default-language: Haskell2010
+
+executable hledger-web
+  main-is: main.hs
+  other-modules:
+      Paths_hledger_web
+  hs-source-dirs:
+      app
+  ghc-options: -Wall -fwarn-tabs -Wcompat -Wincomplete-uni-patterns -Wincomplete-record-updates -Wredundant-constraints
+  cpp-options: -DVERSION="1.23"
+  build-depends:
+      base
+    , hledger-web
+  if (flag(dev)) || (flag(library-only))
+    cpp-options: -DDEVELOPMENT
+  if flag(dev)
+    ghc-options: -O0
+  if flag(library-only)
+    buildable: False
+  if flag(threaded)
+    ghc-options: -threaded
+  default-language: Haskell2010
+
+test-suite test
+  type: exitcode-stdio-1.0
+  main-is: test.hs
+  hs-source-dirs:
+      test
+  ghc-options: -Wall -fwarn-tabs -Wcompat -Wincomplete-uni-patterns -Wincomplete-record-updates -Wredundant-constraints
+  cpp-options: -DVERSION="1.23"
+  build-depends:
+      base
+    , hledger
+    , hledger-lib
+    , hledger-web
+    , hspec
+    , text
+    , yesod
+    , yesod-test
+  if (flag(dev)) || (flag(library-only))
+    cpp-options: -DDEVELOPMENT
+  if flag(dev)
+    ghc-options: -O0
+  default-language: Haskell2010
diff --git a/test/fixtures/Cabal/hackage/keter-0.3.4.cabal b/test/fixtures/Cabal/hackage/keter-0.3.4.cabal
new file mode 100644
index 00000000..0a3973d2
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/keter-0.3.4.cabal
@@ -0,0 +1,69 @@
+Name:                keter
+Version:             0.3.4
+Synopsis:            Web application deployment manager, focusing on Haskell web frameworks
+Description:         Handles deployment of web apps, providing a reverse proxy to achieve zero downtime deployments. For more information, please see the README on Github: <https://github.com/snoyberg/keter#readme>
+Homepage:            http://www.yesodweb.com/
+License:             MIT
+License-file:        LICENSE
+Author:              Michael Snoyman
+Maintainer:          michael@snoyman.com
+Category:            Web, Yesod
+Build-type:          Simple
+Cabal-version:       >=1.8
+
+Library
+  Build-depends:       base                      >= 4             && < 5
+                     , directory
+                     , bytestring
+                     , text
+                     , containers
+                     , transformers
+                     , process
+                     , random
+                     , data-default
+                     , filepath
+                     , zlib
+                     , tar
+                     , network
+                     , time
+                     , template-haskell
+                     , blaze-builder             >= 0.3           && < 0.4
+                     , yaml                      >= 0.7           && < 0.9
+                     , unix-compat               >= 0.3           && < 0.5
+                     , hinotify                  >= 0.3           && < 0.4
+                     , system-filepath           >= 0.4           && < 0.5
+                     , system-fileio             >= 0.3           && < 0.4
+                     , conduit                   >= 0.5           && < 0.6
+                     , network-conduit           >= 0.6           && < 0.7
+                     , network-conduit-tls       >= 0.6           && < 0.7
+                     , http-reverse-proxy        >= 0.1.0.2       && < 0.2
+                     , unix-process-conduit      >= 0.2           && < 0.3
+                     , unix                      >= 2.5           && < 2.7
+                     , wai-app-static            >= 1.3           && < 1.4
+                     , wai                       >= 1.3           && < 1.4
+                     , http-types
+  Exposed-Modules:     Keter.Process
+                       Keter.ProcessTracker
+                       Keter.Postgres
+                       Keter.TempFolder
+                       Keter.App
+                       Keter.Main
+                       Keter.Prelude
+                       Keter.LogFile
+                       Keter.Logger
+                       Keter.Proxy
+                       Keter.PortManager
+                       Keter.SSL
+  c-sources:           cbits/process-tracker.c
+  ghc-options:         -Wall
+
+Executable keter
+  Main-is:             keter.hs
+  hs-source-dirs:      main
+  Build-depends:       base, keter
+  ghc-options:         -threaded -Wall
+  other-modules:       Paths_keter
+
+source-repository head
+  type:     git
+  location: https://github.com/snoyberg/keter
diff --git a/test/fixtures/Cabal/hackage/keter-1.8.4.cabal b/test/fixtures/Cabal/hackage/keter-1.8.4.cabal
new file mode 100644
index 00000000..a9ff4162
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/keter-1.8.4.cabal
@@ -0,0 +1,139 @@
+Cabal-version:       >=1.10
+Name:                keter
+Version:             1.8.4
+Synopsis:            Web application deployment manager, focusing on Haskell web frameworks
+Description:
+    Deployment system for web applications, originally intended for hosting Yesod
+    applications. Keter does the following actions for your application:
+    .
+    * Binds to the main port (usually port 80) and reverse proxies requests to your application based on virtual hostnames.
+    * Provides SSL support if requested.
+    * Automatically launches applications, monitors processes, and relaunches any processes which die.
+    * Provides graceful redeployment support, by launching a second copy of your application, performing a health check[1], and then switching reverse proxying to the new process.
+    * Management of log files.
+    .
+    Keter provides many more advanced features and extension points. It allows configuration of static hosts, redirect rules, management of PostgreSQL databases, and more. It supports a simple bundle format for applications which allows for easy management of your web apps.
+    .
+    1: The health check happens trough checking if a port is opened. If your app doesn't open a port after 30 seconds it's presumed not healthy and gets a term signal.
+
+Homepage:            http://www.yesodweb.com/
+License:             MIT
+License-file:        LICENSE
+Author:              Michael Snoyman
+Maintainer:          michael@snoyman.com
+Category:            Web, Yesod
+Build-type:          Simple
+extra-source-files:  ChangeLog.md
+                     README.md
+
+--Data-Files:        incoming/foo/bundle.sh, incoming/foo/config/keter.yaml
+
+flag system-filepath
+  description: Use system-filepath
+  default: False
+
+Library
+  default-language:    Haskell98
+  Build-depends:       base                      >= 4             && < 5
+                     , directory
+                     , fsnotify >= 0.3
+                     , bytestring
+                     , text
+                     , containers
+                     , transformers
+                     , process                   >= 1.4.3         && < 1.7
+                     , random
+                     , data-default
+                     , filepath
+                     , zlib
+                     , network
+                     , time
+                     , tar                       >= 0.4
+                     , template-haskell
+                     , blaze-builder             >= 0.3           && < 0.5
+                     , yaml                      >= 0.8.4         && < 0.12
+                     , unix-compat               >= 0.3           && < 0.6
+                     , conduit                   >= 1.1
+                     , conduit-extra             >= 1.1
+                     , http-reverse-proxy        >= 0.4.2         && < 0.7
+                     , unix                      >= 2.5
+                     , wai-app-static            >= 3.1           && < 3.2
+                     , wai                       >= 3.2.2
+                     , wai-extra                 >= 3.0.3         && < 3.2
+                     , http-types
+                     , regex-tdfa                >= 1.1
+                     , attoparsec                >= 0.10
+                     , http-client
+                     , http-conduit              >= 2.1
+                     , case-insensitive
+                     , array
+                     , mtl
+                     , warp
+                     , warp-tls                  >= 3.0.3         && < 3.4.0
+                     , aeson
+                     , unordered-containers
+                     , vector
+                     , stm                       >= 2.4
+                     , async
+                     , lifted-base
+                     , tls                       >= 1.4
+                     , tls-session-manager
+                     , optparse-applicative
+                     , indexed-traversable
+
+  if impl(ghc < 7.6)
+    build-depends:     ghc-prim
+  if flag(system-filepath)
+    build-depends:
+                     system-filepath
+    cpp-options:       -DSYSTEM_FILEPATH
+
+  Exposed-Modules:     Keter.Plugin.Postgres
+                       Keter.Types
+                       Keter.Types.V04
+                       Keter.Types.V10
+                       Keter.Types.Common
+                       Keter.Types.Middleware
+                       Keter.App
+                       Keter.AppManager
+                       Keter.LabelMap
+                       Keter.Cli
+                       Keter.Main
+                       Keter.PortPool
+                       Keter.Proxy
+                       Keter.HostManager
+                       Network.HTTP.ReverseProxy.Rewrite
+                       Data.Yaml.FilePath
+                       Data.Aeson.KeyHelper
+                       Codec.Archive.TempTarball
+                       Data.Conduit.LogFile
+                       Data.Conduit.Process.Unix
+  ghc-options:         -Wall
+  c-sources:           cbits/process-tracker.c
+
+Executable keter
+  default-language:    Haskell98
+  Main-is:             keter.hs
+  hs-source-dirs:      main
+  Build-depends:       base, keter, data-default, filepath
+  ghc-options:         -threaded -Wall
+  other-modules:       Paths_keter
+
+test-suite test
+    default-language:    Haskell98
+    hs-source-dirs: test
+    main-is: Spec.hs
+    type: exitcode-stdio-1.0
+    build-depends:   base
+                   , transformers
+                   , conduit
+                   , bytestring
+                   , hspec >= 1.3
+                   , unix
+                   , keter
+                   , HUnit
+    ghc-options:     -Wall -threaded
+
+source-repository head
+  type:     git
+  location: https://github.com/snoyberg/keter
diff --git a/test/fixtures/Cabal/hackage/keter-1.8.cabal b/test/fixtures/Cabal/hackage/keter-1.8.cabal
new file mode 100644
index 00000000..8badfca1
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/keter-1.8.cabal
@@ -0,0 +1,126 @@
+Cabal-version:       >=1.10
+Name:                keter
+Version:             1.8
+Synopsis:            Web application deployment manager, focusing on Haskell web frameworks
+Description:         Hackage documentation generation is not reliable. For up to date documentation, please see: <http://www.stackage.org/package/keter>.
+Homepage:            http://www.yesodweb.com/
+License:             MIT
+License-file:        LICENSE
+Author:              Michael Snoyman
+Maintainer:          michael@snoyman.com
+Category:            Web, Yesod
+Build-type:          Simple
+extra-source-files:  ChangeLog.md
+                     README.md
+
+--Data-Files:        incoming/foo/bundle.sh, incoming/foo/config/keter.yaml
+
+flag system-filepath
+  description: Use system-filepath
+  default: False
+
+Library
+  default-language:    Haskell98
+  Build-depends:       base                      >= 4             && < 5
+                     , directory
+                     , fsnotify >= 0.3
+                     , bytestring
+                     , text
+                     , containers
+                     , transformers
+                     , process                   >= 1.4.3         && < 1.7
+                     , random
+                     , data-default
+                     , filepath
+                     , zlib
+                     , network
+                     , time
+                     , tar                       >= 0.4
+                     , template-haskell
+                     , blaze-builder             >= 0.3           && < 0.5
+                     , yaml                      >= 0.8.4         && < 0.12
+                     , unix-compat               >= 0.3           && < 0.6
+                     , conduit                   >= 1.1
+                     , conduit-extra             >= 1.1
+                     , http-reverse-proxy        >= 0.4.2         && < 0.7
+                     , unix                      >= 2.5
+                     , wai-app-static            >= 3.1           && < 3.2
+                     , wai                       >= 3.2.2
+                     , wai-extra                 >= 3.0.3         && < 3.2
+                     , http-types
+                     , regex-tdfa                >= 1.1
+                     , attoparsec                >= 0.10
+                     , http-client
+                     , http-conduit              >= 2.1
+                     , case-insensitive
+                     , array
+                     , mtl
+                     , warp
+                     , warp-tls                  >= 3.0.3         && < 3.4.0
+                     , aeson
+                     , unordered-containers
+                     , vector
+                     , stm                       >= 2.4
+                     , async
+                     , lifted-base
+                     , tls                       >= 1.4
+                     , tls-session-manager
+                     , optparse-applicative
+                     , indexed-traversable
+
+  if impl(ghc < 7.6)
+    build-depends:     ghc-prim
+  if flag(system-filepath)
+    build-depends:
+                     system-filepath
+    cpp-options:       -DSYSTEM_FILEPATH
+
+  Exposed-Modules:     Keter.Plugin.Postgres
+                       Keter.Types
+                       Keter.Types.V04
+                       Keter.Types.V10
+                       Keter.Types.Common
+                       Keter.Types.Middleware
+                       Keter.App
+                       Keter.AppManager
+                       Keter.LabelMap
+                       Keter.Cli
+                       Keter.Main
+                       Keter.PortPool
+                       Keter.Proxy
+                       Keter.HostManager
+                       Network.HTTP.ReverseProxy.Rewrite
+                       Data.Yaml.FilePath
+                       Data.Aeson.KeyHelper
+                       Codec.Archive.TempTarball
+                       Data.Conduit.LogFile
+                       Data.Conduit.Process.Unix
+  ghc-options:         -Wall
+  c-sources:           cbits/process-tracker.c
+
+Executable keter
+  default-language:    Haskell98
+  Main-is:             keter.hs
+  hs-source-dirs:      main
+  Build-depends:       base, keter, data-default, filepath
+  ghc-options:         -threaded -Wall
+  other-modules:       Paths_keter
+
+test-suite test
+    default-language:    Haskell98
+    hs-source-dirs: test
+    main-is: Spec.hs
+    type: exitcode-stdio-1.0
+    build-depends:   base
+                   , transformers
+                   , conduit
+                   , bytestring
+                   , hspec >= 1.3
+                   , unix
+                   , keter
+                   , HUnit
+    ghc-options:     -Wall -threaded
+
+source-repository head
+  type:     git
+  location: https://github.com/snoyberg/keter
diff --git a/test/fixtures/Cabal/hackage/pandoc-1.13.cabal b/test/fixtures/Cabal/hackage/pandoc-1.13.cabal
new file mode 100644
index 00000000..3486ad48
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/pandoc-1.13.cabal
@@ -0,0 +1,427 @@
+Name:            pandoc
+Version:         1.13
+Cabal-Version:   >= 1.10
+Build-Type:      Custom
+License:         GPL
+License-File:    COPYING
+Copyright:       (c) 2006-2014 John MacFarlane
+Author:          John MacFarlane <jgm@berkeley.edu>
+Maintainer:      John MacFarlane <jgm@berkeley.edu>
+Bug-Reports:     https://github.com/jgm/pandoc/issues
+Stability:       alpha
+Homepage:        http://johnmacfarlane.net/pandoc
+Category:        Text
+Tested-With:     GHC == 7.4.2, GHC == 7.6.3, GHC == 7.8.2
+Synopsis:        Conversion between markup formats
+Description:     Pandoc is a Haskell library for converting from one markup
+                 format to another, and a command-line tool that uses
+                 this library. It can read markdown and (subsets of) HTML,
+                 reStructuredText, LaTeX, DocBook, MediaWiki markup, Haddock
+                 markup, OPML, Emacs Org-Mode, txt2tags and Textile, and it can write
+                 markdown, reStructuredText, HTML, LaTeX, ConTeXt, Docbook,
+                 OPML, OpenDocument, ODT, Word docx, RTF, MediaWiki, DokuWiki,
+                 Textile, groff man pages, plain text, Emacs Org-Mode, AsciiDoc,
+                 Haddock markup, EPUB (v2 and v3), FictionBook2,
+                 InDesign ICML, and several kinds of HTML/javascript
+                 slide shows (S5, Slidy, Slideous, DZSlides, reveal.js).
+                 .
+                 Pandoc extends standard markdown syntax with footnotes,
+                 embedded LaTeX, definition lists, tables, and other
+                 features. A compatibility mode is provided for those
+                 who need a drop-in replacement for Markdown.pl.
+                 .
+                 In contrast to existing tools for converting markdown
+                 to HTML, which use regex substitutions, pandoc has
+                 a modular design: it consists of a set of readers,
+                 which parse text in a given format and produce a native
+                 representation of the document, and a set of writers,
+                 which convert this native representation into a target
+                 format. Thus, adding an input or output format requires
+                 only adding a reader or writer.
+Data-Files:
+                 -- templates
+                 data/templates/default.html
+                 data/templates/default.html5
+                 data/templates/default.docbook
+                 data/templates/default.beamer
+                 data/templates/default.opendocument
+                 data/templates/default.icml
+                 data/templates/default.opml
+                 data/templates/default.latex
+                 data/templates/default.context
+                 data/templates/default.texinfo
+                 data/templates/default.man
+                 data/templates/default.markdown
+                 data/templates/default.rst
+                 data/templates/default.plain
+                 data/templates/default.mediawiki
+                 data/templates/default.dokuwiki
+                 data/templates/default.rtf
+                 data/templates/default.s5
+                 data/templates/default.slidy
+                 data/templates/default.slideous
+                 data/templates/default.revealjs
+                 data/templates/default.dzslides
+                 data/templates/default.asciidoc
+                 data/templates/default.haddock
+                 data/templates/default.textile
+                 data/templates/default.org
+                 data/templates/default.epub
+                 data/templates/default.epub3
+                 -- data for ODT writer
+                 data/reference.odt
+                 -- data for docx writer
+                 data/reference.docx
+                 -- stylesheet for EPUB writer
+                 data/epub.css
+                 -- data for LaTeXMathML writer
+                 data/LaTeXMathML.js
+                 data/MathMLinHTML.js
+                 -- data for dzslides writer
+                 data/dzslides/template.html
+                 -- sample lua custom writer
+                 data/sample.lua
+                 -- documentation
+                 README, COPYRIGHT
+Extra-Source-Files:
+                 -- documentation
+                 INSTALL, BUGS, CONTRIBUTING.md, changelog
+                 -- code to create pandoc.1 man page
+                 Makefile
+                 man/man1/pandoc.1.template
+                 man/man5/pandoc_markdown.5.template
+                 -- generated man pages (produced post-build)
+                 man/man1/pandoc.1
+                 man/man5/pandoc_markdown.5
+                 -- tests
+                 tests/bodybg.gif
+                 tests/*.native
+                 tests/docbook-reader.docbook
+                 tests/html-reader.html
+                 tests/opml-reader.opml
+                 tests/haddock-reader.haddock
+                 tests/insert
+                 tests/lalune.jpg
+                 tests/movie.jpg
+                 tests/latex-reader.latex
+                 tests/textile-reader.textile
+                 tests/markdown-reader-more.txt
+                 tests/markdown-citations.txt
+                 tests/textile-reader.textile
+                 tests/mediawiki-reader.wiki
+                 tests/rst-reader.rst
+                 tests/s5-basic.html
+                 tests/s5-fancy.html
+                 tests/s5-fragment.html
+                 tests/s5-inserts.html
+                 tests/tables.context
+                 tests/tables.docbook
+                 tests/tables.html
+                 tests/tables.latex
+                 tests/tables.man
+                 tests/tables.plain
+                 tests/tables.markdown
+                 tests/tables.mediawiki
+                 tests/tables.textile
+                 tests/tables.opendocument
+                 tests/tables.org
+                 tests/tables.asciidoc
+                 tests/tables.haddock
+                 tests/tables.texinfo
+                 tests/tables.rst
+                 tests/tables.rtf
+                 tests/tables.txt
+                 tests/tables.fb2
+                 tests/testsuite.txt
+                 tests/writer.latex
+                 tests/writer.context
+                 tests/writer.docbook
+                 tests/writer.html
+                 tests/writer.man
+                 tests/writer.markdown
+                 tests/writer.plain
+                 tests/writer.mediawiki
+                 tests/writer.textile
+                 tests/writer.opendocument
+                 tests/writer.org
+                 tests/writer.asciidoc
+                 tests/writer.haddock
+                 tests/writer.rst
+                 tests/writer.icml
+                 tests/writer.rtf
+                 tests/writer.texinfo
+                 tests/writer.fb2
+                 tests/writer.opml
+                 tests/writer.dokuwiki
+                 tests/dokuwiki_inline_formatting.dokuwiki
+                 tests/lhs-test.markdown
+                 tests/lhs-test.markdown+lhs
+                 tests/lhs-test.rst
+                 tests/lhs-test.rst+lhs
+                 tests/lhs-test.latex
+                 tests/lhs-test.latex+lhs
+                 tests/lhs-test.html
+                 tests/lhs-test.html+lhs
+                 tests/lhs-test.fragment.html+lhs
+                 tests/pipe-tables.txt
+                 tests/fb2/*.markdown
+                 tests/fb2/*.fb2
+                 tests/fb2/images-embedded.html
+                 tests/fb2/images-embedded.fb2
+                 tests/fb2/test-small.png
+                 tests/fb2/test.jpg
+                 tests/docx/*.docx
+                 tests/docx/*.native
+                 tests/epub/*.epub
+                 tests/epub/*.native
+                 tests/txt2tags.t2t
+
+Source-repository head
+  type:          git
+  location:      git://github.com/jgm/pandoc.git
+
+Flag embed_data_files
+  Description:   Embed data files in binary for relocatable executable.
+  Default:       False
+
+Flag https
+  Description:   Enable support for downloading of resources over https.
+  Default:       True
+
+Flag make-pandoc-man-pages
+  Description:   Build program to regenerate pandoc man pages from README.
+  Default:       False
+
+Library
+  Build-Depends: base >= 4.2 && <5,
+                 syb >= 0.1 && < 0.5,
+                 containers >= 0.1 && < 0.6,
+                 unordered-containers >= 0.2 && < 0.3,
+                 array >= 0.3 && < 0.6,
+                 parsec >= 3.1 && < 3.2,
+                 mtl >= 1.1 && < 2.3,
+                 network >= 2 && < 2.6,
+                 filepath >= 1.1 && < 1.4,
+                 process >= 1 && < 1.3,
+                 directory >= 1 && < 1.3,
+                 bytestring >= 0.9 && < 0.11,
+                 text >= 0.11 && < 1.2,
+                 zip-archive >= 0.2.3.4 && < 0.3,
+                 old-locale >= 1 && < 1.1,
+                 time >= 1.2 && < 1.5,
+                 HTTP >= 4000.0.5 && < 4000.3,
+                 texmath >= 0.8 && < 0.9,
+                 xml >= 1.3.12 && < 1.4,
+                 random >= 1 && < 1.1,
+                 extensible-exceptions >= 0.1 && < 0.2,
+                 pandoc-types >= 1.12.4 && < 1.13,
+                 aeson >= 0.7 && < 0.9,
+                 tagsoup >= 0.13.1 && < 0.14,
+                 base64-bytestring >= 0.1 && < 1.1,
+                 zlib >= 0.5 && < 0.6,
+                 highlighting-kate >= 0.5.8.5 && < 0.6,
+                 data-default >= 0.4 && < 0.6,
+                 temporary >= 1.1 && < 1.3,
+                 blaze-html >= 0.5 && < 0.8,
+                 blaze-markup >= 0.5.1 && < 0.7,
+                 yaml >= 0.8.8.2 && < 0.9,
+                 scientific >= 0.2 && < 0.4,
+                 vector >= 0.10 && < 0.11,
+                 hslua >= 0.3 && < 0.4,
+                 binary >= 0.5 && < 0.8,
+                 SHA >= 1.6 && < 1.7,
+                 haddock-library >= 1.1 && < 1.2,
+                 old-time,
+                 deepseq-generics >= 0.1 && < 0.2,
+                 JuicyPixels >= 3.1.6.1 && < 3.2
+  if flag(https)
+     Build-Depends: http-client >= 0.3.2 && < 0.4,
+                    http-client-tls >= 0.2 && < 0.3,
+                    http-types >= 0.8 && < 0.9
+     cpp-options:   -DHTTP_CLIENT
+  if flag(embed_data_files)
+     cpp-options:   -DEMBED_DATA_FILES
+     -- Build-Tools:   hsb2hs -- not yet recognized by cabal
+     other-modules: Text.Pandoc.Data
+  if os(windows)
+    Cpp-options:      -D_WINDOWS
+  Ghc-Options:   -rtsopts -Wall -fno-warn-unused-do-bind
+  Ghc-Prof-Options: -auto-all -caf-all -rtsopts
+  Default-Language: Haskell98
+  Other-Extensions:   PatternGuards, OverloadedStrings,
+                      ScopedTypeVariables, GeneralizedNewtypeDeriving,
+                      RelaxedPolyRec, DeriveDataTypeable, TypeSynonymInstances,
+                      FlexibleInstances
+  Hs-Source-Dirs:  src
+
+  Exposed-Modules: Text.Pandoc,
+                   Text.Pandoc.Options,
+                   Text.Pandoc.Pretty,
+                   Text.Pandoc.Shared,
+                   Text.Pandoc.MediaBag,
+                   Text.Pandoc.Readers.HTML,
+                   Text.Pandoc.Readers.LaTeX,
+                   Text.Pandoc.Readers.Markdown,
+                   Text.Pandoc.Readers.MediaWiki,
+                   Text.Pandoc.Readers.RST,
+                   Text.Pandoc.Readers.Org,
+                   Text.Pandoc.Readers.DocBook,
+                   Text.Pandoc.Readers.OPML,
+                   Text.Pandoc.Readers.TeXMath,
+                   Text.Pandoc.Readers.Textile,
+                   Text.Pandoc.Readers.Native,
+                   Text.Pandoc.Readers.Haddock,
+                   Text.Pandoc.Readers.Docx,
+                   Text.Pandoc.Readers.EPUB,
+                   Text.Pandoc.Writers.Native,
+                   Text.Pandoc.Writers.Docbook,
+                   Text.Pandoc.Writers.OPML,
+                   Text.Pandoc.Writers.HTML,
+                   Text.Pandoc.Writers.ICML,
+                   Text.Pandoc.Writers.LaTeX,
+                   Text.Pandoc.Writers.ConTeXt,
+                   Text.Pandoc.Writers.OpenDocument,
+                   Text.Pandoc.Writers.Texinfo,
+                   Text.Pandoc.Writers.Man,
+                   Text.Pandoc.Writers.Markdown,
+                   Text.Pandoc.Writers.Haddock,
+                   Text.Pandoc.Writers.RST,
+                   Text.Pandoc.Writers.Org,
+                   Text.Pandoc.Writers.AsciiDoc,
+                   Text.Pandoc.Writers.Custom,
+                   Text.Pandoc.Writers.Textile,
+                   Text.Pandoc.Writers.MediaWiki,
+                   Text.Pandoc.Writers.DokuWiki,
+                   Text.Pandoc.Writers.RTF,
+                   Text.Pandoc.Writers.ODT,
+                   Text.Pandoc.Writers.Docx,
+                   Text.Pandoc.Writers.EPUB,
+                   Text.Pandoc.Writers.FB2,
+                   Text.Pandoc.PDF,
+                   Text.Pandoc.UTF8,
+                   Text.Pandoc.Templates,
+                   Text.Pandoc.XML,
+                   Text.Pandoc.SelfContained,
+                   Text.Pandoc.Process,
+                   Text.Pandoc.Readers.Txt2Tags
+  Other-Modules:   Text.Pandoc.Readers.Docx.Lists,
+                   Text.Pandoc.Readers.Docx.Reducible,
+                   Text.Pandoc.Readers.Docx.Parse,
+                   Text.Pandoc.Readers.Docx.Fonts
+                   Text.Pandoc.Writers.Shared,
+                   Text.Pandoc.Asciify,
+                   Text.Pandoc.MIME,
+                   Text.Pandoc.Parsing,
+                   Text.Pandoc.UUID,
+                   Text.Pandoc.ImageSize,
+                   Text.Pandoc.Slides,
+                   Text.Pandoc.Highlighting,
+                   Text.Pandoc.Compat.Monoid,
+                   Text.Pandoc.Compat.Except,
+                   Text.Pandoc.Compat.TagSoupEntity,
+                   Text.Pandoc.Compat.Directory
+                   Paths_pandoc
+
+  Buildable:       True
+
+Executable pandoc
+  Build-Depends: pandoc,
+                 pandoc-types >= 1.12.4 && < 1.13,
+                 base >= 4.2 && <5,
+                 directory >= 1 && < 1.3,
+                 filepath >= 1.1 && < 1.4,
+                 network >= 2 && < 2.6,
+                 text >= 0.11 && < 1.2,
+                 bytestring >= 0.9 && < 0.11,
+                 extensible-exceptions >= 0.1 && < 0.2,
+                 highlighting-kate >= 0.5.8.5 && < 0.6,
+                 aeson >= 0.7.0.5 && < 0.9,
+                 yaml >= 0.8.8.2 && < 0.9,
+                 containers >= 0.1 && < 0.6,
+                 HTTP >= 4000.0.5 && < 4000.3
+  Ghc-Options:   -rtsopts -with-rtsopts=-K16m -Wall -fno-warn-unused-do-bind
+  Ghc-Prof-Options: -auto-all -caf-all -rtsopts -with-rtsopts=-K16m
+  if os(windows)
+    Cpp-options:      -D_WINDOWS
+  Default-Language: Haskell98
+  Other-Extensions: PatternGuards, OverloadedStrings,
+                    ScopedTypeVariables, GeneralizedNewtypeDeriving,
+                    RelaxedPolyRec, DeriveDataTypeable, TypeSynonymInstances,
+                    FlexibleInstances
+  Hs-Source-Dirs:  .
+  Main-Is:         pandoc.hs
+  Buildable:       True
+
+-- NOTE:  A trick in Setup.hs makes sure this won't be installed:
+Executable make-pandoc-man-pages
+  Main-Is:       make-pandoc-man-pages.hs
+  Hs-Source-Dirs: man
+  Build-Depends: pandoc,
+                 base >= 4.2 && < 5,
+                 directory >= 1 && < 1.3,
+                 filepath >= 1.1 && < 1.4,
+                 old-time >= 1.0 && < 1.2,
+                 time >= 1.2 && < 1.5
+  Default-Language: Haskell98
+  if flag(make-pandoc-man-pages)
+    Buildable:   True
+  else
+    Buildable:   False
+
+Test-Suite test-pandoc
+  Type:           exitcode-stdio-1.0
+  Main-Is:        test-pandoc.hs
+  Hs-Source-Dirs: tests
+  Build-Depends:  base >= 4.2 && < 5,
+                  syb >= 0.1 && < 0.5,
+                  pandoc,
+                  pandoc-types >= 1.12.4 && < 1.13,
+                  bytestring >= 0.9 && < 0.11,
+                  text >= 0.11 && < 1.2,
+                  directory >= 1 && < 1.3,
+                  filepath >= 1.1 && < 1.4,
+                  process >= 1 && < 1.3,
+                  highlighting-kate >= 0.5.8.5 && < 0.6,
+                  Diff >= 0.2 && < 0.4,
+                  test-framework >= 0.3 && < 0.9,
+                  test-framework-hunit >= 0.2 && < 0.4,
+                  test-framework-quickcheck2 >= 0.2.9 && < 0.4,
+                  QuickCheck >= 2.4 && < 2.8,
+                  HUnit >= 1.2 && < 1.3,
+                  containers >= 0.1 && < 0.6,
+                  ansi-terminal >= 0.5 && < 0.7,
+                  executable-path >= 0.0 && < 0.1,
+                  zip-archive >= 0.2.3.4 && < 0.3
+  Other-Modules:  Tests.Old
+                  Tests.Helpers
+                  Tests.Arbitrary
+                  Tests.Shared
+                  Tests.Walk
+                  Tests.Readers.LaTeX
+                  Tests.Readers.Markdown
+                  Tests.Readers.Org
+                  Tests.Readers.RST
+                  Tests.Readers.Docx
+                  Tests.Readers.Txt2Tags
+                  Tests.Readers.EPUB
+                  Tests.Writers.Native
+                  Tests.Writers.ConTeXt
+                  Tests.Writers.Docbook
+                  Tests.Writers.HTML
+                  Tests.Writers.Markdown
+                  Tests.Writers.Plain
+                  Tests.Writers.AsciiDoc
+                  Tests.Writers.LaTeX
+  Ghc-Options:  -rtsopts -Wall -fno-warn-unused-do-bind
+  Default-Language: Haskell98
+
+benchmark benchmark-pandoc
+  Type:            exitcode-stdio-1.0
+  Main-Is:         benchmark-pandoc.hs
+  Hs-Source-Dirs:  benchmark
+  Build-Depends:   pandoc,
+                   base >= 4.2 && < 5,
+                   syb >= 0.1 && < 0.5,
+                   criterion >= 0.5 && < 0.9
+  Ghc-Options:   -rtsopts -Wall -fno-warn-unused-do-bind
+  Default-Language: Haskell98
diff --git a/test/fixtures/Cabal/hackage/pandoc-3.1.4.cabal b/test/fixtures/Cabal/hackage/pandoc-3.1.4.cabal
new file mode 100644
index 00000000..06f317b8
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/pandoc-3.1.4.cabal
@@ -0,0 +1,842 @@
+cabal-version:   2.4
+name:            pandoc
+version:         3.1.4
+build-type:      Simple
+license:         GPL-2.0-or-later
+license-file:    COPYING.md
+copyright:       (c) 2006-2022 John MacFarlane
+author:          John MacFarlane <jgm@berkeley.edu>
+maintainer:      John MacFarlane <jgm@berkeley.edu>
+bug-reports:     https://github.com/jgm/pandoc/issues
+stability:       alpha
+homepage:        https://pandoc.org
+category:        Text
+tested-with:     GHC == 8.6.5, GHC == 8.8.4, GHC == 8.10.7, GHC == 9.0.2,
+                 GHC == 9.2.5, GHC == 9.4.4
+synopsis:        Conversion between markup formats
+description:     Pandoc is a Haskell library for converting from one markup
+                 format to another.  The formats it can handle include
+                 .
+                 - light markup formats (many variants of Markdown,
+                   reStructuredText, AsciiDoc, Org-mode, Muse, Textile,
+                   txt2tags)
+                 - HTML formats (HTML 4 and 5)
+                 - Ebook formats (EPUB v2 and v3, FB2)
+                 - Documentation formats (GNU TexInfo, Haddock)
+                 - Roff formats (man, ms)
+                 - TeX formats (LaTeX, ConTeXt)
+                 - Typst
+                 - XML formats (DocBook 4 and 5, JATS, TEI Simple, OpenDocument)
+                 - Outline formats (OPML)
+                 - Bibliography formats (BibTeX, BibLaTeX, CSL JSON, CSL YAML,
+                   RIS)
+                 - Word processor formats (Docx, RTF, ODT)
+                 - Interactive notebook formats (Jupyter notebook ipynb)
+                 - Page layout formats (InDesign ICML)
+                 - Wiki markup formats (MediaWiki, DokuWiki, TikiWiki, TWiki,
+                   Vimwiki, XWiki, ZimWiki, Jira wiki, Creole)
+                 - Slide show formats (LaTeX Beamer, PowerPoint, Slidy,
+                   reveal.js, Slideous, S5, DZSlides)
+                 - Data formats (CSV and TSV tables)
+                 - PDF (via external programs such as pdflatex or wkhtmltopdf)
+                 .
+                 Pandoc can convert mathematical content in documents
+                 between TeX, MathML, Word equations, roff eqn, typst,
+                 and plain text. It includes a powerful system for automatic
+                 citations and bibliographies, and it can be customized extensively
+                 using templates, filters, and custom readers and writers
+                 written in Lua.
+data-files:
+                 -- templates
+                 data/templates/styles.html
+                 data/templates/styles.citations.html
+                 data/templates/default.html4
+                 data/templates/default.html5
+                 data/templates/default.chunkedhtml
+                 data/templates/default.docbook4
+                 data/templates/default.docbook5
+                 data/templates/default.jats_archiving
+                 data/templates/default.jats_articleauthoring
+                 data/templates/default.jats_publishing
+                 data/templates/default.tei
+                 data/templates/default.opendocument
+                 data/templates/default.icml
+                 data/templates/default.opml
+                 data/templates/default.latex
+                 data/templates/default.bibtex
+                 data/templates/default.biblatex
+                 data/templates/default.context
+                 data/templates/default.texinfo
+                 data/templates/default.jira
+                 data/templates/default.man
+                 data/templates/default.ms
+                 data/templates/default.markdown
+                 data/templates/default.muse
+                 data/templates/default.commonmark
+                 data/templates/default.rst
+                 data/templates/default.plain
+                 data/templates/default.mediawiki
+                 data/templates/default.dokuwiki
+                 data/templates/default.xwiki
+                 data/templates/default.zimwiki
+                 data/templates/default.rtf
+                 data/templates/default.s5
+                 data/templates/default.slidy
+                 data/templates/default.slideous
+                 data/templates/default.revealjs
+                 data/templates/default.dzslides
+                 data/templates/default.asciidoc
+                 data/templates/default.asciidoctor
+                 data/templates/default.haddock
+                 data/templates/default.textile
+                 data/templates/default.org
+                 data/templates/default.epub2
+                 data/templates/default.epub3
+                 data/templates/article.jats_publishing
+                 data/templates/affiliations.jats
+                 data/templates/default.markua
+                 data/templates/default.typst
+                 data/templates/definitions.typst
+                 data/templates/template.typst
+                 -- translations
+                 data/translations/*.yaml
+                 -- entities
+                 data/docbook-entities.txt
+                 -- source files for reference.docx
+                 data/docx/[Content_Types].xml
+                 data/docx/_rels/.rels
+                 data/docx/docProps/app.xml
+                 data/docx/docProps/core.xml
+                 data/docx/docProps/custom.xml
+                 data/docx/word/document.xml
+                 data/docx/word/fontTable.xml
+                 data/docx/word/comments.xml
+                 data/docx/word/footnotes.xml
+                 data/docx/word/numbering.xml
+                 data/docx/word/settings.xml
+                 data/docx/word/webSettings.xml
+                 data/docx/word/styles.xml
+                 data/docx/word/_rels/document.xml.rels
+                 data/docx/word/_rels/footnotes.xml.rels
+                 data/docx/word/theme/theme1.xml
+                 -- source files for reference.odt
+                 data/odt/mimetype
+                 data/odt/manifest.rdf
+                 data/odt/styles.xml
+                 data/odt/content.xml
+                 data/odt/meta.xml
+                 data/odt/META-INF/manifest.xml
+                 -- source files for reference.pptx
+                 data/pptx/_rels/.rels
+                 data/pptx/docProps/app.xml
+                 data/pptx/docProps/core.xml
+                 data/pptx/ppt/slideLayouts/_rels/slideLayout1.xml.rels
+                 data/pptx/ppt/slideLayouts/_rels/slideLayout2.xml.rels
+                 data/pptx/ppt/slideLayouts/_rels/slideLayout3.xml.rels
+                 data/pptx/ppt/slideLayouts/_rels/slideLayout4.xml.rels
+                 data/pptx/ppt/slideLayouts/_rels/slideLayout5.xml.rels
+                 data/pptx/ppt/slideLayouts/_rels/slideLayout6.xml.rels
+                 data/pptx/ppt/slideLayouts/_rels/slideLayout7.xml.rels
+                 data/pptx/ppt/slideLayouts/_rels/slideLayout8.xml.rels
+                 data/pptx/ppt/slideLayouts/_rels/slideLayout9.xml.rels
+                 data/pptx/ppt/slideLayouts/_rels/slideLayout10.xml.rels
+                 data/pptx/ppt/slideLayouts/_rels/slideLayout11.xml.rels
+                 data/pptx/ppt/slideLayouts/slideLayout1.xml
+                 data/pptx/ppt/slideLayouts/slideLayout2.xml
+                 data/pptx/ppt/slideLayouts/slideLayout3.xml
+                 data/pptx/ppt/slideLayouts/slideLayout4.xml
+                 data/pptx/ppt/slideLayouts/slideLayout5.xml
+                 data/pptx/ppt/slideLayouts/slideLayout6.xml
+                 data/pptx/ppt/slideLayouts/slideLayout7.xml
+                 data/pptx/ppt/slideLayouts/slideLayout8.xml
+                 data/pptx/ppt/slideLayouts/slideLayout9.xml
+                 data/pptx/ppt/slideLayouts/slideLayout10.xml
+                 data/pptx/ppt/slideLayouts/slideLayout11.xml
+                 data/pptx/ppt/_rels/presentation.xml.rels
+                 data/pptx/ppt/theme/theme1.xml
+                 data/pptx/ppt/presProps.xml
+                 data/pptx/ppt/slides/_rels/slide1.xml.rels
+                 data/pptx/ppt/slides/_rels/slide2.xml.rels
+                 data/pptx/ppt/slides/slide2.xml
+                 data/pptx/ppt/slides/slide1.xml
+                 data/pptx/ppt/slides/_rels/slide3.xml.rels
+                 data/pptx/ppt/slides/_rels/slide4.xml.rels
+                 data/pptx/ppt/slides/slide3.xml
+                 data/pptx/ppt/slides/slide4.xml
+                 data/pptx/ppt/viewProps.xml
+                 data/pptx/ppt/tableStyles.xml
+                 data/pptx/ppt/slideMasters/_rels/slideMaster1.xml.rels
+                 data/pptx/ppt/slideMasters/slideMaster1.xml
+                 data/pptx/ppt/presentation.xml
+                 data/pptx/ppt/notesMasters/_rels/notesMaster1.xml.rels
+                 data/pptx/ppt/notesMasters/notesMaster1.xml
+                 data/pptx/ppt/notesSlides/_rels/notesSlide1.xml.rels
+                 data/pptx/ppt/notesSlides/notesSlide1.xml
+                 data/pptx/ppt/notesSlides/_rels/notesSlide2.xml.rels
+                 data/pptx/ppt/notesSlides/notesSlide2.xml
+                 data/pptx/ppt/theme/theme2.xml
+                 data/pptx/[Content_Types].xml
+                  -- stylesheet for EPUB writer
+                 data/epub.css
+                 -- data for dzslides writer
+                 data/dzslides/template.html
+                 -- default abbreviations file
+                 data/abbreviations
+                 -- sample lua custom reader
+                 data/creole.lua
+                 -- lua init script
+                 data/init.lua
+                 -- bash completion template
+                 data/bash_completion.tpl
+                 -- citeproc
+                 data/default.csl
+                 citeproc/biblatex-localization/*.lbx.strings
+                 -- documentation
+                 MANUAL.txt, COPYRIGHT
+extra-source-files:
+                 -- documentation
+                 INSTALL.md, AUTHORS.md, README.md,
+                 CONTRIBUTING.md, BUGS, changelog.md,
+                 man/pandoc.1
+                 -- files needed to build man page
+                 man/manfilter.lua
+                 man/pandoc.1.before
+                 man/pandoc.1.after
+                 -- tests
+                 test/bodybg.gif
+                 test/*.native
+                 test/command/*.md
+                 test/command/*.csl
+                 test/command/biblio.bib
+                 test/command/averroes.bib
+                 test/command/A.txt
+                 test/command/B.txt
+                 test/command/C.txt
+                 test/command/D.txt
+                 test/command/file1.txt
+                 test/command/file2.txt
+                 test/command/three.txt
+                 test/command/01.csv
+                 test/command/chap1/spider.png
+                 test/command/chap2/spider.png
+                 test/command/chap1/text.md
+                 test/command/chap2/text.md
+                 test/command/defaults1.yaml
+                 test/command/defaults2.yaml
+                 test/command/defaults3.yaml
+                 test/command/defaults4.yaml
+                 test/command/defaults5.yaml
+                 test/command/defaults6.yaml
+                 test/command/defaults7.yaml
+                 test/command/defaults8.yaml
+                 test/command/defaults9.yaml
+                 test/command/3533-rst-csv-tables.csv
+                 test/command/3880.txt
+                 test/command/5182.txt
+                 test/command/5700-metadata-file-1.yml
+                 test/command/5700-metadata-file-2.yml
+                 test/command/abbrevs
+                 test/command/SVG_logo-without-xml-declaration.svg
+                 test/command/SVG_logo.svg
+                 test/command/corrupt.svg
+                 test/command/inkscape-cube.svg
+                 test/command/sub-file-chapter-1.tex
+                 test/command/sub-file-chapter-2.tex
+                 test/command/bar.tex
+                 test/command/bar-endinput.tex
+                 test/command/yaml-metadata.yaml
+                 test/command/7813-meta.yaml
+                 test/command/3510-subdoc.org
+                 test/command/3510-export.latex
+                 test/command/3510-src.hs
+                 test/command/3971b.tex
+                 test/command/5876.yaml
+                 test/command/5876/metadata/5876.yaml
+                 test/command/5876/metadata/command/5876.yaml
+                 test/command/6466-beg.hs
+                 test/command/6466-end.hs
+                 test/command/6466-mid.hs
+                 test/command/6466-whole.hs
+                 test/command/7861.yaml
+                 test/command/7861/metadata/placeholder
+                 test/docbook-chapter.docbook
+                 test/docbook-reader.docbook
+                 test/docbook-xref.docbook
+                 test/endnotexml-reader.xml
+                 test/html-reader.html
+                 test/opml-reader.opml
+                 test/org-select-tags.org
+                 test/haddock-reader.haddock
+                 test/insert
+                 test/lalune.jpg
+                 test/man-reader.man
+                 test/movie.jpg
+                 test/media/rId25.jpg
+                 test/media/rId26.jpg
+                 test/media/rId27.jpg
+                 test/latex-reader.latex
+                 test/textile-reader.textile
+                 test/markdown-reader-more.txt
+                 test/markdown-citations.txt
+                 test/textile-reader.textile
+                 test/mediawiki-reader.wiki
+                 test/vimwiki-reader.wiki
+                 test/creole-reader.txt
+                 test/rst-reader.rst
+                 test/jats-reader.xml
+                 test/jira-reader.jira
+                 test/s5-basic.html
+                 test/s5-fancy.html
+                 test/s5-fragment.html
+                 test/s5-inserts.html
+                 test/tables.context
+                 test/tables.docbook4
+                 test/tables.docbook5
+                 test/tables.jats_archiving
+                 test/tables.jats_articleauthoring
+                 test/tables.jats_publishing
+                 test/tables.jira
+                 test/tables.dokuwiki
+                 test/tables.zimwiki
+                 test/tables.icml
+                 test/tables.html4
+                 test/tables.html5
+                 test/tables.latex
+                 test/tables.man
+                 test/tables.ms
+                 test/tables.plain
+                 test/tables.markdown
+                 test/tables.markua
+                 test/tables.mediawiki
+                 test/tables.tei
+                 test/tables.textile
+                 test/tables.opendocument
+                 test/tables.org
+                 test/tables.asciidoc
+                 test/tables.asciidoctor
+                 test/tables.haddock
+                 test/tables.texinfo
+                 test/tables.typst
+                 test/tables.rst
+                 test/tables.rtf
+                 test/tables.txt
+                 test/tables.fb2
+                 test/tables.muse
+                 test/tables.xwiki
+                 test/tables/*.html4
+                 test/tables/*.html5
+                 test/tables/*.latex
+                 test/tables/*.native
+                 test/tables/*.mediawiki
+                 test/tables/*.jats_archiving
+                 test/testsuite.txt
+                 test/writer.latex
+                 test/writer.context
+                 test/writer.docbook4
+                 test/writer.docbook5
+                 test/writer.jats_archiving
+                 test/writer.jats_articleauthoring
+                 test/writer.jats_publishing
+                 test/writer.jira
+                 test/writer.html4
+                 test/writer.html5
+                 test/writer.man
+                 test/writer.ms
+                 test/writer.markdown
+                 test/writer.markua
+                 test/writer.plain
+                 test/writer.mediawiki
+                 test/writer.textile
+                 test/writer.typst
+                 test/writer.opendocument
+                 test/writer.org
+                 test/writer.asciidoc
+                 test/writer.asciidoctor
+                 test/writer.haddock
+                 test/writer.rst
+                 test/writer.icml
+                 test/writer.rtf
+                 test/writer.tei
+                 test/writer.texinfo
+                 test/writer.fb2
+                 test/writer.opml
+                 test/writer.dokuwiki
+                 test/writer.zimwiki
+                 test/writer.xwiki
+                 test/writer.muse
+                 test/writers-lang-and-dir.latex
+                 test/writers-lang-and-dir.context
+                 test/dokuwiki_inline_formatting.dokuwiki
+                 test/lhs-test.markdown
+                 test/lhs-test.markdown+lhs
+                 test/lhs-test.rst
+                 test/lhs-test.rst+lhs
+                 test/lhs-test.latex
+                 test/lhs-test.latex+lhs
+                 test/lhs-test.html
+                 test/lhs-test.html+lhs
+                 test/lhs-test.fragment.html+lhs
+                 test/pipe-tables.txt
+                 test/dokuwiki_external_images.dokuwiki
+                 test/dokuwiki_multiblock_table.dokuwiki
+                 test/fb2/*.markdown
+                 test/fb2/*.fb2
+                 test/fb2/images-embedded.html
+                 test/fb2/images-embedded.fb2
+                 test/fb2/test-small.png
+                 test/fb2/reader/*.fb2
+                 test/fb2/reader/*.native
+                 test/fb2/test.jpg
+                 test/docx/*.docx
+                 test/docx/golden/*.docx
+                 test/docx/*.native
+                 test/epub/*.epub
+                 test/epub/*.native
+                 test/rtf/*.native
+                 test/rtf/*.rtf
+                 test/pptx/*.pptx
+                 test/pptx/**/*.pptx
+                 test/pptx/**/*.native
+                 test/ipynb/*.native
+                 test/ipynb/*.in.native
+                 test/ipynb/*.out.native
+                 test/ipynb/*.ipynb
+                 test/ipynb/*.out.ipynb
+                 test/ipynb/*.out.html
+                 test/txt2tags.t2t
+                 test/twiki-reader.twiki
+                 test/tikiwiki-reader.tikiwiki
+                 test/odt/odt/*.odt
+                 test/odt/markdown/*.md
+                 test/odt/native/*.native
+source-repository head
+  type:          git
+  location:      git://github.com/jgm/pandoc.git
+
+flag embed_data_files
+  Description:   Embed data files in binary for relocatable executable.
+  Default:       False
+
+common common-options
+  default-language: Haskell2010
+  build-depends:    base         >= 4.12 && < 5
+  ghc-options:      -Wall -fno-warn-unused-do-bind
+                    -Wincomplete-record-updates
+                    -Wnoncanonical-monad-instances
+                    -Wcpp-undef
+                    -Wincomplete-uni-patterns
+                    -Widentities
+                    -Wpartial-fields
+                    -Wmissing-signatures
+                    -fhide-source-paths
+                    -- -Wmissing-export-lists
+
+  if impl(ghc >= 8.10)
+    ghc-options:    -Wunused-packages
+
+  if impl(ghc >= 9.0)
+    ghc-options:    -Winvalid-haddock
+
+  if os(windows)
+    cpp-options:      -D_WINDOWS
+
+common common-executable
+  import:           common-options
+  build-depends:    pandoc
+  ghc-options:      -rtsopts -with-rtsopts=-A8m -threaded
+
+library xml-light
+  import:        common-options
+  build-depends: xml                   >= 1.3.12   && < 1.4,
+                 xml-conduit           >= 1.9.1.1  && < 1.10,
+                 xml-types             >= 0.3      && < 0.4,
+                 containers            >= 0.6.0.1  && < 0.7,
+                 text                  >= 1.1.1.0  && < 2.1
+
+  hs-source-dirs:  xml-light
+  exposed-modules: Text.Pandoc.XML.Light,
+                   Text.Pandoc.XML.Light.Types,
+                   Text.Pandoc.XML.Light.Proc,
+                   Text.Pandoc.XML.Light.Output
+
+library
+  import:        common-options
+  build-depends: xml-light,
+                 Glob                  >= 0.7      && < 0.11,
+                 JuicyPixels           >= 3.1.6.1  && < 3.4,
+                 SHA                   >= 1.6      && < 1.7,
+                 aeson                 >= 2.0.1.0  && < 2.2,
+                 aeson-pretty          >= 0.8.9    && < 0.9,
+                 array                 >= 0.5      && < 0.6,
+                 attoparsec            >= 0.12     && < 0.15,
+                 binary                >= 0.7      && < 0.11,
+                 blaze-html            >= 0.9      && < 0.10,
+                 blaze-markup          >= 0.8      && < 0.9,
+                 bytestring            >= 0.9      && < 0.12,
+                 case-insensitive      >= 1.2      && < 1.3,
+                 citeproc              >= 0.8.1    && < 0.9,
+                 commonmark            >= 0.2.2    && < 0.3,
+                 commonmark-extensions >= 0.2.3.4  && < 0.3,
+                 commonmark-pandoc     >= 0.2.1.3  && < 0.3,
+                 containers            >= 0.6.0.1  && < 0.7,
+                 crypton-connection    >= 0.3.1    && < 0.4,
+                 data-default          >= 0.4      && < 0.8,
+                 deepseq               >= 1.3      && < 1.5,
+                 directory             >= 1.2.3    && < 1.4,
+                 doclayout             >= 0.4.0.1  && < 0.5,
+                 doctemplates          >= 0.11     && < 0.12,
+                 base64                >= 0.4      && < 0.5,
+                 emojis                >= 0.1      && < 0.2,
+                 exceptions            >= 0.8      && < 0.11,
+                 file-embed            >= 0.0      && < 0.1,
+                 filepath              >= 1.1      && < 1.5,
+                 gridtables            >= 0.1      && < 0.2,
+                 haddock-library       >= 1.10     && < 1.12,
+                 http-client           >= 0.4.30   && < 0.8,
+                 http-client-tls       >= 0.2.4    && < 0.4,
+                 http-types            >= 0.8      && < 0.13,
+                 ipynb                 >= 0.2      && < 0.3,
+                 jira-wiki-markup      >= 1.5.1    && < 1.6,
+                 mime-types            >= 0.1.1    && < 0.2,
+                 mtl                   >= 2.2      && < 2.4,
+                 network               >= 2.6      && < 3.2,
+                 network-uri           >= 2.6      && < 2.8,
+                 pandoc-types          >= 1.23     && < 1.24,
+                 parsec                >= 3.1      && < 3.2,
+                 pretty                >= 1.1      && < 1.2,
+                 pretty-show           >= 1.10     && < 1.11,
+                 process               >= 1.2.3    && < 1.7,
+                 random                >= 1        && < 1.3,
+                 safe                  >= 0.3.18   && < 0.4,
+                 scientific            >= 0.3      && < 0.4,
+                 skylighting           >= 0.13.3   && < 0.14,
+                 skylighting-core      >= 0.13.3   && < 0.14,
+                 split                 >= 0.2      && < 0.3,
+                 syb                   >= 0.1      && < 0.8,
+                 tagsoup               >= 0.14.6   && < 0.15,
+                 temporary             >= 1.1      && < 1.4,
+                 texmath               >= 0.12.8   && < 0.13,
+                 text                  >= 1.1.1.0  && < 2.1,
+                 text-conversions      >= 0.3      && < 0.4,
+                 time                  >= 1.5      && < 1.14,
+                 unicode-collation     >= 0.1.1    && < 0.2,
+                 unicode-transforms    >= 0.3      && < 0.5,
+                 yaml                  >= 0.11     && < 0.12,
+                 zip-archive           >= 0.4.3    && < 0.5,
+                 zlib                  >= 0.5      && < 0.7,
+                 xml                   >= 1.3.12   && < 1.4,
+                 typst                 >= 0.1      && < 0.2,
+                 vector                >= 0.12     && < 0.14
+
+  if !os(windows)
+    build-depends:  unix >= 2.4 && < 2.9
+  if flag(embed_data_files)
+     cpp-options:   -DEMBED_DATA_FILES
+     other-modules: Text.Pandoc.Data.BakedIn
+  hs-source-dirs:  src
+
+  exposed-modules: Text.Pandoc,
+                   Text.Pandoc.App,
+                   Text.Pandoc.Data,
+                   Text.Pandoc.Options,
+                   Text.Pandoc.Extensions,
+                   Text.Pandoc.Format,
+                   Text.Pandoc.Shared,
+                   Text.Pandoc.Sources,
+                   Text.Pandoc.MediaBag,
+                   Text.Pandoc.Error,
+                   Text.Pandoc.Filter,
+                   Text.Pandoc.Translations,
+                   Text.Pandoc.Translations.Types,
+                   Text.Pandoc.Readers,
+                   Text.Pandoc.Readers.HTML,
+                   Text.Pandoc.Readers.LaTeX,
+                   Text.Pandoc.Readers.Markdown,
+                   Text.Pandoc.Readers.CommonMark,
+                   Text.Pandoc.Readers.Creole,
+                   Text.Pandoc.Readers.BibTeX,
+                   Text.Pandoc.Readers.EndNote,
+                   Text.Pandoc.Readers.RIS,
+                   Text.Pandoc.Readers.CslJson,
+                   Text.Pandoc.Readers.MediaWiki,
+                   Text.Pandoc.Readers.Vimwiki,
+                   Text.Pandoc.Readers.RST,
+                   Text.Pandoc.Readers.Org,
+                   Text.Pandoc.Readers.DocBook,
+                   Text.Pandoc.Readers.JATS,
+                   Text.Pandoc.Readers.Jira,
+                   Text.Pandoc.Readers.OPML,
+                   Text.Pandoc.Readers.Textile,
+                   Text.Pandoc.Readers.Native,
+                   Text.Pandoc.Readers.Haddock,
+                   Text.Pandoc.Readers.TWiki,
+                   Text.Pandoc.Readers.TikiWiki,
+                   Text.Pandoc.Readers.Txt2Tags,
+                   Text.Pandoc.Readers.Docx,
+                   Text.Pandoc.Readers.ODT,
+                   Text.Pandoc.Readers.EPUB,
+                   Text.Pandoc.Readers.Muse,
+                   Text.Pandoc.Readers.Man,
+                   Text.Pandoc.Readers.FB2,
+                   Text.Pandoc.Readers.DokuWiki,
+                   Text.Pandoc.Readers.Ipynb,
+                   Text.Pandoc.Readers.CSV,
+                   Text.Pandoc.Readers.RTF,
+                   Text.Pandoc.Readers.Typst,
+                   Text.Pandoc.Writers,
+                   Text.Pandoc.Writers.Native,
+                   Text.Pandoc.Writers.DocBook,
+                   Text.Pandoc.Writers.JATS,
+                   Text.Pandoc.Writers.OPML,
+                   Text.Pandoc.Writers.HTML,
+                   Text.Pandoc.Writers.ChunkedHTML,
+                   Text.Pandoc.Writers.Ipynb,
+                   Text.Pandoc.Writers.ICML,
+                   Text.Pandoc.Writers.Jira,
+                   Text.Pandoc.Writers.LaTeX,
+                   Text.Pandoc.Writers.ConTeXt,
+                   Text.Pandoc.Writers.Typst,
+                   Text.Pandoc.Writers.OpenDocument,
+                   Text.Pandoc.Writers.Texinfo,
+                   Text.Pandoc.Writers.Man,
+                   Text.Pandoc.Writers.Ms,
+                   Text.Pandoc.Writers.Markdown,
+                   Text.Pandoc.Writers.CommonMark,
+                   Text.Pandoc.Writers.Haddock,
+                   Text.Pandoc.Writers.RST,
+                   Text.Pandoc.Writers.Org,
+                   Text.Pandoc.Writers.AsciiDoc,
+                   Text.Pandoc.Writers.Textile,
+                   Text.Pandoc.Writers.MediaWiki,
+                   Text.Pandoc.Writers.DokuWiki,
+                   Text.Pandoc.Writers.XWiki,
+                   Text.Pandoc.Writers.ZimWiki,
+                   Text.Pandoc.Writers.RTF,
+                   Text.Pandoc.Writers.ODT,
+                   Text.Pandoc.Writers.Docx,
+                   Text.Pandoc.Writers.Powerpoint,
+                   Text.Pandoc.Writers.EPUB,
+                   Text.Pandoc.Writers.FB2,
+                   Text.Pandoc.Writers.TEI,
+                   Text.Pandoc.Writers.Muse,
+                   Text.Pandoc.Writers.CslJson,
+                   Text.Pandoc.Writers.Math,
+                   Text.Pandoc.Writers.Shared,
+                   Text.Pandoc.Writers.OOXML,
+                   Text.Pandoc.Writers.AnnotatedTable,
+                   Text.Pandoc.Writers.BibTeX,
+                   Text.Pandoc.PDF,
+                   Text.Pandoc.UTF8,
+                   Text.Pandoc.Scripting,
+                   Text.Pandoc.Slides,
+                   Text.Pandoc.Templates,
+                   Text.Pandoc.XML,
+                   Text.Pandoc.SelfContained,
+                   Text.Pandoc.Highlighting,
+                   Text.Pandoc.Logging,
+                   Text.Pandoc.Process,
+                   Text.Pandoc.MIME,
+                   Text.Pandoc.Parsing,
+                   Text.Pandoc.Asciify,
+                   Text.Pandoc.Emoji,
+                   Text.Pandoc.ImageSize,
+                   Text.Pandoc.Class,
+                   Text.Pandoc.Class.IO,
+                   Text.Pandoc.Citeproc,
+                   Text.Pandoc.Chunks,
+                   Text.Pandoc.Version
+  other-modules:   Text.Pandoc.App.CommandLineOptions,
+                   Text.Pandoc.App.Input,
+                   Text.Pandoc.App.Opt,
+                   Text.Pandoc.App.OutputSettings,
+                   Text.Pandoc.Class.CommonState,
+                   Text.Pandoc.Class.PandocMonad,
+                   Text.Pandoc.Class.PandocIO,
+                   Text.Pandoc.Class.PandocPure,
+                   Text.Pandoc.Class.Sandbox,
+                   Text.Pandoc.Filter.Environment,
+                   Text.Pandoc.Filter.JSON,
+                   Text.Pandoc.Parsing.Capabilities,
+                   Text.Pandoc.Parsing.Citations,
+                   Text.Pandoc.Parsing.General,
+                   Text.Pandoc.Parsing.GridTable,
+                   Text.Pandoc.Parsing.Lists,
+                   Text.Pandoc.Parsing.Math,
+                   Text.Pandoc.Parsing.Smart,
+                   Text.Pandoc.Parsing.State,
+                   Text.Pandoc.Parsing.Future,
+                   Text.Pandoc.Readers.Docx.Lists,
+                   Text.Pandoc.Readers.Docx.Combine,
+                   Text.Pandoc.Readers.Docx.Parse,
+                   Text.Pandoc.Readers.Docx.Parse.Styles,
+                   Text.Pandoc.Readers.Docx.Util,
+                   Text.Pandoc.Readers.Docx.Fields,
+                   Text.Pandoc.Readers.HTML.Parsing,
+                   Text.Pandoc.Readers.HTML.Table,
+                   Text.Pandoc.Readers.HTML.TagCategories,
+                   Text.Pandoc.Readers.HTML.Types,
+                   Text.Pandoc.Readers.LaTeX.Inline,
+                   Text.Pandoc.Readers.LaTeX.Citation,
+                   Text.Pandoc.Readers.LaTeX.Lang,
+                   Text.Pandoc.Readers.LaTeX.Macro,
+                   Text.Pandoc.Readers.LaTeX.Math,
+                   Text.Pandoc.Readers.LaTeX.Parsing,
+                   Text.Pandoc.Readers.LaTeX.SIunitx,
+                   Text.Pandoc.Readers.LaTeX.Table,
+                   Text.Pandoc.Readers.Typst.Parsing,
+                   Text.Pandoc.Readers.Typst.Math,
+                   Text.Pandoc.Readers.ODT.Base,
+                   Text.Pandoc.Readers.ODT.Namespaces,
+                   Text.Pandoc.Readers.ODT.StyleReader,
+                   Text.Pandoc.Readers.ODT.ContentReader,
+                   Text.Pandoc.Readers.ODT.Generic.Fallible,
+                   Text.Pandoc.Readers.ODT.Generic.SetMap,
+                   Text.Pandoc.Readers.ODT.Generic.Utils,
+                   Text.Pandoc.Readers.ODT.Generic.Namespaces,
+                   Text.Pandoc.Readers.ODT.Generic.XMLConverter,
+                   Text.Pandoc.Readers.ODT.Arrows.State,
+                   Text.Pandoc.Readers.ODT.Arrows.Utils,
+                   Text.Pandoc.Readers.Org.BlockStarts,
+                   Text.Pandoc.Readers.Org.Blocks,
+                   Text.Pandoc.Readers.Org.DocumentTree,
+                   Text.Pandoc.Readers.Org.ExportSettings,
+                   Text.Pandoc.Readers.Org.Inlines,
+                   Text.Pandoc.Readers.Org.Meta,
+                   Text.Pandoc.Readers.Org.ParserState,
+                   Text.Pandoc.Readers.Org.Parsing,
+                   Text.Pandoc.Readers.Org.Shared,
+                   Text.Pandoc.Readers.Metadata,
+                   Text.Pandoc.Readers.Roff,
+                   Text.Pandoc.Writers.Docx.StyleMap,
+                   Text.Pandoc.Writers.Docx.Table,
+                   Text.Pandoc.Writers.Docx.Types,
+                   Text.Pandoc.Writers.GridTable
+                   Text.Pandoc.Writers.JATS.References,
+                   Text.Pandoc.Writers.JATS.Table,
+                   Text.Pandoc.Writers.JATS.Types,
+                   Text.Pandoc.Writers.LaTeX.Caption,
+                   Text.Pandoc.Writers.LaTeX.Notes,
+                   Text.Pandoc.Writers.LaTeX.Table,
+                   Text.Pandoc.Writers.LaTeX.Lang,
+                   Text.Pandoc.Writers.LaTeX.Types,
+                   Text.Pandoc.Writers.LaTeX.Citation,
+                   Text.Pandoc.Writers.LaTeX.Util,
+                   Text.Pandoc.Writers.Markdown.Table,
+                   Text.Pandoc.Writers.Markdown.Types,
+                   Text.Pandoc.Writers.Markdown.Inline,
+                   Text.Pandoc.Writers.Roff,
+                   Text.Pandoc.Writers.Blaze,
+                   Text.Pandoc.Writers.Powerpoint.Presentation,
+                   Text.Pandoc.Writers.Powerpoint.Output,
+                   Text.Pandoc.TeX,
+                   Text.Pandoc.URI,
+                   Text.Pandoc.CSS,
+                   Text.Pandoc.CSV,
+                   Text.Pandoc.RoffChar,
+                   Text.Pandoc.UUID,
+                   Text.Pandoc.Image,
+                   Text.Pandoc.Citeproc.BibTeX,
+                   Text.Pandoc.Citeproc.Name,
+                   Text.Pandoc.Citeproc.CslJson,
+                   Text.Pandoc.Citeproc.Data,
+                   Text.Pandoc.Citeproc.Locator,
+                   Text.Pandoc.Citeproc.MetaValue,
+                   Text.Pandoc.Citeproc.Util,
+                   Paths_pandoc
+  autogen-modules: Paths_pandoc
+  buildable:       True
+
+test-suite test-pandoc
+  import:         common-executable
+  type:           exitcode-stdio-1.0
+  main-is:        test-pandoc.hs
+  hs-source-dirs: test
+  build-depends:  pandoc,
+                  Diff              >= 0.2     && < 0.5,
+                  Glob              >= 0.7     && < 0.11,
+                  bytestring        >= 0.9     && < 0.12,
+                  containers        >= 0.4.2.1 && < 0.7,
+                  directory         >= 1.2.3   && < 1.4,
+                  doctemplates      >= 0.11    && < 0.12,
+                  filepath          >= 1.1     && < 1.5,
+                  mtl               >= 2.2     && < 2.4,
+                  pandoc-types      >= 1.23    && < 1.24,
+                  process           >= 1.2.3   && < 1.7,
+                  tasty             >= 0.11    && < 1.5,
+                  tasty-golden      >= 2.3     && < 2.4,
+                  tasty-hunit       >= 0.9     && < 0.11,
+                  tasty-quickcheck  >= 0.8     && < 0.11,
+                  text              >= 1.1.1.0 && < 2.1,
+                  temporary         >= 1.1     && < 1.4,
+                  time              >= 1.5     && < 1.14,
+                  xml               >= 1.3.12  && < 1.4,
+                  zip-archive       >= 0.4.3   && < 0.5
+  other-modules:  Tests.Old
+                  Tests.Command
+                  Tests.Helpers
+                  Tests.Shared
+                  Tests.MediaBag
+                  Tests.Readers.LaTeX
+                  Tests.Readers.HTML
+                  Tests.Readers.JATS
+                  Tests.Readers.Jira
+                  Tests.Readers.Markdown
+                  Tests.Readers.Org
+                  Tests.Readers.Org.Block
+                  Tests.Readers.Org.Block.CodeBlock
+                  Tests.Readers.Org.Block.Figure
+                  Tests.Readers.Org.Block.Header
+                  Tests.Readers.Org.Block.List
+                  Tests.Readers.Org.Block.Table
+                  Tests.Readers.Org.Directive
+                  Tests.Readers.Org.Inline
+                  Tests.Readers.Org.Inline.Citation
+                  Tests.Readers.Org.Inline.Note
+                  Tests.Readers.Org.Inline.Smart
+                  Tests.Readers.Org.Meta
+                  Tests.Readers.Org.Shared
+                  Tests.Readers.RST
+                  Tests.Readers.RTF
+                  Tests.Readers.Docx
+                  Tests.Readers.ODT
+                  Tests.Readers.Txt2Tags
+                  Tests.Readers.EPUB
+                  Tests.Readers.Muse
+                  Tests.Readers.Creole
+                  Tests.Readers.Man
+                  Tests.Readers.FB2
+                  Tests.Readers.DokuWiki
+                  Tests.Writers.Native
+                  Tests.Writers.ConTeXt
+                  Tests.Writers.DocBook
+                  Tests.Writers.HTML
+                  Tests.Writers.JATS
+                  Tests.Writers.Jira
+                  Tests.Writers.Markdown
+                  Tests.Writers.Org
+                  Tests.Writers.Plain
+                  Tests.Writers.AsciiDoc
+                  Tests.Writers.LaTeX
+                  Tests.Writers.Docx
+                  Tests.Writers.RST
+                  Tests.Writers.TEI
+                  Tests.Writers.Markua
+                  Tests.Writers.Muse
+                  Tests.Writers.FB2
+                  Tests.Writers.Powerpoint
+                  Tests.Writers.OOXML
+                  Tests.Writers.Ms
+                  Tests.Writers.AnnotatedTable
+
+benchmark benchmark-pandoc
+  import:          common-executable
+  type:            exitcode-stdio-1.0
+  main-is:         benchmark-pandoc.hs
+  hs-source-dirs:  benchmark
+  build-depends:   bytestring,
+                   tasty-bench >= 0.2     && <= 0.4,
+                   mtl         >= 2.2     && < 2.4,
+                   text        >= 1.1.1.0 && < 2.1,
+                   deepseq
+  -- we increase heap size to avoid benchmarking garbage collection:
+  ghc-options:     -rtsopts -with-rtsopts=-A8m -threaded
diff --git a/test/fixtures/Cabal/hackage/process-1.0.0.0.cabal b/test/fixtures/Cabal/hackage/process-1.0.0.0.cabal
new file mode 100644
index 00000000..d642100f
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/process-1.0.0.0.cabal
@@ -0,0 +1,36 @@
+name:         process
+version:      1.0.0.0
+x-revision: 1
+license:      BSD3
+license-file: LICENSE
+maintainer:   libraries@haskell.org
+synopsis:     Process libraries
+description:
+    This package contains libraries for dealing with system processes.
+extra-tmp-files:
+    config.log config.status autom4te.cache
+    include/HsProcessConfig.h
+build-type:    Configure
+cabal-version: >=1.2
+
+Library {
+    exposed-modules:
+        System.Cmd
+        System.Process.Internals
+    if impl(ghc)
+        exposed-modules:
+          System.Process
+    c-sources:
+        cbits/runProcess.c
+    include-dirs: include
+    includes:
+        runProcess.h
+    install-includes:
+        runProcess.h
+        HsProcessConfig.h
+    extensions: CPP
+    build-depends: base < 4.3, directory >= 1.0 && < 1.1, filepath  >= 1.1 && < 1.2
+
+    if !os(windows)
+        build-depends: unix
+}
diff --git a/test/fixtures/Cabal/hackage/process-1.6.19.0.cabal b/test/fixtures/Cabal/hackage/process-1.6.19.0.cabal
new file mode 100644
index 00000000..859c2e9b
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/process-1.6.19.0.cabal
@@ -0,0 +1,109 @@
+name:          process
+version:       1.6.19.0
+x-revision: 1
+-- NOTE: Don't forget to update ./changelog.md
+license:       BSD3
+license-file:  LICENSE
+maintainer:    libraries@haskell.org
+bug-reports:   https://github.com/haskell/process/issues
+synopsis:      Process libraries
+category:      System
+build-type:    Configure
+cabal-version: >=1.10
+description:
+    This package contains libraries for dealing with system processes.
+    .
+    The typed-process package is a more recent take on a process API,
+    which uses this package internally. It features better binary
+    support, easier concurrency, and a more composable API. You can
+    read more about it at
+    <https://github.com/fpco/typed-process/#readme>.
+
+extra-source-files:
+    aclocal.m4
+    changelog.md
+    configure
+    configure.ac
+    include/HsProcessConfig.h.in
+    process.buildinfo
+    exes/echo.bat
+    exes/subdir/echo.bat
+    cbits/posix/common.h
+
+extra-tmp-files:
+    autom4te.cache
+    config.log
+    config.status
+    include/HsProcessConfig.h
+
+source-repository head
+    type:     git
+    location: https://github.com/haskell/process.git
+
+library
+    default-language: Haskell2010
+    other-extensions:
+        BangPatterns
+        CPP
+        InterruptibleFFI
+        RecordWildCards
+        Trustworthy
+        Safe
+
+    exposed-modules:
+        System.Cmd
+        System.Process
+        System.Process.Internals
+    other-modules: System.Process.Common
+    if os(windows)
+        c-sources:
+            cbits/win32/runProcess.c
+        other-modules: System.Process.Windows
+        build-depends: Win32 >=2.4 && < 2.15
+        -- ole32 and rpcrt4 are needed to create GUIDs for unique named pipes
+        -- for process.
+        extra-libraries: kernel32, ole32, rpcrt4
+        cpp-options: -DWINDOWS
+    else
+        if arch(javascript)
+            js-sources:
+                jsbits/process.js
+            other-modules: System.Process.JavaScript
+        else
+            c-sources:
+                cbits/posix/runProcess.c
+                cbits/posix/fork_exec.c
+                cbits/posix/posix_spawn.c
+                cbits/posix/find_executable.c
+            other-modules: System.Process.Posix
+            build-depends: unix >= 2.5 && < 2.9
+
+    include-dirs: include
+    includes:
+        runProcess.h
+    install-includes:
+        runProcess.h
+        processFlags.h
+
+    ghc-options: -Wall
+
+    build-depends: base      >= 4.10 && < 4.21,
+                   directory >= 1.1 && < 1.4,
+                   filepath  >= 1.2 && < 1.6,
+                   deepseq   >= 1.1 && < 1.6
+
+test-suite test
+  default-language: Haskell2010
+  hs-source-dirs: test
+  main-is: main.hs
+  type: exitcode-stdio-1.0
+  -- Add otherwise redundant bounds on base since GHC's build system runs
+  -- `cabal check`, which mandates bounds on base.
+  build-depends: base >= 4 && < 5
+               , bytestring
+               , directory
+               , process
+  ghc-options: -threaded
+               -with-rtsopts "-N"
+  if os(windows)
+        cpp-options: -DWINDOWS
diff --git a/test/fixtures/Cabal/hackage/process-1.6.23.0.cabal b/test/fixtures/Cabal/hackage/process-1.6.23.0.cabal
new file mode 100644
index 00000000..ced26259
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/process-1.6.23.0.cabal
@@ -0,0 +1,94 @@
+cabal-version: 2.4
+name:          process
+version:       1.6.23.0
+-- NOTE: Don't forget to update ./changelog.md
+license:       BSD-3-Clause
+license-file:  LICENSE
+maintainer:    libraries@haskell.org
+bug-reports:   https://github.com/haskell/process/issues
+synopsis:      Process libraries
+category:      System
+build-type:    Configure
+description:
+    This package contains libraries for dealing with system processes.
+    .
+    The typed-process package is a more recent take on a process API,
+    which uses this package internally. It features better binary
+    support, easier concurrency, and a more composable API. You can
+    read more about it at
+    <https://github.com/fpco/typed-process/#readme>.
+
+extra-doc-files:
+    changelog.md
+
+extra-source-files:
+    aclocal.m4
+    configure
+    configure.ac
+    include/HsProcessConfig.h.in
+    process.buildinfo
+    exes/echo.bat
+    exes/subdir/echo.bat
+    cbits/posix/common.h
+
+extra-tmp-files:
+    autom4te.cache
+    config.log
+    config.status
+    include/HsProcessConfig.h
+
+source-repository head
+    type:     git
+    location: https://github.com/haskell/process.git
+
+library
+    default-language: Haskell2010
+    other-extensions:
+        BangPatterns
+        CPP
+        InterruptibleFFI
+        RecordWildCards
+        Trustworthy
+        Safe
+
+    exposed-modules:
+        System.Cmd
+        System.Process
+        System.Process.CommunicationHandle
+        System.Process.CommunicationHandle.Internal
+        System.Process.Internals
+    other-modules: System.Process.Common
+    if os(windows)
+        c-sources:
+            cbits/win32/runProcess.c
+        other-modules: System.Process.Windows
+        build-depends: Win32 >=2.4 && < 2.15
+        -- ole32 and rpcrt4 are needed to create GUIDs for unique named pipes
+        -- for process.
+        extra-libraries: kernel32, ole32, rpcrt4
+        cpp-options: -DWINDOWS
+    else
+        if arch(javascript)
+            js-sources:
+                jsbits/process.js
+            other-modules: System.Process.JavaScript
+        else
+            c-sources:
+                cbits/posix/runProcess.c
+                cbits/posix/fork_exec.c
+                cbits/posix/posix_spawn.c
+                cbits/posix/find_executable.c
+            other-modules: System.Process.Posix
+            build-depends: unix >= 2.5 && < 2.9
+
+    include-dirs: include
+    install-includes:
+        runProcess.h
+        processFlags.h
+
+    ghc-options: -Wall
+
+    build-depends: base      >= 4.10 && < 4.21,
+                   directory >= 1.1 && < 1.4,
+                   filepath  >= 1.2 && < 1.6,
+                   deepseq   >= 1.1 && < 1.6
diff --git a/test/fixtures/Cabal/hackage/tls-extra-0.1.0.cabal b/test/fixtures/Cabal/hackage/tls-extra-0.1.0.cabal
new file mode 100644
index 00000000..5648a77d
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/tls-extra-0.1.0.cabal
@@ -0,0 +1,78 @@
+Name:                tls-extra
+Version:             0.1.0
+Description:
+   a set of extra definitions, default values and helpers for tls.
+License:             BSD3
+License-file:        LICENSE
+Copyright:           Vincent Hanquez <vincent@snarc.org>
+Author:              Vincent Hanquez <vincent@snarc.org>
+Maintainer:          Vincent Hanquez <vincent@snarc.org>
+Synopsis:            TLS extra default values and helpers
+Build-Type:          Simple
+Category:            Network
+stability:           experimental
+Cabal-Version:       >=1.6
+Homepage:            http://github.com/vincenthz/hs-tls-extra
+
+Flag test
+  Description:       Build unit test
+  Default:           False
+
+Flag bench
+  Description:       Build benchmarks
+  Default:           False
+
+Flag executable
+  Description:       Build the executable
+  Default:           False
+
+Library
+  Build-Depends:     tls >= 0.5 && < 0.6
+                   , mtl
+                   , cryptohash >= 0.6
+                   , bytestring
+                   , vector
+                   , crypto-api >= 0.5
+                   , cryptocipher >= 0.2.5
+                   , certificate >= 0.7 && < 0.8
+  Exposed-modules:   Network.TLS.Extra
+  other-modules:     Network.TLS.Extra.Certificate
+                     Network.TLS.Extra.Cipher
+                     Network.TLS.Extra.Compression
+                     Network.TLS.Extra.Thread
+  ghc-options:       -Wall
+
+Executable           stunnel
+  Main-is:           Examples/Stunnel.hs
+  if flag(executable)
+    Build-Depends:   network
+                   , cmdargs
+    Buildable:       True
+  else
+    Buildable:       False
+  ghc-options:       -Wall -fno-warn-missing-signatures
+
+Executable           checkciphers
+  Main-is:           Examples/CheckCiphers.hs
+  if flag(executable)
+    Build-Depends:   network
+                   , cmdargs
+    Buildable:       True
+  else
+    Buildable:       False
+  ghc-options:       -Wall -fno-warn-missing-signatures
+
+executable           Tests
+  Main-is:           Tests.hs
+  if flag(test)
+    Buildable:       True
+    Build-Depends:   base >= 3 && < 5
+                   , HUnit
+                   , QuickCheck >= 2
+                   , bytestring
+  else
+    Buildable:       False
+
+source-repository head
+  type: git
+  location: git://github.com/vincenthz/hs-tls-extra
diff --git a/test/fixtures/Cabal/hackage/tls-extra-0.4.6.1.cabal b/test/fixtures/Cabal/hackage/tls-extra-0.4.6.1.cabal
new file mode 100644
index 00000000..5d334645
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/tls-extra-0.4.6.1.cabal
@@ -0,0 +1,60 @@
+Name:                tls-extra
+Version:             0.4.6.1
+Description:
+   a set of extra definitions, default values and helpers for tls.
+License:             BSD3
+License-file:        LICENSE
+Copyright:           Vincent Hanquez <vincent@snarc.org>
+Author:              Vincent Hanquez <vincent@snarc.org>
+Maintainer:          Vincent Hanquez <vincent@snarc.org>
+Synopsis:            TLS extra default values and helpers
+Build-Type:          Simple
+Category:            Network
+stability:           experimental
+Cabal-Version:       >=1.6
+Homepage:            http://github.com/vincenthz/hs-tls-extra
+
+Flag test
+  Description:       Build unit test
+  Default:           False
+
+Library
+  Build-Depends:     base > 3 && < 5
+                   , tls >= 0.9.4 && < 1.0.0
+                   , mtl
+                   , network >= 2.3
+                   , cryptohash >= 0.6
+                   , bytestring
+                   , vector
+                   , crypto-api >= 0.5
+                   , cryptocipher >= 0.3.0 && < 0.4.0
+                   , certificate >= 1.2.0 && < 1.3.0
+                   , pem >= 0.1.0 && < 0.2.0
+                   , text >= 0.5 && < 1.0
+                   , time
+  Exposed-modules:   Network.TLS.Extra
+  other-modules:     Network.TLS.Extra.Certificate
+                     Network.TLS.Extra.Cipher
+                     Network.TLS.Extra.Compression
+                     Network.TLS.Extra.Connection
+                     Network.TLS.Extra.Thread
+                     Network.TLS.Extra.File
+  ghc-options:       -Wall -fno-warn-missing-signatures
+  if os(windows)
+    cpp-options:     -DNOCERTVERIFY
+
+executable           Tests
+  Main-is:           Tests.hs
+  if flag(test)
+    Buildable:       True
+    Build-Depends:   base >= 3 && < 5
+                   , HUnit
+                   , QuickCheck >= 2
+                   , bytestring
+                   , cprng-aes >= 0.2.3
+  else
+    Buildable:       False
+
+source-repository head
+  type: git
+  location: git://github.com/vincenthz/hs-tls-extra
diff --git a/test/fixtures/Cabal/hackage/toml-reader-0.1.0.0.cabal b/test/fixtures/Cabal/hackage/toml-reader-0.1.0.0.cabal
new file mode 100644
index 00000000..94af7cba
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/toml-reader-0.1.0.0.cabal
@@ -0,0 +1,113 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.34.4.
+--
+-- see: https://github.com/sol/hpack
+
+name:           toml-reader
+version:        0.1.0.0
+x-revision: 2
+synopsis:       TOML format parser compliant with v1.0.0.
+description:    TOML format parser compliant with v1.0.0. See README.md for more details.
+category:       TOML, Text, Configuration
+homepage:       https://github.com/brandonchinn178/toml-reader#readme
+bug-reports:    https://github.com/brandonchinn178/toml-reader/issues
+author:         Brandon Chinn <brandonchinn178@gmail.com>
+maintainer:     Brandon Chinn <brandonchinn178@gmail.com>
+license:        BSD3
+license-file:   LICENSE.md
+build-type:     Simple
+extra-source-files:
+    README.md
+    CHANGELOG.md
+    test/tasty/goldens/renderTOMLError/DecodeError.InvalidValue.golden
+    test/tasty/goldens/renderTOMLError/DecodeError.MissingField.golden
+    test/tasty/goldens/renderTOMLError/DecodeError.OtherDecodeError.golden
+    test/tasty/goldens/renderTOMLError/DecodeError.TypeMismatch.golden
+    test/tasty/goldens/renderTOMLError/NormalizeError.DuplicateKeyError.golden
+    test/tasty/goldens/renderTOMLError/NormalizeError.DuplicateSectionError.golden
+    test/tasty/goldens/renderTOMLError/NormalizeError.ExtendTableError.golden
+    test/tasty/goldens/renderTOMLError/NormalizeError.ExtendTableInInlineArrayError.golden
+    test/tasty/goldens/renderTOMLError/NormalizeError.ImplicitArrayForDefinedKeyError.golden
+    test/tasty/goldens/renderTOMLError/NormalizeError.NonTableInNestedImplicitArrayError.golden
+    test/tasty/goldens/renderTOMLError/NormalizeError.NonTableInNestedKeyError.golden
+    test/tasty/goldens/renderTOMLError/ParseError.golden
+
+source-repository head
+  type: git
+  location: https://github.com/brandonchinn178/toml-reader
+
+library
+  exposed-modules:
+      TOML
+      TOML.Decode
+      TOML.Error
+      TOML.Parser
+      TOML.Utils.Map
+      TOML.Utils.NonEmpty
+      TOML.Value
+  other-modules:
+      Paths_toml_reader
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  build-depends:
+      base >=4.9 && <5
+    , containers >=0.6.0.1 && <0.7
+    , deepseq >=1.4.4.0 && <1.5
+    , megaparsec >=7.0.5 && <9.3
+    , parser-combinators >=1.1.0 && <1.4
+    , text >=1.2.3.1 && <2.1
+    , time >=1.8.0.2 && <1.13
+  if impl(ghc >= 8.0)
+    ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wnoncanonical-monad-instances
+  default-language: Haskell2010
+
+test-suite parser-validator
+  type: exitcode-stdio-1.0
+  main-is: ValidateParser.hs
+  other-modules:
+      Paths_toml_reader
+  hs-source-dirs:
+      test/toml-test
+  ghc-options: -Wall
+  build-depends:
+      aeson
+    , base
+    , bytestring
+    , containers
+    , directory
+    , process
+    , text
+    , time
+    , toml-reader
+    , unordered-containers
+    , vector
+  if impl(ghc >= 8.0)
+    ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wnoncanonical-monad-instances
+  default-language: Haskell2010
+
+test-suite toml-reader-tests
+  type: exitcode-stdio-1.0
+  main-is: Main.hs
+  other-modules:
+      TOML.DecodeTest
+      TOML.ErrorTest
+      TOML.Utils.MapTest
+      TOML.Utils.NonEmptyTest
+      Paths_toml_reader
+  hs-source-dirs:
+      test/tasty
+  ghc-options: -Wall
+  build-depends:
+      base
+    , containers
+    , tasty
+    , tasty-golden
+    , tasty-hunit
+    , text
+    , time
+    , toml-reader
+  if impl(ghc >= 8.0)
+    ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wnoncanonical-monad-instances
+  default-language: Haskell2010
diff --git a/test/fixtures/Cabal/hackage/toml-reader-0.2.0.0.cabal b/test/fixtures/Cabal/hackage/toml-reader-0.2.0.0.cabal
new file mode 100644
index 00000000..a06b9fa2
--- /dev/null
+++ b/test/fixtures/Cabal/hackage/toml-reader-0.2.0.0.cabal
@@ -0,0 +1,113 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.35.0.
+--
+-- see: https://github.com/sol/hpack
+
+name:           toml-reader
+version:        0.2.0.0
+x-revision: 2
+synopsis:       TOML format parser compliant with v1.0.0.
+description:    TOML format parser compliant with v1.0.0. See README.md for more details.
+category:       TOML, Text, Configuration
+homepage:       https://github.com/brandonchinn178/toml-reader#readme
+bug-reports:    https://github.com/brandonchinn178/toml-reader/issues
+author:         Brandon Chinn <brandonchinn178@gmail.com>
+maintainer:     Brandon Chinn <brandonchinn178@gmail.com>
+license:        BSD3
+license-file:   LICENSE.md
+build-type:     Simple
+extra-source-files:
+    README.md
+    CHANGELOG.md
+    test/tasty/goldens/renderTOMLError/DecodeError.InvalidValue.golden
+    test/tasty/goldens/renderTOMLError/DecodeError.MissingField.golden
+    test/tasty/goldens/renderTOMLError/DecodeError.OtherDecodeError.golden
+    test/tasty/goldens/renderTOMLError/DecodeError.TypeMismatch.golden
+    test/tasty/goldens/renderTOMLError/NormalizeError.DuplicateKeyError.golden
+    test/tasty/goldens/renderTOMLError/NormalizeError.DuplicateSectionError.golden
+    test/tasty/goldens/renderTOMLError/NormalizeError.ExtendTableError.golden
+    test/tasty/goldens/renderTOMLError/NormalizeError.ExtendTableInInlineArrayError.golden
+    test/tasty/goldens/renderTOMLError/NormalizeError.ImplicitArrayForDefinedKeyError.golden
+    test/tasty/goldens/renderTOMLError/NormalizeError.NonTableInNestedImplicitArrayError.golden
+    test/tasty/goldens/renderTOMLError/NormalizeError.NonTableInNestedKeyError.golden
+    test/tasty/goldens/renderTOMLError/ParseError.golden
+
+source-repository head
+  type: git
+  location: https://github.com/brandonchinn178/toml-reader
+
+library
+  exposed-modules:
+      TOML
+      TOML.Decode
+      TOML.Error
+      TOML.Parser
+      TOML.Utils.Map
+      TOML.Utils.NonEmpty
+      TOML.Value
+  other-modules:
+      Paths_toml_reader
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  build-depends:
+      base >=4.9 && <5
+    , containers >=0.6.0.1 && <0.7
+    , megaparsec >=7.0.5 && <9.5
+    , parser-combinators >=1.1.0 && <1.4
+    , text >=1.2.3.1 && <2.1
+    , time >=1.8.0.2 && <1.13
+  default-language: Haskell2010
+  if impl(ghc >= 8.0)
+    ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wnoncanonical-monad-instances
+
+test-suite parser-validator
+  type: exitcode-stdio-1.0
+  main-is: ValidateParser.hs
+  other-modules:
+      Paths_toml_reader
+  hs-source-dirs:
+      test/toml-test
+  ghc-options: -Wall
+  build-depends:
+      aeson
+    , base
+    , bytestring
+    , containers
+    , directory
+    , process
+    , text
+    , time
+    , toml-reader
+    , unordered-containers
+    , vector
+  default-language: Haskell2010
+  if impl(ghc >= 8.0)
+    ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wnoncanonical-monad-instances
+
+test-suite toml-reader-tests
+  type: exitcode-stdio-1.0
+  main-is: Main.hs
+  other-modules:
+      TOML.DecodeTest
+      TOML.ErrorTest
+      TOML.ParserTest
+      TOML.Utils.MapTest
+      TOML.Utils.NonEmptyTest
+      Paths_toml_reader
+  hs-source-dirs:
+      test/tasty
+  ghc-options: -Wall
+  build-depends:
+      base
+    , containers
+    , tasty
+    , tasty-golden
+    , tasty-hunit
+    , text
+    , time
+    , toml-reader
+  default-language: Haskell2010
+  if impl(ghc >= 8.0)
+    ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wnoncanonical-monad-instances

From 551ffa95bc8cf64b345135f36ea366090bbfcf3e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9ophile=20Choutri=20de=20Tarl=C3=A9?=
 <theophile@choutri.eu>
Date: Sat, 16 Nov 2024 23:52:33 +0100
Subject: [PATCH 2/2] Store affected version ranges by version instead of
 release ID (#791)

This allows for greater flexiblity when it comes to declared versions
that do not appear in the affected package repository
---
 ...4081932_create_affected_version_ranges.sql |  8 +++----
 ...116223018_add_index_on_release_version.sql |  1 +
 src/advisories/Advisories/Import.hs           | 24 ++++---------------
 .../Advisories/Model/Affected/Types.hs        |  7 +++---
 4 files changed, 12 insertions(+), 28 deletions(-)
 create mode 100644 migrations/20241116223018_add_index_on_release_version.sql

diff --git a/migrations/20241014081932_create_affected_version_ranges.sql b/migrations/20241014081932_create_affected_version_ranges.sql
index 90f08086..8fa6b489 100644
--- a/migrations/20241014081932_create_affected_version_ranges.sql
+++ b/migrations/20241014081932_create_affected_version_ranges.sql
@@ -1,15 +1,15 @@
 CREATE TABLE IF NOT EXISTS affected_version_ranges (
     affected_version_id uuid PRIMARY KEY
   , affected_package_id uuid REFERENCES affected_packages NOT NULL
-  , introduced_version uuid REFERENCES releases (release_id) NOT NULL
-  , fixed_version uuid REFERENCES releases (release_id)
+  , introduced_version int[] NOT NULL
+  , fixed_version int[]
 );
 
 CREATE INDEX affected_version_ranges_affected_package_id_fkey
   ON affected_version_ranges (affected_package_id);
 
-CREATE INDEX affected_version_ranges_introduced_version_fkey
+CREATE INDEX affected_version_ranges_introduced_version
   ON affected_version_ranges (introduced_version);
 
-CREATE INDEX affected_version_ranges_fixed_version_fkey
+CREATE INDEX affected_version_ranges_fixed_version
   ON affected_version_ranges (fixed_version);
diff --git a/migrations/20241116223018_add_index_on_release_version.sql b/migrations/20241116223018_add_index_on_release_version.sql
new file mode 100644
index 00000000..b53c3c51
--- /dev/null
+++ b/migrations/20241116223018_add_index_on_release_version.sql
@@ -0,0 +1 @@
+CREATE INDEX ON releases (version);
diff --git a/src/advisories/Advisories/Import.hs b/src/advisories/Advisories/Import.hs
index 11ec54ce..cd254102 100644
--- a/src/advisories/Advisories/Import.hs
+++ b/src/advisories/Advisories/Import.hs
@@ -25,8 +25,6 @@ import Advisories.Model.Affected.Update qualified as Update
 import Flora.Import.Package
 import Flora.Model.Package.Guard (guardThatPackageExists)
 import Flora.Model.Package.Types
-import Flora.Model.Release.Guard (guardThatReleaseExists)
-import Flora.Model.Release.Types
 import OSV.Reference.Orphans
 
 -- | List deduplicated parsed Advisories
@@ -131,40 +129,26 @@ processAffectedPackage advisoryId affected = do
           , declarations = declarations
           }
   Update.insertAffectedPackage affectedPackageDAO
-  processAffectedVersionRanges affectedPackageId package.packageId affected.affectedVersions
+  processAffectedVersionRanges affectedPackageId affected.affectedVersions
 
 processAffectedVersionRanges
   :: ( IOE :> es
      , DB :> es
-     , Trace :> es
-     , Error (NonEmpty AdvisoryImportError) :> es
      )
   => AffectedPackageId
-  -> PackageId
   -> [AffectedVersionRange]
   -> Eff es ()
-processAffectedVersionRanges affectedPackageId packageId affectedVersions = do
+processAffectedVersionRanges affectedPackageId affectedVersions = do
   traverse_
     ( \affectedVersion -> do
         affectedVersionId <- AffectedVersionId <$> liftIO UUID.nextRandom
-        introducedReleaseId <- do
-          release <- guardThatReleaseExists packageId affectedVersion.affectedVersionRangeIntroduced $ \version ->
-            throwError (NonEmpty.singleton $ AffectedVersionNotFound packageId version)
-          pure release.releaseId
-        mFixedReleaseId <- case affectedVersion.affectedVersionRangeFixed of
-          Nothing -> pure Nothing
-          Just version -> do
-            release <- guardThatReleaseExists packageId version $ \releaseVersion ->
-              throwError (NonEmpty.singleton $ AffectedVersionNotFound packageId releaseVersion)
-            pure $ Just release.releaseId
         let versionRangeDAO =
               AffectedVersionRangeDAO
                 { affectedVersionId = affectedVersionId
                 , affectedPackageId = affectedPackageId
-                , introducedVersion = introducedReleaseId
-                , fixedVersion = mFixedReleaseId
+                , introducedVersion = affectedVersion.affectedVersionRangeIntroduced
+                , fixedVersion = affectedVersion.affectedVersionRangeFixed
                 }
-
         Update.insertAffectedVersionRange versionRangeDAO
     )
     affectedVersions
diff --git a/src/advisories/Advisories/Model/Affected/Types.hs b/src/advisories/Advisories/Model/Affected/Types.hs
index 7350a715..30dbe316 100644
--- a/src/advisories/Advisories/Model/Affected/Types.hs
+++ b/src/advisories/Advisories/Model/Affected/Types.hs
@@ -10,7 +10,7 @@ import Database.PostgreSQL.Simple (FromRow, ToRow)
 import Database.PostgreSQL.Simple.FromField
 import Database.PostgreSQL.Simple.Newtypes
 import Database.PostgreSQL.Simple.ToField
-import Distribution.Types.VersionRange (VersionRange)
+import Distribution.Version
 import GHC.Generics
 import Security.Advisories.Core.Advisory
 import Security.CVSS (CVSS)
@@ -22,7 +22,6 @@ import Advisories.System.Orphans ()
 import Distribution.Orphans.ConfVar ()
 import Distribution.Orphans.Version ()
 import Flora.Model.Package.Types
-import Flora.Model.Release.Types
 
 newtype AffectedPackageId = AffectedPackageId {getAffectedPackageId :: UUID}
   deriving stock (Generic, Show)
@@ -65,8 +64,8 @@ newtype AffectedVersionId = AffectedVersionId {getAffectedVersionId :: UUID}
 data AffectedVersionRangeDAO = AffectedVersionRangeDAO
   { affectedVersionId :: AffectedVersionId
   , affectedPackageId :: AffectedPackageId
-  , introducedVersion :: ReleaseId
-  , fixedVersion :: Maybe ReleaseId
+  , introducedVersion :: Version
+  , fixedVersion :: Maybe Version
   }
   deriving stock (Show, Generic)
   deriving anyclass (FromRow, ToRow, NFData)