or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdjava-integration.mdjavascript-objects.mdscript-execution.mdsecurity-debugging.md

security-debugging.mddocs/

0

# Security and Debugging

1

2

Comprehensive security framework and debugging capabilities for production JavaScript execution environments, including access control, sandboxing, error handling, and runtime introspection.

3

4

## Capabilities

5

6

### Security Framework

7

8

#### ClassShutter

9

10

Controls which Java classes are accessible from JavaScript code, providing fine-grained access control.

11

12

```java { .api }

13

/**

14

* Interface for controlling Java class access from JavaScript

15

* Prevents access to sensitive or dangerous Java classes

16

*/

17

public interface ClassShutter {

18

/**

19

* Determines if a Java class should be accessible from JavaScript

20

* @param fullClassName Fully qualified Java class name

21

* @return true if class should be accessible, false otherwise

22

*/

23

boolean visibleToScripts(String fullClassName);

24

}

25

```

26

27

**Usage Examples:**

28

29

```java

30

// Restrictive ClassShutter implementation

31

ClassShutter restrictiveShutter = new ClassShutter() {

32

@Override

33

public boolean visibleToScripts(String fullClassName) {

34

// Allow only safe classes

35

return fullClassName.startsWith("java.lang.String") ||

36

fullClassName.startsWith("java.lang.Math") ||

37

fullClassName.startsWith("java.util.Date");

38

}

39

};

40

41

// Set ClassShutter on Context

42

cx.setClassShutter(restrictiveShutter);

43

44

// Now JavaScript can only access allowed classes

45

try {

46

cx.evaluateString(scope, "java.lang.System.exit(0);", "test", 1, null);

47

// This will fail - System class not accessible

48

} catch (EvaluatorException e) {

49

System.out.println("Access denied: " + e.getMessage());

50

}

51

52

// Whitelist-based ClassShutter

53

Set<String> allowedClasses = Set.of(

54

"java.lang.String",

55

"java.lang.Math",

56

"java.util.ArrayList",

57

"java.util.HashMap"

58

);

59

60

ClassShutter whitelistShutter = allowedClasses::contains;

61

cx.setClassShutter(whitelistShutter);

62

```

63

64

#### SecurityController

65

66

Provides comprehensive security control over script execution with customizable policies.

67

68

```java { .api }

69

/**

70

* Controls script execution security and access permissions

71

* Provides integration with Java security manager

72

*/

73

public abstract class SecurityController {

74

/**

75

* Gets the global SecurityController instance

76

* @return Global SecurityController or null if none set

77

*/

78

public static SecurityController global();

79

80

/**

81

* Checks if global SecurityController is installed

82

* @return true if global controller exists

83

*/

84

public static boolean hasGlobal();

85

86

/**

87

* Sets the global SecurityController instance

88

* @param controller SecurityController to install globally

89

*/

90

public static void initGlobal(SecurityController controller);

91

92

/**

93

* Creates call context for secure execution

94

* @param cx Current Context

95

* @param callable Callable object

96

* @param scope Execution scope

97

* @param thisObj 'this' object

98

* @param args Call arguments

99

* @return Execution result

100

*/

101

public abstract Object callWithDomain(Object securityDomain, Context cx, Callable callable,

102

Scriptable scope, Scriptable thisObj, Object[] args);

103

104

/**

105

* Gets current security domain

106

* @param cx Current Context

107

* @return Current security domain object

108

*/

109

public abstract Object getDynamicSecurityDomain(Object securityDomain);

110

111

/**

112

* Gets class loader for generated classes

113

* @param cx Current Context

114

* @param securityDomain Security domain

115

* @return ClassLoader for generated classes

116

*/

117

public abstract ClassLoader createClassLoader(ClassLoader parent, Object securityDomain);

118

}

119

120

/**

121

* SecurityController implementation using Java security policy

122

* Integrates with Java AccessController and Policy system

123

*/

124

public class PolicySecurityController extends SecurityController {

125

/**

126

* Creates PolicySecurityController instance

127

*/

128

public PolicySecurityController();

129

}

130

```

131

132

**Usage Examples:**

133

134

```java

135

// Install security controller

136

PolicySecurityController securityController = new PolicySecurityController();

137

SecurityController.initGlobal(securityController);

138

139

// Execute scripts with security domain

140

Object securityDomain = "trusted-domain";

141

Context cx = Context.enter();

142

try {

143

cx.setSecurityController(securityController);

144

// Scripts compiled with this domain will have restricted permissions

145

Script script = cx.compileString("java.lang.System.getProperty('user.home')",

146

"test", 1, securityDomain);

147

Object result = script.exec(cx, scope);

148

} finally {

149

Context.exit();

150

}

151

```

152

153

#### Safe Standard Objects

154

155

Initialize JavaScript environment without Java class access for sandboxed execution.

156

157

```java { .api }

158

/**

159

* Initialize safe JavaScript environment without Java access

160

* Prevents JavaScript code from accessing Java packages and classes

161

*/

162

public ScriptableObject initSafeStandardObjects();

163

164

/**

165

* Initialize safe environment with custom scope object

166

* @param scope Scope object to initialize

167

* @return Initialized safe scope

168

*/

169

public Scriptable initSafeStandardObjects(ScriptableObject scope);

170

```

171

172

**Usage Examples:**

173

174

```java

175

// Create safe JavaScript environment

176

Context cx = Context.enter();

177

try {

178

// Safe scope - no Java access

179

Scriptable safeScope = cx.initSafeStandardObjects();

180

181

// This will work - standard JavaScript

182

Object result1 = cx.evaluateString(safeScope,

183

"Math.max(1, 2, 3)", "safe", 1, null);

184

185

// This will fail - no Java access

186

try {

187

cx.evaluateString(safeScope,

188

"java.lang.System.getProperty('user.name')", "unsafe", 1, null);

189

} catch (EvaluatorException e) {

190

System.out.println("Java access blocked: " + e.getMessage());

191

}

192

} finally {

193

Context.exit();

194

}

195

196

// Custom safe scope with additional restrictions

197

ScriptableObject customScope = cx.initSafeStandardObjects();

198

// Remove potentially dangerous global functions

199

customScope.delete("eval"); // Disable eval

200

customScope.delete("Function"); // Disable Function constructor

201

```

202

203

### Error Handling and Reporting

204

205

#### ErrorReporter Interface

206

207

Handles compilation and runtime errors with customizable error processing.

208

209

```java { .api }

210

/**

211

* Interface for reporting JavaScript errors and warnings

212

* Allows custom error handling and logging

213

*/

214

public interface ErrorReporter {

215

/**

216

* Reports a warning during compilation or execution

217

* @param message Warning message

218

* @param sourceName Source file name

219

* @param line Line number where warning occurred

220

* @param lineSource Source code line

221

* @param lineOffset Column offset in line

222

*/

223

void warning(String message, String sourceName, int line, String lineSource, int lineOffset);

224

225

/**

226

* Reports an error during compilation or execution

227

* @param message Error message

228

* @param sourceName Source file name

229

* @param line Line number where error occurred

230

* @param lineSource Source code line

231

* @param lineOffset Column offset in line

232

*/

233

void error(String message, String sourceName, int line, String lineSource, int lineOffset);

234

235

/**

236

* Reports a runtime error and returns exception to throw

237

* @param message Error message

238

* @param sourceName Source file name

239

* @param line Line number where error occurred

240

* @param lineSource Source code line

241

* @param lineOffset Column offset in line

242

* @return EvaluatorException to throw

243

*/

244

EvaluatorException runtimeError(String message, String sourceName, int line, String lineSource, int lineOffset);

245

}

246

247

/**

248

* Default ErrorReporter implementation

249

* Prints errors to System.err and creates standard exceptions

250

*/

251

public class DefaultErrorReporter implements ErrorReporter {

252

/**

253

* Gets singleton instance of DefaultErrorReporter

254

* @return Default ErrorReporter instance

255

*/

256

public static ErrorReporter instance();

257

}

258

```

259

260

**Usage Examples:**

261

262

```java

263

// Custom error reporter with logging

264

ErrorReporter loggingReporter = new ErrorReporter() {

265

private final Logger logger = Logger.getLogger("JavaScript");

266

267

@Override

268

public void warning(String message, String sourceName, int line,

269

String lineSource, int lineOffset) {

270

logger.warning(String.format("JS Warning at %s:%d - %s",

271

sourceName, line, message));

272

}

273

274

@Override

275

public void error(String message, String sourceName, int line,

276

String lineSource, int lineOffset) {

277

logger.severe(String.format("JS Error at %s:%d - %s",

278

sourceName, line, message));

279

}

280

281

@Override

282

public EvaluatorException runtimeError(String message, String sourceName, int line,

283

String lineSource, int lineOffset) {

284

logger.severe(String.format("JS Runtime Error at %s:%d - %s",

285

sourceName, line, message));

286

return new EvaluatorException(message, sourceName, line, lineSource, lineOffset);

287

}

288

};

289

290

// Set custom error reporter

291

cx.setErrorReporter(loggingReporter);

292

293

// Errors will now be logged

294

try {

295

cx.evaluateString(scope, "invalidSyntax(((", "test.js", 1, null);

296

} catch (EvaluatorException e) {

297

// Error was logged by custom reporter

298

}

299

```

300

301

#### Exception Hierarchy

302

303

Comprehensive exception hierarchy for different types of JavaScript errors.

304

305

```java { .api }

306

/**

307

* Base class for all Rhino-specific exceptions

308

* Provides source location information for debugging

309

*/

310

public abstract class RhinoException extends RuntimeException {

311

/**

312

* Gets source file name where error occurred

313

* @return Source file name or null

314

*/

315

public String sourceName();

316

317

/**

318

* Gets line number where error occurred

319

* @return Line number or -1 if unknown

320

*/

321

public int lineNumber();

322

323

/**

324

* Gets source code line where error occurred

325

* @return Source line or null

326

*/

327

public String lineSource();

328

329

/**

330

* Gets column number where error occurred

331

* @return Column number or -1 if unknown

332

*/

333

public int columnNumber();

334

335

/**

336

* Gets JavaScript stack trace

337

* @return Stack trace string

338

*/

339

public String getScriptStackTrace();

340

}

341

342

/**

343

* Exception for compilation and evaluation errors

344

* Thrown when JavaScript syntax is invalid or evaluation fails

345

*/

346

public class EvaluatorException extends RhinoException {

347

/**

348

* Creates EvaluatorException with location info

349

* @param detail Error detail message

350

* @param sourceName Source file name

351

* @param lineNumber Line number

352

* @param lineSource Source line

353

* @param columnNumber Column number

354

*/

355

public EvaluatorException(String detail, String sourceName, int lineNumber,

356

String lineSource, int columnNumber);

357

}

358

359

/**

360

* Exception for ECMA-standard JavaScript runtime errors

361

* Represents Error objects thrown by JavaScript code

362

*/

363

public class EcmaError extends RhinoException {

364

/**

365

* Creates EcmaError from JavaScript Error object

366

* @param nativeError JavaScript Error object

367

* @param sourceName Source file name

368

* @param lineNumber Line number

369

* @param lineSource Source line

370

* @param columnNumber Column number

371

*/

372

public EcmaError(Scriptable nativeError, String sourceName, int lineNumber,

373

String lineSource, int columnNumber);

374

375

/**

376

* Gets the JavaScript Error object

377

* @return Native Error object

378

*/

379

public Scriptable getErrorObject();

380

381

/**

382

* Gets error name (TypeError, ReferenceError, etc.)

383

* @return Error type name

384

*/

385

public String getName();

386

}

387

388

/**

389

* Exception for values thrown from JavaScript throw statements

390

* Wraps any JavaScript value that was thrown

391

*/

392

public class JavaScriptException extends RhinoException {

393

/**

394

* Creates JavaScriptException wrapping thrown value

395

* @param value JavaScript value that was thrown

396

* @param sourceName Source file name

397

* @param lineNumber Line number

398

*/

399

public JavaScriptException(Object value, String sourceName, int lineNumber);

400

401

/**

402

* Gets the thrown JavaScript value

403

* @return Original thrown value

404

*/

405

public Object getValue();

406

}

407

408

/**

409

* Exception wrapping Java exceptions thrown from JavaScript

410

* Occurs when Java methods called from JavaScript throw exceptions

411

*/

412

public class WrappedException extends EvaluatorException {

413

/**

414

* Creates WrappedException wrapping Java exception

415

* @param exception Original Java exception

416

*/

417

public WrappedException(Throwable exception);

418

419

/**

420

* Gets the wrapped Java exception

421

* @return Original Java exception

422

*/

423

public Throwable getWrappedException();

424

}

425

```

426

427

**Usage Examples:**

428

429

```java

430

// Comprehensive error handling

431

try {

432

Object result = cx.evaluateString(scope, jsCode, "test.js", 1, null);

433

} catch (EvaluatorException ee) {

434

// Compilation or evaluation error

435

System.err.println("Evaluation error at " + ee.sourceName() + ":" + ee.lineNumber());

436

System.err.println("Message: " + ee.getMessage());

437

if (ee.lineSource() != null) {

438

System.err.println("Source: " + ee.lineSource());

439

}

440

} catch (EcmaError ee) {

441

// JavaScript Error object thrown

442

System.err.println("JavaScript " + ee.getName() + ": " + ee.getMessage());

443

System.err.println("At " + ee.sourceName() + ":" + ee.lineNumber());

444

} catch (JavaScriptException jse) {

445

// JavaScript throw statement

446

Object thrownValue = jse.getValue();

447

System.err.println("JavaScript threw: " + Context.toString(thrownValue));

448

System.err.println("At " + jse.sourceName() + ":" + jse.lineNumber());

449

} catch (WrappedException we) {

450

// Java exception from JavaScript-called Java method

451

Throwable original = we.getWrappedException();

452

System.err.println("Java exception in JavaScript: " + original.getClass().getName());

453

System.err.println("Message: " + original.getMessage());

454

original.printStackTrace();

455

}

456

```

457

458

### Debugging Support

459

460

#### Debugger Interface

461

462

Core debugging interface for runtime inspection and control.

463

464

```java { .api }

465

/**

466

* Main debugger interface for JavaScript execution debugging

467

* Provides hooks for compilation and execution events

468

*/

469

public interface Debugger {

470

/**

471

* Called when a script or function is compiled

472

* @param cx Current Context

473

* @param fnOrScript Compiled script or function

474

* @param source JavaScript source code

475

*/

476

void handleCompilationDone(Context cx, DebuggableScript fnOrScript, String source);

477

478

/**

479

* Gets debug frame for script execution

480

* @param cx Current Context

481

* @param fnOrScript Script or function being executed

482

* @return DebugFrame for tracking execution

483

*/

484

DebugFrame getFrame(Context cx, DebuggableScript fnOrScript);

485

}

486

487

/**

488

* Debug frame for tracking script execution

489

* Provides hooks for execution events and variable access

490

*/

491

public interface DebugFrame {

492

/**

493

* Called when execution enters function/script

494

* @param cx Current Context

495

* @param activation Activation object (local variables)

496

* @param thisObj 'this' object for execution

497

* @param args Function arguments

498

*/

499

void onEnter(Context cx, Scriptable activation, Scriptable thisObj, Object[] args);

500

501

/**

502

* Called when execution reaches new source line

503

* @param cx Current Context

504

* @param lineNumber Current line number

505

*/

506

void onLineChange(Context cx, int lineNumber);

507

508

/**

509

* Called when exception is thrown

510

* @param cx Current Context

511

* @param ex Exception that was thrown

512

*/

513

void onExceptionThrown(Context cx, Throwable ex);

514

515

/**

516

* Called when execution exits function/script

517

* @param cx Current Context

518

* @param byThrow true if exiting due to exception

519

* @param resultOrException Return value or exception

520

*/

521

void onExit(Context cx, boolean byThrow, Object resultOrException);

522

}

523

524

/**

525

* Provides debug information about compiled scripts

526

* Used by debugger to understand script structure

527

*/

528

public interface DebuggableScript {

529

/**

530

* Checks if this is top-level script (not function)

531

* @return true if top-level script

532

*/

533

boolean isTopLevel();

534

535

/**

536

* Checks if this is a function

537

* @return true if function

538

*/

539

boolean isFunction();

540

541

/**

542

* Gets function name (null for anonymous)

543

* @return Function name or null

544

*/

545

String getFunctionName();

546

547

/**

548

* Gets source file name

549

* @return Source file name

550

*/

551

String getSourceName();

552

553

/**

554

* Gets array of line numbers with executable code

555

* @return Array of line numbers

556

*/

557

int[] getLineNumbers();

558

559

/**

560

* Gets number of nested functions

561

* @return Function count

562

*/

563

int getFunctionCount();

564

565

/**

566

* Gets nested function by index

567

* @param index Function index

568

* @return Nested DebuggableScript

569

*/

570

DebuggableScript getFunction(int index);

571

572

/**

573

* Gets parameter names for function

574

* @return Array of parameter names

575

*/

576

String[] getParamNames();

577

}

578

```

579

580

**Usage Examples:**

581

582

```java

583

// Simple debugger implementation

584

Debugger simpleDebugger = new Debugger() {

585

@Override

586

public void handleCompilationDone(Context cx, DebuggableScript fnOrScript, String source) {

587

System.out.println("Compiled: " + fnOrScript.getSourceName() +

588

(fnOrScript.isFunction() ? " (function)" : " (script)"));

589

}

590

591

@Override

592

public DebugFrame getFrame(Context cx, DebuggableScript fnOrScript) {

593

return new DebugFrame() {

594

@Override

595

public void onEnter(Context cx, Scriptable activation,

596

Scriptable thisObj, Object[] args) {

597

System.out.println("Entering: " + fnOrScript.getFunctionName());

598

}

599

600

@Override

601

public void onLineChange(Context cx, int lineNumber) {

602

System.out.println("Line: " + lineNumber);

603

}

604

605

@Override

606

public void onExceptionThrown(Context cx, Throwable ex) {

607

System.out.println("Exception: " + ex.getMessage());

608

}

609

610

@Override

611

public void onExit(Context cx, boolean byThrow, Object resultOrException) {

612

System.out.println("Exit" + (byThrow ? " (exception)" : " (normal)"));

613

}

614

};

615

}

616

};

617

618

// Enable debugging

619

cx.setDebugger(simpleDebugger, null);

620

cx.setGeneratingDebug(true);

621

622

// Execute with debugging

623

cx.evaluateString(scope, """

624

function test(x) {

625

var y = x * 2;

626

return y + 1;

627

}

628

test(5);

629

""", "debug-test.js", 1, null);

630

```

631

632

#### Stack Introspection

633

634

Runtime stack inspection for debugging and error reporting.

635

636

```java { .api }

637

/**

638

* Represents an element in the JavaScript call stack

639

* Provides file, function, and line information

640

*/

641

public class ScriptStackElement {

642

/**

643

* Source file name

644

*/

645

public final String fileName;

646

647

/**

648

* Function name (null for top-level)

649

*/

650

public final String functionName;

651

652

/**

653

* Line number in source

654

*/

655

public final int lineNumber;

656

657

/**

658

* Creates stack element

659

* @param fileName Source file name

660

* @param functionName Function name

661

* @param lineNumber Line number

662

*/

663

public ScriptStackElement(String fileName, String functionName, int lineNumber);

664

665

/**

666

* String representation for debugging

667

* @return Formatted stack element string

668

*/

669

public String toString();

670

}

671

```

672

673

**Usage Examples:**

674

675

