diff --git a/cli/flags.rs b/cli/flags.rs index 772c719a8849b7..b3ada13188c35c 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -112,6 +112,7 @@ pub struct Flags { pub inspect_brk: Option, pub seed: Option, pub v8_flags: Option>, + pub unstable: bool, pub lock: Option, pub lock_write: bool, @@ -498,6 +499,10 @@ fn run_test_args_parse(flags: &mut Flags, matches: &clap::ArgMatches) { flags.cached_only = true; } + if matches.is_present("unstable") { + flags.unstable = true; + } + if matches.is_present("seed") { let seed_string = matches.value_of("seed").unwrap(); let seed = seed_string.parse::().unwrap(); @@ -919,6 +924,11 @@ fn run_test_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { .long("cached-only") .help("Require that remote dependencies are already cached"), ) + .arg( + Arg::with_name("unstable") + .long("unstable") + .help("Enable unstable APIs"), + ) .arg( Arg::with_name("seed") .long("seed") diff --git a/cli/js/lib.deno.ns.d.ts b/cli/js/lib.deno.ns.d.ts index 64e3613f5e568d..4964f1b018c50c 100644 --- a/cli/js/lib.deno.ns.d.ts +++ b/cli/js/lib.deno.ns.d.ts @@ -1561,6 +1561,8 @@ declare namespace Deno { export function linkSync(oldpath: string, newpath: string): void; /** Creates `newpath` as a hard link to `oldpath`. + * + * **UNSTABLE**: needs security review. * * await Deno.link("old/name", "new/name"); * @@ -1568,6 +1570,8 @@ declare namespace Deno { export function link(oldpath: string, newpath: string): Promise; /** **UNSTABLE**: `type` argument type may be changed to `"dir" | "file"`. + * + * **UNSTABLE**: needs security review. * * Creates `newpath` as a symbolic link to `oldpath`. * @@ -1586,6 +1590,8 @@ declare namespace Deno { ): void; /** **UNSTABLE**: `type` argument may be changed to `"dir" | "file"` + * + * **UNSTABLE**: needs security review. * * Creates `newpath` as a symbolic link to `oldpath`. * diff --git a/cli/js/tests/unit_test_runner.ts b/cli/js/tests/unit_test_runner.ts index ed8b9dd80da676..43009c6123a543 100755 --- a/cli/js/tests/unit_test_runner.ts +++ b/cli/js/tests/unit_test_runner.ts @@ -90,6 +90,7 @@ function spawnWorkerRunner( const cmd = [ Deno.execPath(), "run", + "--unstable", // TODO(ry) be able to test stable vs unstable "-A", "cli/js/tests/unit_test_runner.ts", "--worker", diff --git a/cli/ops/fs.rs b/cli/ops/fs.rs index c46da6c04511d7..5ab53481092df2 100644 --- a/cli/ops/fs.rs +++ b/cli/ops/fs.rs @@ -647,6 +647,7 @@ fn op_link( args: Value, _zero_copy: Option, ) -> Result { + state.check_unstable("Deno.link"); let args: LinkArgs = serde_json::from_value(args)?; let oldpath = resolve_from_cwd(Path::new(&args.oldpath))?; let newpath = resolve_from_cwd(Path::new(&args.newpath))?; @@ -675,6 +676,7 @@ fn op_symlink( args: Value, _zero_copy: Option, ) -> Result { + state.check_unstable("Deno.symlink"); let args: SymlinkArgs = serde_json::from_value(args)?; let oldpath = resolve_from_cwd(Path::new(&args.oldpath))?; let newpath = resolve_from_cwd(Path::new(&args.newpath))?; diff --git a/cli/ops/plugins.rs b/cli/ops/plugins.rs index 17f366d39f9d29..f4212f57927cba 100644 --- a/cli/ops/plugins.rs +++ b/cli/ops/plugins.rs @@ -39,6 +39,7 @@ pub fn op_open_plugin( args: Value, _zero_copy: Option, ) -> Result { + state.check_unstable("Deno.openPlugin"); let args: OpenPluginArgs = serde_json::from_value(args).unwrap(); let filename = deno_fs::resolve_from_cwd(Path::new(&args.filename))?; diff --git a/cli/state.rs b/cli/state.rs index 96be28f2f65458..40662b56330395 100644 --- a/cli/state.rs +++ b/cli/state.rs @@ -230,6 +230,23 @@ impl State { dispatcher(isolate, &state, args, zero_copy) } } + + /// Quits the process if the --unstable flag was not provided. + /// + /// This is intentionally a non-recoverable check so that people cannot probe + /// for unstable APIs from stable programs. + pub fn check_unstable(&self, api_name: &str) { + // TODO(ry) Maybe use IsolateHandle::terminate_execution here to provide a + // stack trace in JS. + let s = self.0.borrow(); + if !s.global_state.flags.unstable { + eprintln!( + "Unstable API '{}'. The --unstable flag must be provided.", + api_name + ); + std::process::exit(70); + } + } } impl ModuleLoader for State { diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 819ccda728a2f3..ae905840dec872 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -1568,6 +1568,13 @@ itest!(top_level_for_await_ts { output: "top_level_for_await.out", }); +itest!(unstable { + args: "run unstable.js", + check_stderr: true, + exit_code: 70, + output: "unstable.out", +}); + itest!(_053_import_compression { args: "run --reload --allow-net 053_import_compression/main.ts", output: "053_import_compression.out", diff --git a/cli/tests/std_tests.rs b/cli/tests/std_tests.rs index 32906c2fa5756e..f5b1f1998f8e35 100644 --- a/cli/tests/std_tests.rs +++ b/cli/tests/std_tests.rs @@ -18,6 +18,7 @@ mod tests { let mut deno = deno_cmd .current_dir(cwd) // note: std tests expect to run from "std" dir .arg("test") + .arg("--unstable") .arg("--seed=86") // Some tests rely on specific random numbers. .arg("-A") // .arg("-Ldebug") diff --git a/cli/tests/unstable.js b/cli/tests/unstable.js new file mode 100644 index 00000000000000..e6f2274cdc2f59 --- /dev/null +++ b/cli/tests/unstable.js @@ -0,0 +1,2 @@ +// This program should require the --unstable flag +Deno.openPlugin("foo"); diff --git a/cli/tests/unstable.out b/cli/tests/unstable.out new file mode 100644 index 00000000000000..31fead7d968b4f --- /dev/null +++ b/cli/tests/unstable.out @@ -0,0 +1 @@ +Unstable API 'Deno.openPlugin'. The --unstable flag must be provided. diff --git a/test_plugin/tests/integration_tests.rs b/test_plugin/tests/integration_tests.rs index fc18d79d9ffd97..1f118b77a9623a 100644 --- a/test_plugin/tests/integration_tests.rs +++ b/test_plugin/tests/integration_tests.rs @@ -31,6 +31,7 @@ fn basic() { assert!(build_plugin_output.status.success()); let output = deno_cmd() .arg("--allow-plugin") + .arg("--unstable") .arg("tests/test.js") .arg(BUILD_VARIANT) .output()