Files
f0d695ec97
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (push) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / node-8 (push) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-business (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / original-voyage (push) Skipped
QRun-IO/ci-policy: ci / original-cli (push) Skipped
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Failing after 7s
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
QRun-IO/ci-policy: ci / ci-result (push) Failing after 6s
QRun-IO/ci-policy: native / native-result (push) Successful in 6s
chore: preserve paused QBit work and document remaining issues
Save the current implementation and README, status, and known-issue documentation at the user-requested stop. No completed release or deployment is claimed.

Co-Authored-By: Claude <noreply@anthropic.com>

Co-Authored-By: GPT-6 Codex <noreply@openai.com>
2026-11-04 10:45:28 -06:00

251 lines
11 KiB
Rust

/* Copyright (C) 2026 Kingsrook, LLC.
* SPDX-License-Identifier: Apache-2.0
*/
//! Fixed trusted compatibility cases. These are not hostile-runtime qualification.
#![allow(clippy::expect_used)]
use std::cell::Cell;
use std::rc::Rc;
use std::thread;
use std::time::Duration;
use qqq_javy_runtime::{Failure, process};
use serde_json::{Value, json};
use sha2::{Digest, Sha256};
/// Synthetic exact runtime identity; an actual supervisor binds its loaded binary digest.
fn runtime_id() -> String {
"ab".repeat(32)
}
/// Prepare only fixed test module sources; this is not TypeScript compilation.
fn prepare(files: Vec<(&str, &str)>) -> Value {
prepare_result(files).expect("trusted preparation")
}
/// Keep compiler refusals observable without relaxing the trusted cache boundary.
fn prepare_result(files: Vec<(&str, &str)>) -> Result<Value, Failure> {
let bundle = json!({"format":"qqq-esm/1","entryPoint":"main.js","files":files.into_iter().map(|(path,content)|json!({"path":path,"content":content})).collect::<Vec<_>>()}).to_string();
let input = json!({"operation":"prepare","runtimeSha256":runtime_id(),"artifactSha256":format!("{:x}",Sha256::digest(bundle.as_bytes())),"bundle":bundle});
let output = process(input.to_string().as_bytes(), |_| panic!("prepare must not broker"))?;
Ok(serde_json::from_slice(&output).expect("prepared JSON"))
}
/// Create the exact hot request shape used by the persistent transport, without bundle.
fn request(prepared: &Value) -> Value {
json!({"protocol":"qbit-scripting-worker/1","type":"execute","requestId":"trusted-test","artifactId":"fixed",
"artifactSha256":prepared["artifactSha256"],"entryPoint":"main.ts","event":"CONTEXT","payload":{"actor":"alpha"},"capabilityToken":"synthetic-only",
"limits":{"wallMillis":2000,"cpuMillis":1000,"memoryBytes":64*1024*1024,"outputBytes":65536,"brokerCalls":2}})
}
/// Invoke through the public private-envelope boundary.
fn run(prepared: &Value, request: Value, broker: impl Fn(&[u8]) -> Result<Vec<u8>, Failure> + 'static) -> Result<Value, Failure> {
let input = json!({"operation":"run","runtimeSha256":runtime_id(),"preparedSha256":prepared["preparedSha256"],"prepared":prepared,"request":request});
let output = process(input.to_string().as_bytes(), broker)?;
Ok(serde_json::from_slice(&output).expect("result JSON"))
}
/// Exact existing broker response, containing only synthetic data.
fn allow(bytes: &[u8]) -> Result<Vec<u8>, Failure> {
let request: Value = serde_json::from_slice(bytes).expect("broker request");
assert_eq!(request["operation"], "query");
assert_eq!(request["token"], "synthetic-only");
Ok(
json!({"protocol":"qbit-scripting-worker/1","type":"brokerResult","ok":true,"rows":[{"id":7}],"truncated":false})
.to_string()
.into_bytes(),
)
}
/// Preparation must not execute module top-level effects or throw statements.
#[test]
fn preparation_does_not_evaluate_author_module() {
let prepared = prepare(vec![(
"main.js",
"throw new Error('fixed top level'); export function main(){return {ok:true}}",
)]);
let result = run(&prepared, request(&prepared), |_| panic!("no broker")).expect("bounded result");
assert_eq!(result["message"]["outcome"], "SCRIPT_ERROR");
}
/// Java's artifact character limit is not an ASCII-only byte limit.
#[test]
fn unicode_artifact_above_one_megabyte_remains_compatible() {
let source = format!(
"const marker='{}'; export function main(){{return {{length:marker.length}}}}",
"界".repeat(400_000)
);
assert!(source.len() > 1024 * 1024);
assert!(source.encode_utf16().count() < 1024 * 1024);
let prepared = prepare(vec![("main.js", source.as_str())]);
let result = run(&prepared, request(&prepared), |_| panic!("no broker")).expect("bounded Unicode result");
assert_eq!(result["message"]["outcome"], "OK");
assert_eq!(result["message"]["result"]["length"], 400_000);
}
/// Separate fixed module, await and broker cases localize compatibility failures.
#[test]
fn fixed_compatibility_boundaries() {
for (name, source) in [
("relative", "import {id} from './state.js'; export function main(){return {id}}"),
("tla", "await Promise.resolve(); export function main(){return {id:7}}"),
(
"broker",
"export async function main(c,q){const rows=await q.query('purchaseOrder',['id']);return {id:rows[0].id}}",
),
] {
let prepared = prepare(vec![("main.js", source), ("state.js", "export const id=7;")]);
let result = run(&prepared, request(&prepared), allow).expect("fixed boundary result");
assert_eq!(result["message"]["outcome"], "OK", "boundary: {name}");
assert_eq!(result["message"]["result"]["id"], 7);
}
}
/// Bytecode loading restores strict runtime import checks after nested module reads.
#[test]
fn nested_bytecode_and_dynamic_imports_preserve_attribute_policy() {
for attributes in ["", ", {with:{type:'json'}}"] {
let source = format!(
"import {{id}} from './nested/state.js'; export async function main(){{const extra=await import('./extra.js'{attributes});return {{id,extra:extra.id}}}}"
);
let prepared = prepare(vec![
("main.js", &source),
("nested/state.js", "export {id} from '../value.js';"),
("value.js", "export const id=7;"),
("extra.js", "export const id=8;"),
]);
let result = run(&prepared, request(&prepared), |_| panic!("no broker")).expect("bounded dynamic result");
assert_eq!(result["message"]["outcome"], if attributes.is_empty() { "OK" } else { "SCRIPT_ERROR" });
if attributes.is_empty() {
assert_eq!(result["message"]["result"], json!({"id":7,"extra":8}));
}
}
}
/// Attributed source imports never enter the private compiled-bytecode cache.
#[test]
fn attributed_static_import_is_refused_at_preparation() {
for attributed_path in ["main.js", "unused.js"] {
let mut files = vec![
(
attributed_path,
"import {id} from './state.js' with {type:'json'}; export function main(){return {id}}",
),
("state.js", "export const id=7;"),
];
if attributed_path != "main.js" {
files.push(("main.js", "export function main(){return {ok:true}}"));
}
assert_eq!(prepare_result(files).expect_err("unsupported import attributes"), Failure::Compile);
}
}
/// Relative modules, live bindings, TLA, async main and Promise broker remain observable.
#[test]
fn fresh_modules_time_random_and_async_broker() {
let prepared = prepare(vec![
(
"state.js",
"export let count=0; export const stamp=Date.now(); export const random=Math.random(); export function bump(){count++}",
),
(
"main.js",
"import {count,stamp,random,bump} from './state.js'; await Promise.resolve(); export async function main(context,qqq){const rows=await qqq.query('purchaseOrder',['id']); bump(); return {count,stamp,random,id:rows[0].id,actor:context.payload.actor}}",
),
]);
let first = run(&prepared, request(&prepared), allow).expect("first invocation");
thread::sleep(Duration::from_millis(5));
let mut second_request = request(&prepared);
second_request["payload"]["actor"] = json!("beta");
let second = run(&prepared, second_request, allow).expect("second invocation");
assert_eq!(first["message"]["outcome"], "OK");
assert_eq!(second["message"]["outcome"], "OK");
let a = &first["message"]["result"];
let b = &second["message"]["result"];
assert_eq!(a["count"], 1);
assert_eq!(b["count"], 1);
assert_eq!(a["id"], 7);
assert_eq!(b["actor"], "beta");
assert_ne!(a["stamp"], b["stamp"]);
assert_ne!(a["random"], b["random"]);
}
/// Pending promises cannot be treated as successful completion.
#[test]
fn idle_pending_main_is_refused() {
let prepared = prepare(vec![("main.js", "export function main(){return new Promise(()=>{})}")]);
let result = run(&prepared, request(&prepared), |_| panic!("no broker")).expect("bounded result");
assert_eq!(result["message"]["outcome"], "SCRIPT_ERROR");
}
/// A second unawaited SDK call is refused by the existing one-pending-call rule.
#[test]
fn simultaneous_broker_calls_are_refused() {
let prepared = prepare(vec![(
"main.js",
"export async function main(c,q){await Promise.all([q.query('purchaseOrder',['id']),q.query('purchaseOrder',['id'])]);return {ok:true}}",
)]);
let calls = Rc::new(Cell::new(0));
let observed = Rc::clone(&calls);
let result = run(&prepared, request(&prepared), move |bytes| {
observed.set(observed.get() + 1);
allow(bytes)
})
.expect("bounded result");
assert_eq!(result["message"]["outcome"], "SCRIPT_ERROR");
assert!(calls.get() <= 1);
}
/// Current broker refusal is not converted into an authorized row result.
#[test]
fn broker_denial_is_generic() {
let prepared = prepare(vec![(
"main.js",
"export async function main(c,q){return {rows:await q.query('purchaseOrder',['id'])}}",
)]);
let result = run(&prepared, request(&prepared), |_| Err(Failure::Broker)).expect("bounded result");
assert_eq!(result["message"]["outcome"], "SCRIPT_ERROR");
assert_eq!(result["message"]["error"]["message"], "Script execution failed");
}
/// Cached code is bound to both publication bytes and runtime identity before execution.
#[test]
fn altered_bytecode_or_runtime_never_brokers() {
let prepared = prepare(vec![("main.js", "export function main(){return {ok:true}}")]);
let mut changed = prepared.clone();
changed["modules"][0]["bytecode"] = json!("AA==");
assert_eq!(
run(&changed, request(&prepared), |_| panic!("must not broker")).expect_err("changed bytes"),
Failure::Identity
);
changed = prepared.clone();
changed["runtimeSha256"] = json!("cd".repeat(32));
assert_eq!(
run(&changed, request(&prepared), |_| panic!("must not broker")).expect_err("changed runtime"),
Failure::Identity
);
}
/// Author serialization callbacks cannot create an unchecked detached rejection.
#[test]
fn detached_rejection_during_result_serialization_is_refused() {
let prepared = prepare(vec![(
"main.js",
"export function main(){return {toJSON(){Promise.reject(new Error('fixed trusted rejection'));return {ok:true}}}}",
)]);
let result = run(&prepared, request(&prepared), |_| panic!("no broker")).expect("bounded serialization result");
assert_eq!(result["message"]["outcome"], "SCRIPT_ERROR");
}
/// Return values are bounded plain objects and preserve declared output limits.
#[test]
fn output_limit_and_array_results_refused() {
for source in ["export function main(){return {text:'x'.repeat(200)}}", "export function main(){return [1,2]}"] {
let prepared = prepare(vec![("main.js", source)]);
let mut input = request(&prepared);
input["limits"]["outputBytes"] = json!(64);
let result = run(&prepared, input, |_| panic!("no broker")).expect("bounded result");
assert_eq!(result["message"]["outcome"], "SCRIPT_ERROR");
}
}