```java

676

// Get current JavaScript stack trace

677

try {

678

cx.evaluateString(scope, """

679

function a() { b(); }

680

function b() { c(); }

681

function c() { throw new Error('test'); }

682

a();

683

""", "stack-test.js", 1, null);

684

} catch (JavaScriptException jse) {

685

// Get stack trace from exception

686

String stackTrace = jse.getScriptStackTrace();

687

System.out.println("JavaScript stack trace:");

688

System.out.println(stackTrace);

689

690

// Parse stack elements if needed

691

String[] lines = stackTrace.split("\n");

692

for (String line : lines) {

693

System.out.println("Stack frame: " + line);

694

}

695

}

696

```

697

698

### Advanced Security Features

699

700

#### Context Feature Controls

701

702

Fine-grained control over JavaScript language features for security.

703

704

```java { .api }

705

// Feature constants for security control

706

public static final int FEATURE_STRICT_MODE = 11;

707

public static final int FEATURE_ENHANCED_JAVA_ACCESS = 13;

708

public static final int FEATURE_E4X = 6; // E4X XML literals

709

public static final int FEATURE_DYNAMIC_SCOPE = 7;

710

public static final int FEATURE_STRICT_VARS = 8;

711

public static final int FEATURE_STRICT_EVAL = 9;

712

public static final int FEATURE_WARNING_AS_ERROR = 12;

713

public static final int FEATURE_THREAD_SAFE_OBJECTS = 17;

714

715

/**

716

* Checks if feature is enabled in current Context

717

* @param featureIndex Feature constant

718

* @return true if feature is enabled

719

*/

720

public boolean hasFeature(int featureIndex);

721

```

722

723

**Usage Examples:**

724

725

```java

726

// Disable potentially dangerous features

727

Context cx = Context.enter();

728

try {

729

// Enable strict mode

730

cx.setLanguageVersion(Context.VERSION_ES6);

731

732

// Disable enhanced Java access

733

if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) {

734

// Custom ContextFactory can override feature settings

735

}

736

737

// Enable strict variable declarations

738

// (requires custom ContextFactory to override hasFeature)

739

740

} finally {

741

Context.exit();

742

}

743

744

// Custom ContextFactory for security

745

ContextFactory secureFactory = new ContextFactory() {

746

@Override

747

protected boolean hasFeature(Context cx, int featureIndex) {

748

switch (featureIndex) {

749

case Context.FEATURE_ENHANCED_JAVA_ACCESS:

750

return false; // Disable enhanced Java access

751

case Context.FEATURE_STRICT_MODE:

752

return true; // Force strict mode

753

case Context.FEATURE_E4X:

754

return false; // Disable E4X XML literals

755

default:

756

return super.hasFeature(cx, featureIndex);

757

}

758

}

759

};

760

761

// Use secure factory

762

Object result = secureFactory.call(cx -> {

763

Scriptable scope = cx.initSafeStandardObjects();

764

return cx.evaluateString(scope, jsCode, "secure.js", 1, null);

765

});

766

```

767

768

#### Timeout and Resource Control

769

770

Protection against infinite loops and resource exhaustion.

771

772

```java

773

// Custom ContextFactory with timeout

774

ContextFactory timeoutFactory = new ContextFactory() {

775

@Override

776

protected void observeInstructionCount(Context cx, int instructionCount) {

777

// Check if execution should be interrupted

778

long currentTime = System.currentTimeMillis();

779

long startTime = (Long) cx.getThreadLocal("startTime");

780

if (currentTime - startTime > 5000) { // 5 second timeout

781

throw new Error("Script execution timeout");

782

}

783

}

784

785

@Override

786

protected Context makeContext() {

787

Context cx = super.makeContext();

788

cx.setInstructionObserverThreshold(10000); // Check every 10k instructions

789

cx.putThreadLocal("startTime", System.currentTimeMillis());

790

return cx;

791

}

792

};

793

794

// Execute with timeout protection

795

try {

796

timeoutFactory.call(cx -> {

797

Scriptable scope = cx.initStandardObjects();

798

return cx.evaluateString(scope, "while(true) {}", "infinite.js", 1, null);

799

});

800

} catch (Error e) {

801

System.out.println("Execution interrupted: " + e.getMessage());

802

}

803

```