---
title: 'Sandbox Usage'
source: 'https://academia.sh/en/courses/endpoint-security/sandbox-usage'
course: 'Endpoint and System Hardening'
language: en
updated: '2026-09-26T03:02:51+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Defines the sandbox as a run decision rather than a control and writes the number of points it closes as zero'
    - 'Separately counts behavior seen and not seen in the observation window and names the environment condition that is never provided'
    - "Calculates together the item that cannot be decided, the wrong decision, and the decision's time cost"
---

# Sandbox Usage

The sandbox is measured as a run decision: 15 of 18 suspicious items can run in isolation, four configurations raise observed behavior from 23 to 33, 5 items cannot be decided in any configuration, wrong allow and wrong block never change in any configuration, and closed foothold points are zero on every row.

The previous lesson decided whether an item could run by looking at path and signature; neither
criterion ever asked what the item **does**, and that left exactly the items it missed. The only
way to learn what an item does is to run it — but running it on the endpoint defeats the purpose
of the decision itself.

The **sandbox** is the answer to this bind: a suspicious item is run somewhere isolated from the
production environment and its behavior is observed. How strong the isolation is was measured in
the **Information Technology and Systems Fundamentals** course and is taken as an input here. What is measured is not
the isolation, it is the decision itself.

## Not a Control, a Decision

This lesson's result can be written in the first line: the sandbox closes no foothold point. A
startup entry, a service definition, or a scheduled task is in the same state after the sandbox is
set up as before. The only thing the sandbox produces is a **decision**, and that decision becomes
input to another control — the allowlist. It is written to only the second of the course's two
columns, the visibility column, and this lesson is that column's purest example.

The decision has three values. **Block**: a decisive behavior was seen in observation. **Allow**:
every behavior of the item was seen and none was decisive. **Uncertain**: the item ran but at
least one behavior was not seen, so its absence does not mean it does not exist. The third value is
this lesson's real subject; a two-valued decision table folds uncertain into allow and the
measurement breaks.

When a suspicious item is run, it may try to reach out over the network; the sandbox's controlled
network must never reach a real target, and examining an item belonging to another organization
without **written authorization and a defined scope** is both illegal and unprofessional.

- **SH25.** The eighteen-item queue, eight behavior classes, and four configurations are a
  **model**; no real item is run. Suspicious items are identified only by type and observed
  behavior class; what they are and how they arrived is not written.
- **SH26.** The strength of the isolation is not measured here; it is taken as an input.
- **SH27.** A behavior becoming visible is treated as depending on a single environment condition.
  In reality a behavior may need more than one condition; this grows the unseen column.
- **SH28.** Whether an item can run at all depends on a single required condition; if the
  condition is not provided, the item is never run.
- **SH29.** The observation-window and manual-review durations are written by hand; there is no
  randomness.
- **SH30.** The decisive flag belongs to the behavior, not the item. The same behavior can also be
  seen in a legitimate item, and the model marked it that way.

```js
// sandbox/queue.mjs — the run decision in a sandbox IS A MODEL. No real item is run;
// suspicious items are identified only by type and observed behavior class.
// A behavior becoming visible depends on the sandbox providing its environment condition.
export const BEHAVIOR = {
  'file-write': '-', 'process-creation': '-', 'persistence-definition': '-',
  'network-connection': 'network', 'credential-read': 'domain-identity',
  'scheduled-trigger': 'long-duration', 'user-action': 'user-interaction',
  'device-access': 'native-hardware',
};
// Configuration: environment conditions it provides, observation window (min), whether it needs an operator.
export const CONFIG = {
  isolated: { provides: [], window: 5, operator: false },
  'controlled-network': { provides: ['network'], window: 5, operator: false },
  'long-window': { provides: ['network', 'long-duration'], window: 60, operator: false },
  interactive: { provides: ['network', 'long-duration', 'user-interaction'], window: 60, operator: true },
};
export const MANUAL_MIN = 25;                 // manual review time per uncertain item
// row: name | type | condition required to run | actual | behaviors ('*' = decisive)
export const ITEM = `
  diagnostic-tool             binary -               legitimate  file-write,process-creation
  new-version-installer       binary -               legitimate  file-write,network-connection,*persistence-definition
  internal-helper-tool        binary -               legitimate  file-write,process-creation,network-connection
  meter-reading-tool          binary native-hardware  legitimate  device-access,file-write
  field-setup-script          script -               legitimate  file-write,*persistence-definition,user-action
  quarterly-report-script     script -               legitimate  file-write,network-connection
  internal-report-tool        binary -               legitimate  file-write,network-connection
  new-document-template       document -             legitimate  file-write,user-action
  vendor-archive               archive -             legitimate  file-write,process-creation
  domain-management-script    script domain-identity  legitimate  network-connection,*credential-read
  suspicious-item-1           binary -               malicious   file-write,process-creation,*persistence-definition
  suspicious-item-2           script -               malicious   file-write,network-connection,*credential-read
  suspicious-item-3           binary -               malicious   file-write,*scheduled-trigger
  suspicious-item-4           binary -               malicious   process-creation,*credential-read
  suspicious-item-5           document -             malicious   file-write,*user-action
  suspicious-item-6           binary -               malicious   file-write,process-creation
  suspicious-item-7           archive -              malicious   file-write,*network-connection
  suspicious-item-8           binary native-hardware  malicious   file-write,*device-access`
  .trim().split('\n').map((r) => {
    const [name, type, required, actual, beh] = r.trim().split(/\s+/);
    return { name, type, required, malicious: actual === 'malicious',
      behavior: beh.split(',').map((d) => ({ name: d.replace('*', ''), decisive: d.startsWith('*') })) };
  });

export const condition = (d) => BEHAVIOR[d.name];
export const visible = (d, c) => condition(d) === '-' || CONFIG[c].provides.includes(condition(d));
export const runs = (k, c) => k.required === '-' || CONFIG[c].provides.includes(k.required);

// A single item's decision in a configuration: block | allow | uncertain | no-run
export function decide(k, c) {
  if (!runs(k, c)) return 'no-run';
  const seen = k.behavior.filter((d) => visible(d, c));
  if (seen.some((d) => d.decisive)) return 'block';
  return seen.length === k.behavior.length ? 'allow' : 'uncertain';
}

export function scan(c) {
  const s = { ran: 0, didNotRun: 0, seen: 0, unseen: 0,
    block: 0, allow: 0, uncertain: 0, wrongAllow: 0, wrongBlock: 0 };
  for (const k of ITEM) {
    const d = decide(k, c);
    if (d === 'no-run') { s.didNotRun += 1; continue; }
    s.ran += 1;
    s.seen += k.behavior.filter((b) => visible(b, c)).length;
    s.unseen += k.behavior.filter((b) => !visible(b, c)).length;
    s[d] += 1;
    if (d === 'allow' && k.malicious) s.wrongAllow += 1;
    if (d === 'block' && !k.malicious) s.wrongBlock += 1;
  }
  s.observeMin = s.ran * CONFIG[c].window;
  s.operatorMin = CONFIG[c].operator ? s.observeMin : 0;
  s.manualMin = (s.uncertain + s.didNotRun) * MANUAL_MIN;
  return s;
}
```

## Four Configurations, the Same Queue

```js
// sandbox/measure.mjs — four sandbox configurations evaluated against the same suspicious-item queue
import { BEHAVIOR, CONFIG, ITEM, MANUAL_MIN, condition, visible, runs, decide, scan } from './queue.mjs';

const row = (g, ...s) => console.log(s.map((v, i) =>
  (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join('').trimEnd());
const neverProvided = [...new Set(Object.values(BEHAVIOR))]
  .filter((c) => c !== '-' && !Object.values(CONFIG).some((v) => v.provides.includes(c)));

console.log(`${ITEM.length} suspicious items, ${ITEM.filter((k) => k.malicious).length} malicious; ` +
  `${Object.keys(BEHAVIOR).length} behavior classes, ${Object.keys(CONFIG).length} configurations`);
console.log(`condition never provided by any configuration: ${neverProvided.join(', ')}`);

const A = [-21, 9, 12, 10, 12, 9, 7, 11, 13, 14];
console.log('\n1. result per configuration');
row(A, 'configuration', 'ran', 'no-run', 'seen', 'unseen', 'block', 'allow',
  'uncertain', 'wrong allow', 'wrong block');
for (const c of Object.keys(CONFIG)) {
  const s = scan(c);
  row(A, c, s.ran, s.didNotRun, s.seen, s.unseen, s.block, s.allow, s.uncertain,
    s.wrongAllow, s.wrongBlock);
}

const B = [-27, 12, 14, 21, 14, 14];
console.log('\n2. decision per item');
row(B, 'item', 'actual', ...Object.keys(CONFIG));
for (const k of ITEM)
  row(B, k.name, k.malicious ? 'malicious' : 'legitimate', ...Object.keys(CONFIG).map((c) => decide(k, c)));

const C = [-21, 9, 13, 14, 12, 11, 15];
console.log('\n3. decision time (min) and closed foothold points');
row(C, 'configuration', 'window', 'observe min', 'operator min', 'manual min', 'total min', 'closed points');
for (const c of Object.keys(CONFIG)) {
  const s = scan(c);
  row(C, c, CONFIG[c].window, s.observeMin, s.operatorMin, s.manualMin,
    s.observeMin + s.operatorMin + s.manualMin, 0);
}
const u = scan('long-window'), e = scan('interactive');
console.log(`\nitems falling to manual review (long-window)   ${u.uncertain + u.didNotRun} / ${ITEM.length}`);
console.log(`undecided in every configuration                ${e.uncertain + e.didNotRun} / ${ITEM.length}`);
console.log(`  malicious items unstoppable in the best config  ` +
  `${ITEM.filter((k) => k.malicious && decide(k, 'interactive') !== 'block').length}`);
console.log(`wrong allow / wrong block in every configuration ${e.wrongAllow} / ${e.wrongBlock}`);
console.log(`manual review time                               ${MANUAL_MIN} min/item`);
```

```
18 suspicious items, 8 malicious; 8 behavior classes, 4 configurations
condition never provided by any configuration: domain-identity, native-hardware

1. result per configuration
configuration              ran      no-run      seen      unseen    block  allow  uncertain  wrong allow   wrong block
isolated                    15           3        23          12        3      3          9            1             2
controlled-network          15           3        29           6        4      6          5            1             2
long-window                 15           3        30           5        5      6          4            1             2
interactive                 15           3        33           2        6      7          2            1             2

2. decision per item
item                             actual      isolated   controlled-network   long-window   interactive
diagnostic-tool              legitimate         allow                allow         allow         allow
new-version-installer        legitimate         block                block         block         block
internal-helper-tool         legitimate     uncertain                allow         allow         allow
meter-reading-tool           legitimate        no-run               no-run        no-run        no-run
field-setup-script           legitimate         block                block         block         block
quarterly-report-script      legitimate     uncertain                allow         allow         allow
internal-report-tool         legitimate     uncertain                allow         allow         allow
new-document-template        legitimate     uncertain            uncertain     uncertain         allow
vendor-archive               legitimate         allow                allow         allow         allow
domain-management-script     legitimate        no-run               no-run        no-run        no-run
suspicious-item-1             malicious         block                block         block         block
suspicious-item-2             malicious     uncertain            uncertain     uncertain     uncertain
suspicious-item-3             malicious     uncertain            uncertain         block         block
suspicious-item-4             malicious     uncertain            uncertain     uncertain     uncertain
suspicious-item-5             malicious     uncertain            uncertain     uncertain         block
suspicious-item-6             malicious         allow                allow         allow         allow
suspicious-item-7             malicious     uncertain                block         block         block
suspicious-item-8             malicious        no-run               no-run        no-run        no-run

3. decision time (min) and closed foothold points
configuration           window  observe min  operator min  manual min  total min  closed points
isolated                     5           75             0         300        375              0
controlled-network           5           75             0         200        275              0
long-window                 60          900             0         175       1075              0
interactive                 60          900           900         125       1925              0

items falling to manual review (long-window)   7 / 18
undecided in every configuration                5 / 18
  malicious items unstoppable in the best config  4
wrong allow / wrong block in every configuration 1 / 2
manual review time                               25 min/item
```

## Observed and Unobserved Behavior

15 of eighteen items can be run, 3 cannot be run at all. Two of the ones that cannot run are items
requiring native hardware — the meter-reading tool and a suspicious binary — and one is a
management script requiring a domain identity. The sandbox says nothing about these three items; a
sixth of the queue falls to manual review before any observation happens at all.

Loosening the isolation increases observed behavior: from 23 to 33, unobserved from 12 to 2. But
what is being loosened is exactly the isolation itself. Adding a controlled network makes six more
behaviors visible; extending the observation window from five minutes to sixty adds only one
behavior; operator interaction adds three behaviors. The gains are not equal to each other, and
neither are the costs.

Two conditions are never provided in any configuration: **domain identity** and **native
hardware**. The first cannot be provided because giving the sandbox a real domain identity
eliminates the isolation — everything reachable with that identity enters the sandbox. The second
cannot be provided because the hardware is not there. These two conditions are not a
configuration gap; they are the method's limit: as long as the sandbox is not the production
environment itself, behavior tied to the production environment stays invisible, and the moment it
is the production environment, it stops being a sandbox.

## The Item Left Undecided

The second table shows how decisions change from configuration to configuration. In the isolated
configuration, 9 of the 15 running items stay uncertain; in the most capable configuration that
number drops to 2. But three values stay the same: wrong allow at 1, wrong block at 2, and items
that cannot run at 3.

**Wrong allow** is a single item, and it does not get fixed in any configuration. All of that
item's behaviors are visible in observation — file write and process creation — and neither is
decisive. Extending the observation window, adding the network, or seating an operator changes
nothing for this item, because what is missing is not observation, it is the distinction itself. If
an item's sandbox behavior cannot be told apart from a legitimate item's, the sandbox cannot produce
a decision about it.

**Wrong block** is two items, and the reason is the same, reversed. The new-version installer and
the field setup script both write a persistence definition — the very thing this course measures,
a foothold-point definition. A decisive behavior is seen and the decision comes out "block." Both
are legitimate work. The sandbox sees the behavior, not **who did it under what authority**;
writing a persistence definition during installation is an installer's job.

Five items cannot be decided in any configuration, and **three are malicious**. Three come from
items that cannot be run, two from items with a behavior requiring domain identity. Of the queue's
eight malicious items, only four are stopped by the sandbox; of the remaining four, three get no
answer from the sandbox at all — the question is handed to manual review — and the fourth passes
with a wrong **allow** decision in all four configurations and never falls to review.

## The Cost of the Decision

The third table gives the cost, and it is not one-directional. Observation time is 75 minutes in
the isolated configuration, 900 in the long window. Manual review time goes the other way: from
300 minutes down to 125. Added together, the cheapest configuration turns out to be **controlled
network** — 275 minutes — and the most expensive is interactive, 1925 minutes. The gap is exactly
sevenfold, and in return uncertain items drop from 5 to 2 while wrong decisions do not change at
all.

This is the number that determines where the decision stops. Adding a controlled network to a
five-minute window raises observed behavior from 23 to 29 and **lowers** total time from 375 to
275, because fewer items fall to manual review. Extending the window to sixty minutes, on the
other hand, gains a single behavior and quadruples the time. The cheap step is adding the network;
the expensive one is extending the duration.

There is another cost the table does not show: the sandbox itself needs maintenance. For observation
to work, the environment has to resemble production endpoints — the same software set, the same
versions, the same configuration — and the more it resembles them, the less isolation there is.
Every time the endpoints' baseline changes, the sandbox image has to be updated too; when it
is not, an item may show no behavior at all because it does not recognize the environment it is
running in, and the decision silently drifts to "allow." Add to this the inventory of the machines
that run the sandbox itself: they are endpoints too, and they go into this course's first-lesson
count.

The last column is zero in every row. No matter how many more behaviors one configuration reveals
over another, the number of closed foothold points does not change. The decision the sandbox
produces either adds a row to the allowlist or it does not; what closes the point is that row, not
the sandbox.

## Summary

- The sandbox is not a control but a run decision: closed foothold points are zero across all four
  configurations. The decision it produces becomes input to the allowlist, and the point closes
  there.
- The decision is three-valued; if uncertain is not kept separate, the measurement breaks. In the
  isolated configuration, 9 of the 15 running items are uncertain; in the most capable
  configuration, 2.
- Two environment conditions can never be provided in any configuration — domain identity and
  native hardware. Providing the first eliminates the isolation; this is not a gap, it is the
  method's limit.
- Wrong allow (1) and wrong block (2) never change in any configuration. Wrong block happens
  because a legitimate installer also writes a persistence definition: the sandbox sees the
  behavior, not the authority.
- Only four of the eight malicious items are stopped by the sandbox; three cannot be decided and go
  to manual review, and one passes with a wrong allow decision in all four configurations.
- Time runs in two directions: adding a controlled network lowers total time from 375 minutes to
  275; extending the window to sixty minutes gains one behavior and pushes time to 1075 minutes.

## Next Step

Everything counted up to here held while the machine was on and an operating system was running.
The inventory was pulled from inside that system, the allowlist audited that system's run
decisions, the sandbox ran an item on a copy of that system. Every one of them shares the same
assumption: the only way to reach the machine is through the running system.

The next lesson removes that assumption. When the disk is pulled from the machine and mounted
somewhere else, the running system drops out of the picture, and every control drops with it: the
allowlist performs no evaluation, the hardening baseline is not read, no log is kept. What gets
measured is this: how many items can be read with physical access on an unencrypted and an
encrypted disk, which foothold-point class full disk encryption closes, and, item by item, what
encryption **does not** protect while the machine is on, a session is open, or the screen is
locked. The topic closes with this measurement.
