| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Ecluse.Core.Cve
Description
The advisory lookup capability: answer CVE questions about a package
version from a local, already-synced osv.db artifact, never the network.
The handle is deliberately dumb data access over one artifact file. Rule
semantics live in pure predicates over what it returns
(insideAffectedRange), because SQLite's text collation cannot order
versions; only compareVersions can. The one deliberate
exception is cveRemediationProbe: a fixed bound in the artifact is a single
canonical version string, so exact-fix matching is plain string equality and
rides the (package_name, fixed_version) index in one traversal. A fix
published under a non-canonical version string misses the probe and simply
waits out the ordinary quarantine; the operator workaround is an explicit
AllowByIdentity rule.
An artifact is accepted or rejected at openCveDb (epoch stamp, table shape,
ecosystem), with rejection as a value: the caller keeps its last known-good
handle and alarms. See Ecluse.Core.Cve.Internal for the hardening detail.
Ownership is split at the type level: openCveDb yields a CveDb, the
owning resource whose holder alone may cveDbClose; consumers are handed only
its CveLookup view, so nothing evaluating rules can release a shared
connection. A lexically-scoped use (a test, a one-shot check) brackets with
withCveDb; a dynamically-scoped owner (the background sync's shadow-swap,
which retires an artifact only when no evaluation still reads it) holds the
CveDb and closes explicitly.
Synopsis
- data CveDb = CveDb {
- cveDbLookup :: CveLookup
- cveDbClose :: IO ()
- cveDbMeta :: [(Text, Text)]
- openCveDb :: Ecosystem -> FilePath -> IO (Either CveDbRejected CveDb)
- withCveDb :: Ecosystem -> FilePath -> (CveLookup -> IO a) -> IO (Either CveDbRejected a)
- data CveLookup = CveLookup {
- cveRemediationProbe :: Text -> Text -> IO Bool
- cveAdvisoriesFor :: Text -> IO [AdvisoryRange]
- data AdvisoryRange = AdvisoryRange {}
- data CveDbRejected
- insideAffectedRange :: Ecosystem -> Text -> AdvisoryRange -> Bool
The owning resource
One opened artifact: the consumer view plus the owner's close. Whoever
holds this owns the connection's lifetime; hand consumers cveDbLookup only.
Constructors
| CveDb | |
Fields
| |
openCveDb :: Ecosystem -> FilePath -> IO (Either CveDbRejected CveDb) Source #
Open an osv.db artifact and build the owning handle over it, or reject
the artifact (CveDbRejected) with its connection already closed. Throws on
faults below the acceptance contract (an unreadable file, a malformed
provenance row), and then too the connection is already closed: an exception
never leaks it.
withCveDb :: Ecosystem -> FilePath -> (CveLookup -> IO a) -> IO (Either CveDbRejected a) Source #
Bracket a lexically-scoped use of an artifact: open, hand the consumer
view to the action, and close on any exit. A rejected artifact short-circuits
(Left) without running the action; its connection is already closed.
The consumer view
Advisory questions about one ecosystem's artifact -- the read-only view a
consumer (a rule evaluation) is handed. It deliberately cannot release the
underlying connection; that is the owning CveDb's capability.
Names and versions are the artifact's own vocabulary: the OSV wire package
name (scope inline, e.g. @scope/name) and verbatim version text. Callers
render their domain values to that form at the boundary.
Constructors
| CveLookup | |
Fields
| |
What a lookup returns
data AdvisoryRange Source #
One advisory range recorded against a package: the advisory's identifier,
its optional qualitative severity label, and the affected interval's bounds as
the artifact stores them (verbatim version text; Nothing introduced means
"from the beginning", Nothing fixed means "no fix known").
Constructors
| AdvisoryRange | |
Instances
| Show AdvisoryRange Source # | |
Defined in Ecluse.Core.Cve.Internal Methods showsPrec :: Int -> AdvisoryRange -> ShowS # show :: AdvisoryRange -> String # showList :: [AdvisoryRange] -> ShowS # | |
| Eq AdvisoryRange Source # | |
Defined in Ecluse.Core.Cve.Internal Methods (==) :: AdvisoryRange -> AdvisoryRange -> Bool # (/=) :: AdvisoryRange -> AdvisoryRange -> Bool # | |
Rejection
data CveDbRejected Source #
Why a downloaded artifact was refused before a handle was built over it.
A rejection is a value, not an exception: the caller (the sync task, once it exists) has a real decision to make, keep the last known-good database and alarm, rather than a fault to unwind from.
Constructors
| CveDbWrongEpoch Int | The artifact's |
| CveDbRangesNotATable | The ranges relation is not a plain table -- a view here is attacker-authored SQL wearing the table's name. |
| CveDbEcosystemMismatch (Maybe Text) | The artifact's |
Instances
| Show CveDbRejected Source # | |
Defined in Ecluse.Core.Cve.Internal Methods showsPrec :: Int -> CveDbRejected -> ShowS # show :: CveDbRejected -> String # showList :: [CveDbRejected] -> ShowS # | |
| Eq CveDbRejected Source # | |
Defined in Ecluse.Core.Cve.Internal Methods (==) :: CveDbRejected -> CveDbRejected -> Bool # (/=) :: CveDbRejected -> CveDbRejected -> Bool # | |
Pure range matching
insideAffectedRange :: Ecosystem -> Text -> AdvisoryRange -> Bool Source #
Is this version inside the advisory range's affected interval,
introduced <= v < fixed, under the ecosystem's version ordering?
Fail-closed for the allow direction. This predicate guards the remediation fast lane (a fixed version must not fast-track while it sits inside another advisory's affected range), so every unprovable comparison, an unparseable bound, an unparseable version, counts as inside: trust is only ever granted on evidence. A future deny-direction consumer wants the same polarity for its own reason (cannot prove safe, assume affected), but must not reuse this documentation's rationale blindly if its needs diverge.