v0.3.8-pre.011

This commit is contained in:
2026-09-03 22:31:28 +02:00
parent 516c52ece9
commit 80fe2fd9e2
5 changed files with 461 additions and 5 deletions

View File

@@ -1,12 +1,12 @@
# file: Cargo.toml
# version: 468
# version: 469
[workspace]
resolver = "3"
members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-store-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-job-api", "crates/ksp-job-backfill-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib"]
[workspace.package]
version = "0.3.8-pre.10.fix.2"
version = "0.3.8-pre.11"
edition = "2024"
license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"

View File

@@ -0,0 +1,369 @@
// file: crates/ksp-app-store-desk/tests/release_completeness.rs
// version: 1
//! Release-completeness firewall for the Store Desk V1 RAW surface.
fn parse_json(source: &str) -> serde_json::Result<serde_json::Value> {
return serde_json::from_str::<serde_json::Value>(source);
}
fn toml_table_keys<'a>(source: &'a str, table: &str) -> std::vec::Vec<&'a str> {
let header = format!("[{table}]");
let mut in_table = false;
let mut keys = std::vec::Vec::<&str>::new();
for line in source.lines() {
let trimmed = line.trim();
if trimmed == header {
in_table = true;
continue;
}
if in_table && trimmed.starts_with('[') {
break;
}
if !in_table || trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
if let std::option::Option::Some((key, _)) = trimmed.split_once('=') {
let key = key.trim();
let normalized = match key.strip_suffix(".workspace") {
std::option::Option::Some(value) => value,
std::option::Option::None => key,
};
keys.push(normalized);
}
}
keys.sort_unstable();
return keys;
}
fn json_object_keys(value: &serde_json::Value) -> std::vec::Vec<&str> {
let mut keys = match value.as_object() {
std::option::Option::Some(object) => object.keys().map(std::string::String::as_str).collect::<std::vec::Vec<&str>>(),
std::option::Option::None => std::vec::Vec::<&str>::new(),
};
keys.sort_unstable();
return keys;
}
fn source_segment<'a>(source: &'a str, start: &str, end: &str) -> &'a str {
let after_start = match source.split_once(start) {
std::option::Option::Some((_, tail)) => tail,
std::option::Option::None => return "",
};
return match after_start.split_once(end) {
std::option::Option::Some((head, _)) => head,
std::option::Option::None => after_start,
};
}
fn handler_commands(source: &str) -> std::vec::Vec<&str> {
let block = source_segment(source, "tauri::generate_handler![", "]);\n}");
let mut commands = block.lines().map(str::trim).filter(|line| !line.is_empty()).map(|line| line.trim_end_matches(',')).collect::<std::vec::Vec<&str>>();
commands.sort_unstable();
return commands;
}
fn crate_modules(source: &str) -> std::vec::Vec<&str> {
let mut modules = source
.lines()
.map(str::trim)
.filter(|line| line.starts_with("mod ") && line.ends_with(';'))
.map(|line| line.trim_start_matches("mod ").trim_end_matches(';'))
.collect::<std::vec::Vec<&str>>();
modules.sort_unstable();
return modules;
}
fn visible_struct_names(source: &str) -> std::vec::Vec<&str> {
let mut names = source
.lines()
.map(str::trim)
.filter(|line| line.starts_with("pub(crate) struct "))
.filter_map(|line| line.strip_prefix("pub(crate) struct "))
.filter_map(|line| line.split_whitespace().next())
.map(|name| name.trim_end_matches('{'))
.collect::<std::vec::Vec<&str>>();
names.sort_unstable();
return names;
}
fn const_line<'a>(source: &'a str, name: &str) -> &'a str {
let prefix = format!("const {name}:");
for line in source.lines() {
if line.starts_with(prefix.as_str()) {
return line;
}
}
return "";
}
#[test]
fn pre_011_rust_and_frontend_dependency_inventories_are_exact() {
let manifest = include_str!("../Cargo.toml");
assert_eq!(toml_table_keys(manifest, "build-dependencies"), ["tauri-build"]);
assert_eq!(
toml_table_keys(manifest, "dependencies"),
[
"chrono",
"fs2",
"ksp-config-lib",
"ksp-core-lib",
"ksp-logging-lib",
"ksp-store-lib",
"serde",
"tauri",
"tauri-plugin-tracing",
"tokio",
"ts-rs",
]
);
assert_eq!(toml_table_keys(manifest, "dev-dependencies"), ["serde_json"]);
let package = parse_json(include_str!("../package.json"));
assert!(package.is_ok(), "Store Desk package.json must stay valid JSON");
if let std::result::Result::Ok(package) = package {
assert_eq!(
json_object_keys(&package["dependencies"]),
[
"@fltsci/tauri-plugin-tracing",
"@fortawesome/fontawesome-free",
"@tauri-apps/api",
"bootstrap",
"datatables.net-bs5",
"resize-observer-polyfill",
"simplebar",
]
);
assert_eq!(
json_object_keys(&package["devDependencies"]),
["@tauri-apps/cli", "@types/bootstrap", "@types/node", "sass-embedded", "typescript", "vite"]
);
}
}
#[test]
fn pre_011_module_command_and_tauri_capability_inventories_are_exact() {
let lib = include_str!("../src/lib.rs");
assert_eq!(
crate_modules(lib),
[
"app_state",
"bootstrap",
"constants",
"dto_account",
"dto_common",
"dto_observation",
"dto_transaction",
"errors",
"frontend_logging",
"logging_runtime",
"splash",
"store_runtime",
"tauri",
"tw_main",
"tw_splash",
]
);
let tauri = include_str!("../src/tauri.rs");
assert_eq!(
handler_commands(tauri),
[
"emit_frontend_log",
"get_shell_status",
"splash_frontend_ready",
"store_get_account_detail",
"store_get_transaction_detail",
"store_query_account_observations",
"store_query_accounts",
"store_query_transaction_observations",
"store_query_transactions",
"store_runtime_status",
]
);
let capability = parse_json(include_str!("../capabilities/default.json"));
assert!(capability.is_ok(), "Store Desk capability JSON must stay valid");
if let std::result::Result::Ok(capability) = capability {
assert_eq!(capability["identifier"], "default");
assert_eq!(capability["windows"], serde_json::json!(["splash", "main"]));
assert_eq!(capability["permissions"], serde_json::json!(["core:default", "tracing:default"]));
}
}
#[test]
fn pre_011_config_composition_is_exact_network_scoped_and_secret_free() {
let source = include_str!("../../../config/composite.ksp-app-store-desk.json");
let composite = parse_json(source);
assert!(composite.is_ok(), "Store Desk composite must stay valid JSON");
if let std::result::Result::Ok(composite) = composite {
assert_eq!(composite["format_version"], 1);
assert_eq!(composite["default_profile"], "mainnet");
let profiles = composite["profiles"].as_array();
assert!(profiles.is_some());
if let std::option::Option::Some(profiles) = profiles {
assert_eq!(profiles.len(), 3);
let profile_ids = profiles
.iter()
.map(|profile| match profile["profile_id"].as_str() {
std::option::Option::Some(value) => value,
std::option::Option::None => "",
})
.collect::<std::vec::Vec<&str>>();
assert_eq!(profile_ids, ["devnet", "mainnet", "testnet"]);
for profile in profiles {
let profile_id = match profile["profile_id"].as_str() {
std::option::Option::Some(value) => value,
std::option::Option::None => "",
};
let documents = profile["documents"].as_array();
assert!(documents.is_some());
if let std::option::Option::Some(documents) = documents {
assert_eq!(documents.len(), 2);
assert_eq!(documents[0]["component_id"], "logging");
assert_eq!(documents[0]["file_id"], "cfg.std.logging");
assert_eq!(documents[0]["profile_id"], "supertrace");
assert_eq!(documents[1]["component_id"], "store");
assert_eq!(documents[1]["file_id"], "cfg.std.store");
assert_eq!(documents[1]["profile_id"], profile_id);
}
}
}
}
for forbidden in ["postgres://", "postgresql://", "password", "credential", "connection_uri", "connectionUri", "KSP_STORE_POSTGRES"] {
assert!(!source.contains(forbidden), "Store Desk composite leaked physical/secret material: {forbidden}");
}
}
#[test]
fn pre_011_frontend_security_scan_keeps_network_storage_native_dialogs_and_physical_store_out() {
let frontend = [
include_str!("../frontend/ts/frontend_log.ts"),
include_str!("../frontend/ts/invoke.ts"),
include_str!("../frontend/ts/main.ts"),
include_str!("../frontend/ts/splash.ts"),
include_str!("../frontend/main.html"),
include_str!("../frontend/splash.html"),
]
.join("\n");
for forbidden in [
"fetch(",
"XMLHttpRequest",
"WebSocket(",
"EventSource(",
"localStorage",
"sessionStorage",
"indexedDB",
"document.cookie",
"window.open(",
"alert(",
"confirm(",
"prompt(",
"postgres://",
"postgresql://",
"connection_uri",
"connectionUri",
"ksp_store_postgres",
"tokio_postgres",
"deadpool_postgres",
"SELECT ",
"INSERT ",
"UPDATE ",
"DELETE ",
] {
assert!(!frontend.contains(forbidden), "Store Desk frontend crossed the release security firewall: {forbidden}");
}
}
#[test]
fn pre_011_ipc_dto_inventory_is_exact_and_table_contracts_remain_raw_byte_free() {
let dto_account = include_str!("../src/dto_account.rs");
let dto_common = include_str!("../src/dto_common.rs");
let dto_observation = include_str!("../src/dto_observation.rs");
let dto_transaction = include_str!("../src/dto_transaction.rs");
let dto_sources = [dto_account, dto_common, dto_observation, dto_transaction].join("\n");
assert_eq!(
visible_struct_names(dto_sources.as_str()),
[
"CommandErrorDto",
"ShellStatusDto",
"StoreAccountDetailDto",
"StoreAccountDetailRequestDto",
"StoreAccountObservationQueryRequestDto",
"StoreAccountObservationQueryResponseDto",
"StoreAccountObservationRowDto",
"StoreAccountQueryRequestDto",
"StoreAccountQueryResponseDto",
"StoreAccountRowDto",
"StoreObservationProvenanceDto",
"StoreRuntimeStatusDto",
"StoreTransactionDetailDto",
"StoreTransactionDetailRequestDto",
"StoreTransactionObservationQueryRequestDto",
"StoreTransactionObservationQueryResponseDto",
"StoreTransactionObservationRowDto",
"StoreTransactionQueryRequestDto",
"StoreTransactionQueryResponseDto",
"StoreTransactionRowDto",
]
);
for request_marker in [
"pub(crate) struct StoreTransactionQueryRequestDto",
"pub(crate) struct StoreTransactionDetailRequestDto",
"pub(crate) struct StoreAccountQueryRequestDto",
"pub(crate) struct StoreAccountDetailRequestDto",
"pub(crate) struct StoreTransactionObservationQueryRequestDto",
"pub(crate) struct StoreAccountObservationQueryRequestDto",
] {
assert!(dto_sources.contains(request_marker), "missing Store Desk request DTO: {request_marker}");
}
for forbidden in ["RawPageCursor", "tokio_postgres", "deadpool_postgres", "ksp_store_postgres", "source_payload_bytes", "raw_bytes"] {
assert!(!dto_sources.contains(forbidden), "Store Desk DTO surface leaked forbidden contract material: {forbidden}");
}
assert!(!source_segment(dto_transaction, "pub(crate) struct StoreTransactionRowDto", "/// Bounded detail projection").contains("payload_preview"));
assert!(!source_segment(dto_account, "pub(crate) struct StoreAccountRowDto", "/// Bounded detail projection").contains("data_preview"));
assert!(!dto_observation.contains("payload_preview"));
assert!(!dto_observation.contains("data_preview"));
}
#[test]
fn pre_011_cursor_keyset_navigation_and_random_access_inspection_coexist_without_contract_collision() {
let api = include_str!("../../ksp-store-api/src/lib.rs");
let facade = include_str!("../../ksp-store-lib/src/lib.rs");
let transaction = include_str!("../../ksp-store-postgres-lib/src/raw_transaction.rs");
let account = include_str!("../../ksp-store-postgres-lib/src/raw_account.rs");
let app_runtime = include_str!("../src/store_runtime.rs");
for expected in ["RawPageCursor", "RawPageRequest", "RawInspectionPageRequest", "RawTransactionInspectionQuery", "RawAccountStateInspectionQuery"] {
assert!(api.contains(expected), "Store API lost pagination/inspection contract: {expected}");
assert!(facade.contains(expected), "Store facade lost pagination/inspection re-export: {expected}");
}
for cursor_statement in ["LIST_TRANSACTIONS_ASC_SQL", "LIST_TRANSACTIONS_DESC_SQL"] {
let statement = const_line(transaction, cursor_statement);
assert!(!statement.is_empty(), "missing transaction cursor statement: {cursor_statement}");
assert!(!statement.contains("OFFSET"), "transaction cursor statement became OFFSET based: {cursor_statement}");
}
for cursor_statement in [
"LIST_ACCOUNT_STATES_ASC_SQL",
"LIST_ACCOUNT_STATES_DESC_SQL",
"LIST_ACCOUNT_STATES_BY_PUBKEY_ASC_SQL",
"LIST_ACCOUNT_STATES_BY_PUBKEY_DESC_SQL",
] {
let statement = const_line(account, cursor_statement);
assert!(!statement.is_empty(), "missing account cursor statement: {cursor_statement}");
assert!(!statement.contains("OFFSET"), "account cursor statement became OFFSET based: {cursor_statement}");
}
for inspection_statement in
["INSPECT_TRANSACTIONS_ASC_SQL", "INSPECT_TRANSACTIONS_DESC_SQL", "INSPECT_OBSERVATIONS_ASC_SQL", "INSPECT_OBSERVATIONS_DESC_SQL"]
{
let statement = const_line(transaction, inspection_statement);
assert!(statement.contains("OFFSET"), "transaction inspection statement lost random access: {inspection_statement}");
}
for inspection_statement in [
"INSPECT_ACCOUNT_STATES_ASC_SQL",
"INSPECT_ACCOUNT_STATES_DESC_SQL",
"INSPECT_ACCOUNT_OBSERVATIONS_ASC_SQL",
"INSPECT_ACCOUNT_OBSERVATIONS_DESC_SQL",
] {
let statement = const_line(account, inspection_statement);
assert!(statement.contains("OFFSET"), "account inspection statement lost random access: {inspection_statement}");
}
assert!(app_runtime.contains("RawInspectionPageRequest::new"));
assert!(!app_runtime.contains("RawPageCursor"));
}

