mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-12 19:55:14 +02:00
[pitboss] phase 21: Track F.1 — SurfaceMap module + Python/Flask vertical
This commit is contained in:
parent
f8bff38217
commit
c03326a658
9 changed files with 1396 additions and 1 deletions
|
|
@ -228,6 +228,15 @@ pub mod index {
|
|||
CREATE INDEX IF NOT EXISTS idx_dynamic_verdict_cache_spec_hash
|
||||
ON dynamic_verdict_cache(spec_hash);
|
||||
|
||||
-- Phase 21: persisted attack-surface map. One row per project.
|
||||
-- Stored as canonical JSON so the round-trip is byte-identical
|
||||
-- across rescans (see `SurfaceMap::to_json`).
|
||||
CREATE TABLE IF NOT EXISTS surface_map (
|
||||
project TEXT PRIMARY KEY,
|
||||
map_json BLOB NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes on (project, file_path) for the per-file replace_* paths.
|
||||
-- Without these, every DELETE WHERE project=? AND file_path=? does a
|
||||
-- full table scan, which dominates indexing time as the cache grows.
|
||||
|
|
@ -547,6 +556,22 @@ pub mod index {
|
|||
conn.execute_batch(SCHEMA)?;
|
||||
}
|
||||
|
||||
// Phase 21: ensure the `surface_map` table exists on
|
||||
// DBs created before this column set was introduced.
|
||||
let surface_exists: bool = conn
|
||||
.query_row(
|
||||
"SELECT 1 FROM sqlite_master
|
||||
WHERE type = 'table' AND name = 'surface_map'",
|
||||
[],
|
||||
|_| Ok(true),
|
||||
)
|
||||
.optional()?
|
||||
.unwrap_or(false);
|
||||
if !surface_exists {
|
||||
tracing::info!("creating surface_map table");
|
||||
conn.execute_batch(SCHEMA)?;
|
||||
}
|
||||
|
||||
// Schema version check: invalidate cached summary tables
|
||||
// when the on-disk artefact layout has changed in an
|
||||
// incompatible way, independently of the engine version.
|
||||
|
|
@ -1882,6 +1907,63 @@ pub mod index {
|
|||
Ok(out)
|
||||
}
|
||||
|
||||
/// Persist a [`crate::surface::SurfaceMap`] for this project.
|
||||
///
|
||||
/// Replaces any previously-persisted map; the table holds one row
|
||||
/// per project. The map is canonicalised before serialisation so
|
||||
/// `replace_surface_map` + `load_surface_map` round-trip is
|
||||
/// byte-identical for structurally identical maps.
|
||||
pub fn replace_surface_map(
|
||||
&mut self,
|
||||
map: &crate::surface::SurfaceMap,
|
||||
) -> NyxResult<()> {
|
||||
let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as i64;
|
||||
let mut canon = map.clone();
|
||||
let bytes = canon
|
||||
.to_json()
|
||||
.map_err(|e| NyxError::Msg(format!("surface map serialise: {e}")))?;
|
||||
self.c().execute(
|
||||
"INSERT OR REPLACE INTO surface_map (project, map_json, updated_at)
|
||||
VALUES (?1, ?2, ?3)",
|
||||
params![self.project, bytes, now],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Load the persisted [`crate::surface::SurfaceMap`] for this
|
||||
/// project, or `None` when no map has been written.
|
||||
pub fn load_surface_map(&self) -> NyxResult<Option<crate::surface::SurfaceMap>> {
|
||||
let row: Option<Vec<u8>> = self
|
||||
.c()
|
||||
.query_row(
|
||||
"SELECT map_json FROM surface_map WHERE project = ?1",
|
||||
params![self.project],
|
||||
|r| r.get::<_, Vec<u8>>(0),
|
||||
)
|
||||
.optional()?;
|
||||
let Some(bytes) = row else {
|
||||
return Ok(None);
|
||||
};
|
||||
let map = crate::surface::SurfaceMap::from_json(&bytes)
|
||||
.map_err(|e| NyxError::Msg(format!("surface map deserialise: {e}")))?;
|
||||
Ok(Some(map))
|
||||
}
|
||||
|
||||
/// Return the raw JSON bytes stored for the surface map without
|
||||
/// deserialising. Used by the round-trip parity tests so they
|
||||
/// can compare on-disk bytes across rescans.
|
||||
pub fn load_surface_map_bytes(&self) -> NyxResult<Option<Vec<u8>>> {
|
||||
let row: Option<Vec<u8>> = self
|
||||
.c()
|
||||
.query_row(
|
||||
"SELECT map_json FROM surface_map WHERE project = ?1",
|
||||
params![self.project],
|
||||
|r| r.get::<_, Vec<u8>>(0),
|
||||
)
|
||||
.optional()?;
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
/// Remove a file and all derived persisted state for this project.
|
||||
///
|
||||
/// This deletes the file row, issues, and all persisted summary rows so
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue