Add validation limits
This commit is contained in:
@@ -1,19 +1,31 @@
|
||||
Seed handling and input hardening (deferred from the B-list fixes)
|
||||
=====================================================================
|
||||
|
||||
- Validate ranSeed in the GUI and startSequence: it must be an integer in a
|
||||
safe range (e.g. -2^31 .. 2^31-1) if a fixed seed is requested. Blocks a
|
||||
crafted share link from injecting a garbage seed value into the input deck.
|
||||
- Derive per-scan-point seeds deterministically from the base seed and the
|
||||
DONE - Validate ranSeed in the GUI and startSequence: it must be an integer in a
|
||||
safe range (|v| <= 2^31-1, matching ISEED = IDINT(RI) in trimspNL.F). Enforced
|
||||
in runSimulationSequence() (before any job is generated) AND in CreateInpFile()
|
||||
(defense-in-depth), so a crafted share link or direct deck builder is blocked
|
||||
with a "please change it" message. Non-numeric / NaN / non-integer / oversized
|
||||
seeds no longer reach the engine.
|
||||
DONE - Derive per-scan-point seeds deterministically from the base seed and the
|
||||
scan index, so a multi-point run is reproducible in any finish order
|
||||
(currently: initialSeed + iScan, but only when the user set a seed).
|
||||
- Warn when a shared configuration fixed the RanSeed: every scan point then
|
||||
uses the same generator, which can produce statistically misleading results.
|
||||
- Cap client-controlled magnitudes: numberProj (projectile count) and
|
||||
valEnergy must be upper-bounded from share links before the engine runs, to
|
||||
prevent an accidental or malicious "run forever / write enormous .rge" job.
|
||||
- Consider persisting the effective per-point seed back into the *.seq output
|
||||
metadata so results self-document their RNG state.
|
||||
(currently: initialSeed + iScan, only when the user set a valid seed).
|
||||
DONE - Cap valEnergy magnitude at 60000 eV (positive incidence mode and the
|
||||
Maxwellian transmission mode |E0| = temperature). Stopping fits (KDEE1=4) are
|
||||
documented valid to ~50 keV for helium (meaning-of-params.txt:177-185), so 60
|
||||
keV is the model's fitted ceiling. Applied to scanned energies and the base
|
||||
value in runSimulationSequence() and CreateInpFile().
|
||||
- Warn when a shared configuration fixed the RanSeed: every scan point then uses
|
||||
the same generator, which can produce statistically misleading results.
|
||||
- Cap client-controlled numberProj magnitude: an unbounded projectile count (e.g.
|
||||
1e9) does not terminate within the 300 s watchdog and can flood scan points
|
||||
(empirically verified: 1e9 did not finish in 15 s). GUI ProjNumberLimit() is
|
||||
currently dead code (defined at TrimSPlib.js:3516, never wired to an event).
|
||||
- Persisting the effective per-point seed: the seed already lives in each
|
||||
archived *.inp (all three deck slots), so reproducibility does not require the
|
||||
*.seq header change; extending *.seq would shift the 999(A20,1x) layer slots
|
||||
and break getSequenceColumnInfo(). Surfacing it in run details instead is
|
||||
deferred - not sure it is needed.
|
||||
|
||||
Deferred follow-ups from the C-list CGI hardening
|
||||
=================================================
|
||||
|
||||
@@ -3634,6 +3634,16 @@ function setValues(content) {
|
||||
|
||||
|
||||
function CreateInpFile() {
|
||||
// Gate client-controlled magnitudes here too so a crafted config can never
|
||||
// reach the engine, regardless of which caller builds the deck.
|
||||
var inpSeedError = validateSeedString(All["ranSeed"]);
|
||||
if (inpSeedError) {
|
||||
throw new Error(inpSeedError);
|
||||
}
|
||||
var inpEnergyError = validateEnergyValue(All["valEnergy"]);
|
||||
if (inpEnergyError) {
|
||||
throw new Error(inpEnergyError);
|
||||
}
|
||||
// Create and return an input deck for the TRIM.SP-NL transport engine.
|
||||
// Takes object All as input for all relevant parameters
|
||||
|
||||
@@ -3781,6 +3791,45 @@ var TRIMSP_WEB_MAX_CONCURRENCY = 4;
|
||||
// an effectively unlimited list of jobs and freeze the tab.
|
||||
var TRIMSP_MAX_SCAN_POINTS = 5000;
|
||||
|
||||
// Validated limits for client-owned input values. The Ziegler/ICRU stopping
|
||||
// fits are only tuned up to ~50 keV for helium (KDEE1=4, see
|
||||
// meaning-of-params.txt:177-185), so energies beyond 60 keV are outside the
|
||||
// model's fitted validity. Negative energies select the Maxwellian initial
|
||||
// distribution where |E0| is used as the ion temperature (trimspNL.F:806), so
|
||||
// the same magnitude cap applies to both modes.
|
||||
var TRIMSP_ENERGY_CAP_EV = 60000;
|
||||
|
||||
// The engine casts the deck seed to a signed 32-bit integer
|
||||
// (ISEED = IDINT(RI), trimspNL.F:818-827), so any magnitude beyond that is
|
||||
// silently truncated. Restrict the seed to |v| <= 2^31-1.
|
||||
var TRIMSP_SEED_MAX_ABS = 2147483647;
|
||||
|
||||
// Returns an empty string when valid, otherwise a message that asks the user
|
||||
// to change the offending value.
|
||||
function validateEnergyValue(raw) {
|
||||
var str = String(raw).trim();
|
||||
var value = Number(str);
|
||||
if (str === "" || !Number.isFinite(value)) {
|
||||
return "Energy must be numeric. Please change it.";
|
||||
}
|
||||
if (Math.abs(value) > TRIMSP_ENERGY_CAP_EV) {
|
||||
return "Energy magnitude must not exceed 60000 eV (the stopping fits are not validated beyond ~50 keV). Please change it.";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function validateSeedString(raw) {
|
||||
var str = String(raw).trim();
|
||||
if (str === "" || !/^[+-]?\d+$/.test(str)) {
|
||||
return "Seed must be an integer. Please change it.";
|
||||
}
|
||||
var value = Number(str);
|
||||
if (Math.abs(value) > TRIMSP_SEED_MAX_ABS) {
|
||||
return "Seed is out of range (use an integer between -2147483648 and 2147483647). Please change it.";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Expand a From/Step/To range into a list of scan values. Both ascending and
|
||||
// descending steps are valid (e.g. scanning from high to low energy), so the
|
||||
// old "loop while value <= to" form is replaced by a count-based expansion
|
||||
@@ -3995,12 +4044,29 @@ async function runSimulationSequence() {
|
||||
return(0);
|
||||
}
|
||||
|
||||
// Cap the projectile energy magnitude before any job is generated. For an
|
||||
// energy sweep the scan values are authoritative; for any other sweep the
|
||||
// base valEnergy must still stay in the fitting range.
|
||||
var evalues = (ScanAttrib === "valEnergy") ? SValues : [Number(All["valEnergy"])];
|
||||
for (var evIndex = 0; evIndex < evalues.length; evIndex++) {
|
||||
var energyError = validateEnergyValue(evalues[evIndex]);
|
||||
if (energyError) {
|
||||
updateStatusBar({ run: energyError });
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Freeze the initial GUI state and expand the scan into independent jobs.
|
||||
// Each job gets its own input file name and a deterministic seed offset so
|
||||
// the results are reproducible even when runs finish out of order.
|
||||
const scanValues = SValues.slice();
|
||||
const initialAll = Object.assign({}, All);
|
||||
const initialSeed = parseInt(initialAll["ranSeed"], 10);
|
||||
const seedError = validateSeedString(initialAll["ranSeed"]);
|
||||
if (seedError) {
|
||||
updateStatusBar({ run: seedError });
|
||||
return(0);
|
||||
}
|
||||
let jobs = [];
|
||||
|
||||
for (let iScan = 0; iScan < scanValues.length; iScan++) {
|
||||
|
||||
Reference in New Issue
Block a user