48
deltas/0.3.8/pre.011.md Normal file
View File

@@ -0,0 +1,48 @@
<!-- file: deltas/0.3.8/pre.011.md -->
<!-- version: 1 -->
# Delta `0.3.8-pre.011`
## Base
- livraison directe : `0.3.8-pre.010-fix.002` ;
- workspace : `0.3.8-pre.10.fix.2` ;
- gate opérateur précédent : entièrement vert, sans warning Clippy.
## Objet
Fermer la sécurité, les dependency boundaries et la release completeness de Store Desk V1 RAW sans ajouter de fonctionnalité runtime.
## Changements
- ajout de `tests/release_completeness.rs` ;
- inventaires Cargo/npm exacts ;
- inventaires modules Rust, commands Tauri et permissions capability exacts ;
- composition Config Store Desk exacte et secret-free ;
- scan frontend no-network/no-browser-storage/no-native-dialog/no-physical-Store ;
- inventaire DTO/IPC exact et metadata-only ;
- canari explicite de coexistence cursor/keyset machine et inspection offset/limit DataTables ;
- synchronisation workspace vers `0.3.8-pre.11` ;
- plan et validation mis à jour.
## Hors périmètre
- aucun runtime/frontend/DTO modifié ;
- aucun Store API/façade/PostgreSQL/SQL/schema/migration modifié ;
- aucune Config ou capability Tauri modifiée ;
- aucune nouvelle dépendance.
## Gate recommandé
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-app-store-desk
cargo test -p ksp-store-lib
cargo check -p ksp-store-lib --no-default-features
```

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/029-V0_3_8_STORE_DESK_PLAN.md -->
<!-- version: 12 -->
<!-- version: 13 -->
# Plan v0.3.8 — Store Desk V1 RAW
@@ -622,7 +622,9 @@ Le hardening V1 centralise la validation offset/limit côté Rust, rejette les d
### pre.011 — security / dependency / release completeness
Dependency firewall, Config composition, IPC/security scans, no-browser-storage, exact module/dependency/capability inventories, coexistence cursor + inspection.
Verrouiller la surface V1 sans ajouter de fonctionnalité métier ni modifier Store API/façade/PostgreSQL. Un nouveau firewall exécutable `release_completeness.rs` doit rendre exacts les inventaires directs Cargo/npm, les modules Rust Store Desk, les commands Tauri et les permissions desktop. La composition Config Store Desk reste exactement `logging + store` sur `devnet/mainnet/testnet`, sans URI ni secret dans le composite.
Le frontend est rescanné contre toute ouverture réseau navigateur, stockage navigateur RAW, dialogue natif ou matérialisation PostgreSQL/SQL. Les DTO/IPC doivent rester backend-neutral et metadata-only, avec les previews RAW bornés uniquement dans les DTO de détail déjà admis. Enfin, la coexistence des deux paradigmes de pagination est verrouillée explicitement : `RawPageCursor`/`RawPageRequest` et les statements PostgreSQL `LIST_*` restent keyset et OFFSET-free pour les machines/workers, tandis que `RawInspectionPageRequest` et les statements `INSPECT_*` conservent leur random access `OFFSET` pour Store Desk/DataTables. Aucun runtime n'est ajouté par cette tranche.
### pre.012 — gate technique final

View File

@@ -1,5 +1,5 @@
<!-- file: docs/validation/025-V0_3_8_STORE_DESK.md -->
<!-- version: 24 -->
<!-- version: 25 -->
# Validation v0.3.8 — Store Desk V1 RAW
@@ -785,3 +785,40 @@ Le gate opérateur de `0.3.8-pre.10.fix.1` confirme que les audits KSP, `cargo c
- [X] la version workspace devient `0.3.8-pre.10.fix.2` car un test Rust est modifié.
Le gate Cargo du fix reste à rejouer après application du delta.
## 38. Clôture `pre.010-fix.002` et préparation `pre.011` — release completeness
Le gate opérateur de `0.3.8-pre.10.fix.2` est entièrement vert :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (314 table(s), 714 file(s))
cargo check --workspace: PASS
cargo clippy --workspace --all-targets: PASS
cargo test -p ksp-app-store-desk: PASS (37 unitaires + 6 dependency + 15 desktop contract + 14 security + 1 public API)
cargo test -p ksp-store-lib: PASS
cargo check -p ksp-store-lib --no-default-features: PASS
```
`pre.011` n'ajoute aucune fonctionnalité runtime. La tranche ajoute un firewall de release completeness Store Desk :
- [X] `workspace.package.version = 0.3.8-pre.11` ;
- [X] inventaires directs Cargo exacts : build, runtime et dev dependencies Store Desk ;
- [X] inventaires npm exacts : runtime et dev dependencies, sans dépendance Select/DataTables parasite ;
- [X] inventaire exact des modules Rust `ksp-app-store-desk` ;
- [X] inventaire exact des dix commands Tauri ;
- [X] capability desktop exacte : fenêtres `splash/main`, permissions uniquement `core:default` et `tracing:default` ;
- [X] composition Config exacte `logging + store` pour `devnet/mainnet/testnet`, profil Logging `supertrace`, sans URI/credential/secret dans le composite ;
- [X] scan frontend hostile : aucun `fetch`/XHR/WebSocket/EventSource, aucun local/session storage/IndexedDB/cookie, aucun dialogue natif, aucune URI PostgreSQL/SQL/type backend physique ;
- [X] inventaire DTO visible exact et absence de `RawPageCursor`, backend physique ou source RAW bytes dans la surface IPC ;
- [X] rows Transaction/Account/Observation restent payload/data/source-byte-free ;
- [X] coexistence cursor + inspection verrouillée : exports `RawPageCursor`/`RawPageRequest` et `RawInspectionPageRequest` simultanés ;
- [X] statements PostgreSQL `LIST_*` historiques restent OFFSET-free ;
- [X] statements `INSPECT_*` restent random-access avec `OFFSET` ;
- [X] Store Desk runtime consomme `RawInspectionPageRequest` et ne consomme jamais `RawPageCursor` ;
- [X] aucun fichier Store API/façade/PostgreSQL/Config/runtime/frontend n'est modifié par `pre.011`.
Contrôles statiques d'assemblage : audits Rust/exports/Markdown propres avant création du delta. Le gate Cargo de `pre.011` reste à rejouer par l'opérateur.