diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Changelog for `sarif`
+
+## 0.1 - 22 December 2022
+
+* First release with an initial implementation of some key aspects of the SARIF specification
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Michael B. Gale
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# SARIF implementation for Haskell
+
+This Haskell library provides types and JSON instances for the [Static Analysis Results Interchange Format (SARIF)](https://sarifweb.azurewebsites.net). Static analysis tools written in Haskell may use this library to export their results which can then be consumed by e.g. [GitHub Code Scanning](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning).
diff --git a/sarif.cabal b/sarif.cabal
new file mode 100644
--- /dev/null
+++ b/sarif.cabal
@@ -0,0 +1,58 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.35.1.
+--
+-- see: https://github.com/sol/hpack
+
+name:           sarif
+version:        0.1
+synopsis:       SARIF implementation for Haskell
+description:    Provides types and JSON instances for the Static Analysis Results Interchange Format (SARIF).
+category:       Data
+homepage:       https://github.com/mbg/sarif#readme
+bug-reports:    https://github.com/mbg/sarif/issues
+author:         Michael B. Gale
+maintainer:     github@michael-gale.co.uk
+copyright:      Copyright (c) Michael B. Gale
+license:        MIT
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    README.md
+    CHANGELOG.md
+
+source-repository head
+  type: git
+  location: https://github.com/mbg/sarif
+
+library
+  exposed-modules:
+      Data.Aeson.Optional
+      Data.SARIF
+      Data.SARIF.Artifact
+      Data.SARIF.Level
+      Data.SARIF.Location
+      Data.SARIF.Log
+      Data.SARIF.MultiformatMessageString
+      Data.SARIF.ReportingDescriptor
+      Data.SARIF.Result
+      Data.SARIF.Run
+      Data.SARIF.Tool
+  other-modules:
+      Paths_sarif
+  hs-source-dirs:
+      src
+  default-extensions:
+      DeriveAnyClass
+      DeriveGeneric
+      OverloadedStrings
+      RecordWildCards
+  ghc-options: -Wall
+  build-depends:
+      aeson
+    , base >=4.8 && <5
+    , bytestring
+    , containers
+    , text
+    , uuid-types
+  default-language: Haskell2010
diff --git a/src/Data/Aeson/Optional.hs b/src/Data/Aeson/Optional.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Aeson/Optional.hs
@@ -0,0 +1,56 @@
+--------------------------------------------------------------------------------
+-- SARIF implementation for Haskell
+--------------------------------------------------------------------------------
+-- This source code is licensed under the MIT license found in the LICENSE    --
+-- file in the root directory of this source tree.                            --
+--------------------------------------------------------------------------------
+
+{-# LANGUAGE CPP #-}
+
+-- | Provides extensions to "Data.Aeson" for constructing JSON objects which
+-- omit fields that are @null@.
+module Data.Aeson.Optional (
+    module Aeson,
+    object,
+    (.=),
+    (.=?)
+) where
+
+--------------------------------------------------------------------------------
+
+import Data.Aeson as Aeson hiding (object, (.=))
+import Data.Aeson.Types (Pair)
+import qualified Data.Aeson as JSON (object)
+import Data.Maybe
+#if !MIN_VERSION_aeson(2,0,0)
+import Data.Text
+#endif
+
+--------------------------------------------------------------------------------
+
+-- | `object` @pairList@ constructs a JSON objects from @pairList@, omitting
+-- all `Nothing` values.
+object :: [Maybe Pair] -> Value
+object = JSON.object . catMaybes
+
+infixr 8 .=?, .=
+-- | Construct an optional field from an optional value. If the value is
+-- `Nothing`, then the field will be omitted from the JSON object.
+#if MIN_VERSION_aeson(2,0,0)
+(.=?) :: ToJSON a => Key -> Maybe a -> Maybe Pair
+#else
+(.=?) :: ToJSON a => Text -> Maybe a -> Maybe Pair
+#endif
+_ .=? Nothing = Nothing
+key .=? Just val = Just (key, toJSON val)
+
+-- | Construct a mandatory field from a value. The resulting field will always
+-- be present in the resulting JSON object.
+#if MIN_VERSION_aeson(2,0,0)
+(.=) :: ToJSON a => Key -> a -> Maybe Pair
+#else
+(.=) :: ToJSON a => Text -> a -> Maybe Pair
+#endif
+key .= val = Just (key, toJSON val)
+
+--------------------------------------------------------------------------------
diff --git a/src/Data/SARIF.hs b/src/Data/SARIF.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/SARIF.hs
@@ -0,0 +1,26 @@
+--------------------------------------------------------------------------------
+-- SARIF implementation for Haskell
+--------------------------------------------------------------------------------
+-- This source code is licensed under the MIT license found in the LICENSE    --
+-- file in the root directory of this source tree.                            --
+--------------------------------------------------------------------------------
+
+-- | Re-exports all modules from this library. You probably want to import
+-- this module as qualified.
+module Data.SARIF (
+    module SARIF
+) where
+
+--------------------------------------------------------------------------------
+
+import Data.SARIF.Artifact as SARIF
+import Data.SARIF.Level as SARIF
+import Data.SARIF.Location as SARIF
+import Data.SARIF.Log as SARIF
+import Data.SARIF.MultiformatMessageString as SARIF
+import Data.SARIF.ReportingDescriptor as SARIF
+import Data.SARIF.Result as SARIF
+import Data.SARIF.Run as SARIF
+import Data.SARIF.Tool as SARIF
+
+--------------------------------------------------------------------------------
diff --git a/src/Data/SARIF/Artifact.hs b/src/Data/SARIF/Artifact.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/SARIF/Artifact.hs
@@ -0,0 +1,41 @@
+--------------------------------------------------------------------------------
+-- SARIF implementation for Haskell
+--------------------------------------------------------------------------------
+-- This source code is licensed under the MIT license found in the LICENSE    --
+-- file in the root directory of this source tree.                            --
+--------------------------------------------------------------------------------
+
+-- | Provides the `Artifact` type to represent SARIF artifacts.
+module Data.SARIF.Artifact (
+    Artifact(..)
+) where
+
+--------------------------------------------------------------------------------
+
+import Data.Aeson.Optional
+import Data.Text
+
+import Data.SARIF.Location
+
+--------------------------------------------------------------------------------
+
+-- | An artifact represents e.g. a source file.
+data Artifact = MkArtifact {
+    -- | The location where the artifact was found.
+    artifactLocation :: ArtifactLocation,
+    -- | The mime type of the artifact.
+    artifactMimeType :: Maybe Text
+} deriving (Eq, Show)
+
+instance ToJSON Artifact where
+    toJSON MkArtifact{..} = object
+        [ "location" .= artifactLocation
+        , "mimeType" .=? artifactMimeType
+        ]
+
+instance FromJSON Artifact where
+    parseJSON = withObject "Artifact" $ \obj ->
+        MkArtifact <$> obj .: "location"
+                   <*> obj .:? "mimeType"
+
+--------------------------------------------------------------------------------
diff --git a/src/Data/SARIF/Level.hs b/src/Data/SARIF/Level.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/SARIF/Level.hs
@@ -0,0 +1,45 @@
+--------------------------------------------------------------------------------
+-- SARIF implementation for Haskell
+--------------------------------------------------------------------------------
+-- This source code is licensed under the MIT license found in the LICENSE    --
+-- file in the root directory of this source tree.                            --
+--------------------------------------------------------------------------------
+
+-- | Provides the `Level` type, which enumerates SARIF result levels.
+-- See https://docs.oasis-open.org/sarif/sarif/v2.1.0/cs01/sarif-v2.1.0-cs01.html#_Ref493511208
+module Data.SARIF.Level (
+    Level(..)
+) where
+
+--------------------------------------------------------------------------------
+
+import Data.Aeson.Optional hiding (Error)
+
+--------------------------------------------------------------------------------
+
+-- | A `Level` value represents the severity of a result.
+data Level
+    -- | The concept of “severity” does not apply.
+    = None
+    -- | A minor problem or an opportunity to improve the code was found.
+    | Note
+    -- | A problem was found.
+    | Warning
+    -- | A serious problem was found.
+    | Error
+    deriving (Eq, Show)
+
+instance ToJSON Level where
+    toJSON None = "none"
+    toJSON Note = "note"
+    toJSON Warning = "warning"
+    toJSON Error = "error"
+
+instance FromJSON Level where
+    parseJSON (String "none") = pure None
+    parseJSON (String "note") = pure Note
+    parseJSON (String "warning") = pure Warning
+    parseJSON (String "error") = pure Error
+    parseJSON _ = fail "Unexpected value for result level"
+
+--------------------------------------------------------------------------------
diff --git a/src/Data/SARIF/Location.hs b/src/Data/SARIF/Location.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/SARIF/Location.hs
@@ -0,0 +1,98 @@
+--------------------------------------------------------------------------------
+-- SARIF implementation for Haskell
+--------------------------------------------------------------------------------
+-- This source code is licensed under the MIT license found in the LICENSE    --
+-- file in the root directory of this source tree.                            --
+--------------------------------------------------------------------------------
+
+-- | Provides types used to refer to different sorts of locations, such as
+-- files, as well as parts of files.
+module Data.SARIF.Location (
+    Location(..),
+    ArtifactLocation(..),
+    Region(..),
+    PhysicalLocation(..)
+) where
+
+--------------------------------------------------------------------------------
+
+import Data.Aeson
+import Data.Text
+
+--------------------------------------------------------------------------------
+
+-- | Represents locations.
+newtype Location = MkLocation {
+    -- | The physical location (i.e. part of a file) to which this location
+    -- refers to.
+    locationPhysicalLocation :: Maybe PhysicalLocation
+} deriving (Eq, Show)
+
+instance ToJSON Location where
+    toJSON MkLocation{..} = object
+        [ "physicalLocation" .= locationPhysicalLocation
+        ]
+
+instance FromJSON Location where
+    parseJSON = withObject "Location" $ \obj ->
+        MkLocation <$> obj .:? "physicalLocation"
+
+-- | Represents artifact locations such as file paths.
+newtype ArtifactLocation = MkArtifactLocation {
+    artifactLocationUri :: Text
+} deriving (Eq, Show)
+
+instance ToJSON ArtifactLocation where
+    toJSON MkArtifactLocation{..} = object
+        [ "uri" .= artifactLocationUri
+        ]
+
+instance FromJSON ArtifactLocation where
+    parseJSON = withObject "ArtifactLocation" $ \obj ->
+        MkArtifactLocation <$> obj .: "uri"
+
+-- | Represents regions of code with a start and an end.
+data Region = MkRegion {
+    -- | The line on which this region starts.
+    regionStartLine :: Int,
+    -- | The column within the starting line where this region starts.
+    regionStartColumn :: Int,
+    -- | The line on which this region ends.
+    regionEndLine :: Int,
+    -- | The column within the ending line where this region ends.
+    regionEndColumn :: Int
+} deriving (Eq, Show)
+
+instance ToJSON Region where
+    toJSON MkRegion{..} = object
+        [ "startLine" .= regionStartLine
+        , "startColumn" .= regionStartColumn
+        , "endLine" .= regionEndLine
+        , "endColumn" .= regionEndColumn
+        ]
+
+instance FromJSON Region where
+    parseJSON = withObject "Region" $ \obj ->
+        MkRegion <$> obj .: "startLine"
+                 <*> obj .: "startColumn"
+                 <*> obj .: "endLine"
+                 <*> obj .: "endColumn"
+
+-- | Represents parts of artifacts, e.g. a `Region` within a file.
+data PhysicalLocation = MkPhysicalLocation {
+    physicalLocationArtifactLocation :: ArtifactLocation,
+    physicalLocationRegion :: Region
+} deriving (Eq, Show)
+
+instance ToJSON PhysicalLocation where
+    toJSON MkPhysicalLocation{..} = object
+        [ "artifactLocation" .= physicalLocationArtifactLocation
+        , "region" .= physicalLocationRegion
+        ]
+
+instance FromJSON PhysicalLocation where
+    parseJSON = withObject "PhysicalLocation" $ \obj ->
+        MkPhysicalLocation <$> obj .: "artifactLocation"
+                           <*> obj .: "region"
+
+--------------------------------------------------------------------------------
diff --git a/src/Data/SARIF/Log.hs b/src/Data/SARIF/Log.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/SARIF/Log.hs
@@ -0,0 +1,67 @@
+--------------------------------------------------------------------------------
+-- SARIF implementation for Haskell
+--------------------------------------------------------------------------------
+-- This source code is licensed under the MIT license found in the LICENSE    --
+-- file in the root directory of this source tree.                            --
+--------------------------------------------------------------------------------
+
+-- | Provides the top-level structure of SARIF.
+module Data.SARIF.Log (
+    Log(..),
+    defaultLog,
+    decodeSarifFileStrict,
+    encodeSarifAsLBS
+) where
+
+--------------------------------------------------------------------------------
+
+import Data.Aeson
+import qualified Data.ByteString.Lazy as LBS
+import Data.Text
+
+import Data.SARIF.Run (Run(..))
+
+--------------------------------------------------------------------------------
+
+-- | Each SARIF file contains one `Log` value at the top.
+data Log = MkLog {
+    -- | The version identifier of the SARIF format used by the file.
+    logVersion :: Text,
+    -- | A list of descriptions of runs of static analysis tools.
+    logRuns :: [Run]
+} deriving (Eq, Show)
+
+-- | The URL of the JSON schema that describes SARIF.
+schemaUrl :: Text
+schemaUrl = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
+
+instance ToJSON Log where
+    toJSON MkLog{..} = object
+        [ "version" .= logVersion
+        , "runs" .= logRuns
+        , "$schema" .= schemaUrl
+        ]
+
+instance FromJSON Log where
+    parseJSON = withObject "Log" $ \obj ->
+        MkLog <$> obj .: "version"
+              <*> obj .: "runs"
+
+-- | Represents a default `Log` value.
+defaultLog :: Log
+defaultLog = MkLog{
+    logVersion = "2.1.0",
+    logRuns = []
+}
+
+-- | `decodeSarifFileStrict` @filepath@ is a type-specialised version of
+-- `eitherDecodeFileStrict` for `Log`.
+decodeSarifFileStrict :: FilePath -> IO (Either String Log)
+decodeSarifFileStrict = eitherDecodeFileStrict
+
+-- | `encodeSarifAsLBS` @log@ encodes a `Log` value as a lazy `LBS.ByteString`.
+-- This is a type-specialised version of `encode`.
+encodeSarifAsLBS :: Log -> LBS.ByteString
+encodeSarifAsLBS = encode
+
+--------------------------------------------------------------------------------
diff --git a/src/Data/SARIF/MultiformatMessageString.hs b/src/Data/SARIF/MultiformatMessageString.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/SARIF/MultiformatMessageString.hs
@@ -0,0 +1,51 @@
+--------------------------------------------------------------------------------
+-- SARIF implementation for Haskell
+--------------------------------------------------------------------------------
+-- This source code is licensed under the MIT license found in the LICENSE    --
+-- file in the root directory of this source tree.                            --
+--------------------------------------------------------------------------------
+
+-- | Provides the `MultiformatMessageString` type which is used to represent
+-- messages in different formats, such as text or markdown.
+module Data.SARIF.MultiformatMessageString (
+    MultiformatMessageString(..),
+    defaultMultiformatMessageString
+) where
+
+--------------------------------------------------------------------------------
+
+import Data.Aeson.Optional
+import Data.Text
+
+--------------------------------------------------------------------------------
+
+-- | Represents a message in at least textual representation, but also allowing
+-- it to be provided in other formats such as markdown.
+data MultiformatMessageString = MkMultiformatMessageString {
+    -- | A textual representation of the message, which is mandatory.
+    mmsText :: Text,
+    -- | Optionally, a markdown representation of the message.
+    mmsMarkdown :: Maybe Text
+} deriving (Eq, Show)
+
+instance ToJSON MultiformatMessageString where
+    toJSON MkMultiformatMessageString{..} = object
+        [ "text" .= mmsText
+        , "markdown" .=? mmsMarkdown
+        ]
+
+instance FromJSON MultiformatMessageString where
+    parseJSON = withObject "MultiformatMessageString" $ \obj ->
+        MkMultiformatMessageString <$> obj .: "text"
+                                   <*> obj .:? "markdown"
+
+-- | `defaultMultiformatMessageString` @messageText@ constructs a
+-- `MultiformatMessageString` value where @messageText@ is the textual
+-- representation of the message.
+defaultMultiformatMessageString :: Text -> MultiformatMessageString
+defaultMultiformatMessageString text = MkMultiformatMessageString{
+    mmsText = text,
+    mmsMarkdown = Nothing
+}
+
+--------------------------------------------------------------------------------
diff --git a/src/Data/SARIF/ReportingDescriptor.hs b/src/Data/SARIF/ReportingDescriptor.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/SARIF/ReportingDescriptor.hs
@@ -0,0 +1,110 @@
+--------------------------------------------------------------------------------
+-- SARIF implementation for Haskell
+--------------------------------------------------------------------------------
+-- This source code is licensed under the MIT license found in the LICENSE    --
+-- file in the root directory of this source tree.                            --
+--------------------------------------------------------------------------------
+
+-- | Provides the `ReportingDescriptor` type, which is essentially used to
+-- represent descriptions of rules that a static analysis tool has applied.
+module Data.SARIF.ReportingDescriptor (
+    ReportingConfiguration(..),
+    defaultReportingConfiguration,
+    ReportingDescriptor(..),
+    defaultReportingDescriptor
+) where
+
+--------------------------------------------------------------------------------
+
+import Data.Aeson.Optional
+import qualified Data.Map.Lazy as M
+import Data.Text
+
+import Data.SARIF.Level
+import Data.SARIF.MultiformatMessageString
+
+--------------------------------------------------------------------------------
+
+-- | Represents default configurations for `ReportingDescriptor` values. That is
+-- properties which may be overriden by individual results.
+newtype ReportingConfiguration = MkReportingConfiguration {
+    -- | The default severity of the reporting descriptor.
+    rcLevel :: Maybe Level
+} deriving (Eq, Show)
+
+instance ToJSON ReportingConfiguration where
+    toJSON MkReportingConfiguration{..} = object
+        [ "level" .=? rcLevel
+        ]
+instance FromJSON ReportingConfiguration where
+    parseJSON = withObject "ReportingConfiguration" $ \obj ->
+        MkReportingConfiguration <$> obj .: "level"
+
+-- | `defaultReportingConfiguration` is a default
+-- `ReportingConfiguration` value.
+defaultReportingConfiguration :: ReportingConfiguration
+defaultReportingConfiguration = MkReportingConfiguration{
+    rcLevel = Nothing
+}
+
+-- | Represents rules that a static analysis tool
+data ReportingDescriptor = MkReportingDescriptor {
+    -- | The unique ID of the rule.
+    rdId :: Text,
+    -- | A friendly name for the rule, which should be one word in
+    -- upper camel case.
+    rdName :: Maybe Text,
+    -- | A short description for the rule, which may contain spaces
+    -- and symbols.
+    rdShortDescription :: Maybe MultiformatMessageString,
+    -- | The full description.
+    rdFullDescription :: Maybe MultiformatMessageString,
+    -- | A URL to some help for this rule.
+    rdHelpUri :: Maybe Text,
+    -- | A recommendation for what to do in order to resolve an occurrence
+    -- of this rule.
+    rdHelp :: Maybe MultiformatMessageString,
+    -- | The default reporting configuration for the rule.
+    rdDefaultConfiguration :: Maybe ReportingConfiguration,
+    -- | Extra properties for this rule.
+    rdProperties :: M.Map Text Value
+} deriving (Eq, Show)
+
+instance ToJSON ReportingDescriptor where
+    toJSON MkReportingDescriptor{..} = object
+        [ "id" .= rdId
+        , "name" .=? rdName
+        , "shortDescription" .=? rdShortDescription
+        , "fullDescription" .=? rdFullDescription
+        , "helpUri" .=? rdHelpUri
+        , "help" .=? rdHelp
+        , "defaultConfiguration" .=? rdDefaultConfiguration
+        , "properties" .= rdProperties
+        ]
+
+instance FromJSON ReportingDescriptor where
+    parseJSON = withObject "ReportingDescriptor" $ \obj ->
+        MkReportingDescriptor <$> obj .: "id"
+                              <*> obj .:? "name"
+                              <*> obj .:? "shortDescription"
+                              <*> obj .:? "fullDescription"
+                              <*> obj .:? "helpUri"
+                              <*> obj .:? "help"
+                              <*> obj .:? "defaultConfiguration"
+                              <*> obj .: "properties" .!= M.empty
+
+-- | `defaultReportingDescriptor` @id@ constructs a default
+-- `ReportingDescriptor` with the given @id@, which must be unique.
+defaultReportingDescriptor :: Text -> ReportingDescriptor
+defaultReportingDescriptor ident = MkReportingDescriptor{
+    rdId = ident,
+    rdName = Nothing,
+    rdShortDescription = Nothing,
+    rdFullDescription = Nothing,
+    rdHelpUri = Nothing,
+    rdHelp = Nothing,
+    rdDefaultConfiguration = Nothing,
+    rdProperties = M.empty
+}
+
+--------------------------------------------------------------------------------
diff --git a/src/Data/SARIF/Result.hs b/src/Data/SARIF/Result.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/SARIF/Result.hs
@@ -0,0 +1,54 @@
+--------------------------------------------------------------------------------
+-- SARIF implementation for Haskell
+--------------------------------------------------------------------------------
+-- This source code is licensed under the MIT license found in the LICENSE    --
+-- file in the root directory of this source tree.                            --
+--------------------------------------------------------------------------------
+
+-- | Provides the `Result` type which represents results of a
+-- static analysis tool.
+module Data.SARIF.Result (
+    Level(..),
+    Result(..)
+) where
+
+--------------------------------------------------------------------------------
+
+import Data.Aeson.Optional hiding (Result, Error)
+import Data.Text
+
+import Data.SARIF.Level
+import Data.SARIF.Location
+import Data.SARIF.MultiformatMessageString
+
+--------------------------------------------------------------------------------
+
+-- | Represents the results of a run of a static analysis tool.
+data Result = MkResult {
+    -- | The unique ID of the rule of which this result is an occurrence of.
+    resultRuleId :: Text,
+    -- | A result-specific message which may refer to specific variable names
+    -- etc. which caused the rule to trigger.
+    resultMessage :: MultiformatMessageString,
+    -- | A list of locations which caused the rule to trigger.
+    resultLocations :: [Location],
+    -- | An optional override for the default `Level` of the rule.
+    resultLevel :: Maybe Level
+} deriving (Eq, Show)
+
+instance ToJSON Result where
+    toJSON MkResult{..} = object
+        [ "ruleId" .= resultRuleId
+        , "message" .= resultMessage
+        , "locations" .= resultLocations
+        , "level" .=? resultLevel
+        ]
+
+instance FromJSON Result where
+    parseJSON = withObject "Result" $ \obj ->
+        MkResult <$> obj .: "ruleId"
+                 <*> obj .: "message"
+                 <*> obj .: "locations"
+                 <*> obj .: "level"
+
+--------------------------------------------------------------------------------
diff --git a/src/Data/SARIF/Run.hs b/src/Data/SARIF/Run.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/SARIF/Run.hs
@@ -0,0 +1,48 @@
+--------------------------------------------------------------------------------
+-- SARIF implementation for Haskell
+--------------------------------------------------------------------------------
+-- This source code is licensed under the MIT license found in the LICENSE    --
+-- file in the root directory of this source tree.                            --
+--------------------------------------------------------------------------------
+
+-- | Provides the `Run` type which represents a single run of a
+-- static analysis tool.
+module Data.SARIF.Run (
+    Run(..)
+) where
+
+--------------------------------------------------------------------------------
+
+import Data.Aeson hiding (Result)
+
+import Data.SARIF.Artifact
+import Data.SARIF.Result (Result(..))
+import Data.SARIF.Tool (Tool(..))
+
+--------------------------------------------------------------------------------
+
+-- | Represents individual runs of static analysis tools.
+data Run = MkRun {
+    -- | A description of the tool that was run.
+    runTool :: Tool,
+    -- | A list of the artifacts that were scanned by the tool.
+    runArtifacts :: [Artifact],
+    -- | The results produced by the tool as a result of scanning
+    -- the artifacts.
+    runResults :: [Result]
+} deriving (Eq, Show)
+
+instance ToJSON Run where
+    toJSON MkRun{..} = object
+        [ "tool" .= runTool
+        , "artifacts" .= runArtifacts
+        , "results" .= runResults
+        ]
+
+instance FromJSON Run where
+    parseJSON = withObject "Run" $ \obj ->
+        MkRun <$> obj .: "tool"
+              <*> obj .: "artifacts" .!= []
+              <*> obj .: "results" .!= []
+
+--------------------------------------------------------------------------------
diff --git a/src/Data/SARIF/Tool.hs b/src/Data/SARIF/Tool.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/SARIF/Tool.hs
@@ -0,0 +1,98 @@
+--------------------------------------------------------------------------------
+-- SARIF implementation for Haskell
+--------------------------------------------------------------------------------
+-- This source code is licensed under the MIT license found in the LICENSE    --
+-- file in the root directory of this source tree.                            --
+--------------------------------------------------------------------------------
+
+-- | Provides the `Tool` type which is used to describe static analysis tools.
+module Data.SARIF.Tool (
+    Tool(..),
+    ToolComponent(..),
+    defaultToolComponent
+) where
+
+--------------------------------------------------------------------------------
+
+import GHC.Generics
+
+import Data.Aeson.Optional
+import Data.Text
+import Data.UUID.Types
+
+import Data.SARIF.ReportingDescriptor
+
+--------------------------------------------------------------------------------
+
+-- | Used to describe static analysis tools.
+data Tool = MkTool {
+    -- | The description of the tool itself.
+    toolDriver :: ToolComponent,
+    -- | Descriptions of any extensions to the tool that were used.
+    toolExtensions :: [ToolComponent]
+} deriving (Eq, Show, Generic)
+
+instance ToJSON Tool where
+    toJSON MkTool{..} = object
+        [ "driver" .= toolDriver
+        , "extensions" .= toolExtensions
+        ]
+
+instance FromJSON Tool where
+    parseJSON = withObject "Tool" $ \obj ->
+        MkTool <$> obj .: "driver"
+               <*> obj .: "extensions" .!= []
+
+-- | A description of a tool component, such as a static analysis tool or
+-- an extension to one.
+data ToolComponent = MkToolComponent {
+    -- | The short name of the tool component.
+    toolComponentName :: Maybe Text,
+    -- | The full name of the tool component.
+    toolComponentFullName :: Maybe Text,
+    -- | The semver of the tool component.
+    toolComponentSemanticVersion :: Maybe Text,
+    -- | The version of the tool component.
+    toolComponentVersion :: Maybe Text,
+    -- | A GUID which identifies the tool component.
+    toolComponentGUID :: Maybe UUID,
+    -- | A URI pointing to more information about the tool component.
+    toolComponentInformationUri :: Maybe Text,
+    -- | A list of rules that the tool component applies.
+    toolComponentRules :: [ReportingDescriptor]
+} deriving (Eq, Show, Generic)
+
+instance ToJSON ToolComponent where
+    toJSON MkToolComponent{..} = object
+        [ "name" .= toolComponentName
+        , "fullName" .=? toolComponentFullName
+        , "semanticVersion" .=? toolComponentSemanticVersion
+        , "version" .=? toolComponentVersion
+        , "guid" .=? toolComponentGUID
+        , "informationUri" .=? toolComponentInformationUri
+        , "rules" .= toolComponentRules
+        ]
+
+instance FromJSON ToolComponent where
+    parseJSON = withObject "ToolComponent" $ \obj ->
+        MkToolComponent <$> obj .: "name"
+                        <*> obj .: "fullName"
+                        <*> obj .:? "semanticVersion"
+                        <*> obj .:? "version"
+                        <*> obj .:? "guid"
+                        <*> obj .:? "informationUri"
+                        <*> obj .: "rules" .!= []
+
+-- | `defaultToolComponent` is a default value of the `ToolComponent` type.
+defaultToolComponent :: ToolComponent
+defaultToolComponent = MkToolComponent{
+    toolComponentName = Nothing,
+    toolComponentFullName = Nothing,
+    toolComponentSemanticVersion = Nothing,
+    toolComponentVersion = Nothing,
+    toolComponentGUID = Nothing,
+    toolComponentInformationUri = Nothing,
+    toolComponentRules = []
+}
+
+--------------------------------------------------------------------------------
