浏览代码

Merge pull request #3408 from tauri-apps/docs/api

Lucas Fernandes Nogueira 3 年之前
父节点
当前提交
d29c5d551f

+ 2 - 3
core/tauri-utils/src/config.rs

@@ -629,7 +629,7 @@ macro_rules! check_feature {
 /// Each pattern can start with a variable that resolves to a system base directory.
 /// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
 /// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
-/// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$CWD`.
+/// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`.
 #[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
 #[cfg_attr(feature = "schema", derive(JsonSchema))]
 pub struct FsAllowlistScope(pub Vec<PathBuf>);
@@ -882,7 +882,7 @@ pub struct ShellAllowedCommand {
   /// It can start with a variable that resolves to a system base directory.
   /// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
   /// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
-  /// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$CWD`.
+  /// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`.
   #[serde(rename = "cmd")]
   pub command: PathBuf,
 
@@ -936,7 +936,6 @@ pub enum ShellAllowedArg {
     /// before it will be executed.
     ///
     /// [regex]: https://docs.rs/regex/latest/regex/#syntax
-    #[serde(default)]
     validator: String,
   },
 }

文件差异内容过多而无法显示
+ 0 - 0
core/tauri/scripts/bundle.js


+ 4 - 2
examples/api/src-tauri/tauri.conf.json

@@ -91,11 +91,13 @@
         "scope": [
           {
             "name": "sh",
-            "cmd": "sh"
+            "cmd": "sh",
+            "args": ["-c", { "validator": "\\S+" }]
           },
           {
             "name": "cmd",
-            "cmd": "cmd"
+            "cmd": "cmd",
+            "args": ["/C", { "validator": "\\S+" }]
           }
         ]
       },

+ 29 - 34
examples/streaming/index.html

@@ -1,36 +1,31 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="UTF-8" />
-    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <style>
-      video {
-        width: 100vw;
-        height: 100vh;
-      }
-    </style>
-  </head>
-  <body>
-    <video
-      id="video_source"
-      controls=""
-      autoplay=""
-      name="media"
-    >
-      <source src="stream://example/test_video.mp4" type="video/mp4" />
-    </video>
-    <script>
-      ;(function () {
-        if (navigator.userAgent.includes('Windows')) {
-          const video = document.getElementById('video_source')
-          const sources = video.getElementsByTagName('source')
-          // on windows the custom protocl should be the host
-          // followed by the complete path
-          sources[0].src = 'https://stream.example/test_video.mp4'
-          video.load()
-        }
-      })()
-    </script>
-  </body>
-</html>
+
+<head>
+  <meta charset="UTF-8" />
+  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <style>
+    video {
+      width: 100vw;
+      height: 100vh;
+    }
+  </style>
+</head>
+
+<body>
+  <video id="video_source" controls="" autoplay="" name="media">
+    <source type="video/mp4" />
+  </video>
+  <script>
+    const { convertFileSrc } = window.__TAURI__.tauri
+    const video = document.getElementById('video_source')
+    const source = document.createElement('source')
+    source.type = 'video/mp4'
+    source.src = convertFileSrc('example/test_video.mp4', 'stream')
+    video.appendChild(source)
+    video.load()
+  </script>
+</body>
+
+</html>

+ 3 - 2
examples/streaming/src-tauri/tauri.conf.json

@@ -3,7 +3,8 @@
     "distDir": ["../index.html"],
     "devPath": ["../index.html"],
     "beforeDevCommand": "",
-    "beforeBuildCommand": ""
+    "beforeBuildCommand": "",
+    "withGlobalTauri": true
   },
   "tauri": {
     "bundle": {
@@ -47,7 +48,7 @@
       }
     ],
     "security": {
-      "csp": "default-src 'self'"
+      "csp": "default-src 'self'; media-src stream: https://stream.example"
     },
     "updater": {
       "active": false

+ 37 - 0
tooling/api/src/fs.ts

@@ -28,6 +28,43 @@
  * }
  * ```
  * It is recommended to allowlist only the APIs you use for optimal bundle size and security.
+ *
+ * ## Security
+ *
+ * This module prevents path traversal, not allowing absolute paths or parent dir components
+ * (i.e. "/usr/path/to/file" or "../path/to/file" paths are not allowed).
+ * Paths accessed with this API must be relative to one of the [[BaseDirectory | base directories]]
+ * so if you need access to arbitrary filesystem paths, you must write such logic on the core layer instead.
+ *
+ * The API has a scope configuration that forces you to restrict the paths that can be accessed using glob patterns.
+ *
+ * The scope configuration is an array of glob patterns describing folder paths that are allowed.
+ * For instance, this scope configuration only allows accessing files on the
+ * *databases* folder of the [[path.appDir | $APP directory]]:
+ * ```json
+ * {
+ *   "tauri": {
+ *     "allowlist": {
+ *       "fs": {
+ *         "scope": ["$APP/databases/*"]
+ *       }
+ *     }
+ *   }
+ * }
+ * ```
+ *
+ * Notice the use of the `$APP` variable. The value is injected at runtime, resolving to the [[path.appDir | app directory]].
+ * The available variables are:
+ * [[path.audioDir | `$AUDIO`]], [[path.cacheDir | `$CACHE`]], [[path.configDir | `$CONFIG`]], [[path.dataDir | `$DATA`]],
+ * [[path.localDataDir | `$LOCALDATA`]], [[path.desktopDir | `$DESKTOP`]], [[path.documentDir | `$DOCUMENT`]],
+ * [[path.downloadDir | `$DOWNLOAD`]], [[path.executableDir | `$EXE`]], [[path.fontDir | `$FONT`]], [[path.homeDir | `$HOME`]],
+ * [[path.pictureDir | `$PICTURE`]], [[path.publicDir | `$PUBLIC`]], [[path.runtimeDir | `$RUNTIME`]],
+ * [[path.templateDir | `$TEMPLATE`]], [[path.videoDir | `$VIDEO`]], [[path.resourceDir | `$RESOURCE`]], [[path.appDir | `$APP`]].
+ *
+ * Trying to execute any API with a URL not configured on the scope results in a promise rejection due to denied access.
+ *
+ * Note that this scope applies to **all** APIs on this module.
+ *
  * @module
  */
 

+ 19 - 0
tooling/api/src/http.ts

@@ -21,6 +21,25 @@
  * }
  * ```
  * It is recommended to allowlist only the APIs you use for optimal bundle size and security.
+ *
+ * ## Security
+ *
+ * This API has a scope configuration that forces you to restrict the URLs and paths that can be accessed using glob patterns.
+ *
+ * For instance, this scope configuration only allows making HTTP requests to the GitHub API for the `tauri-apps` organization:
+ * ```json
+ * {
+ *   "tauri": {
+ *     "allowlist": {
+ *       "http": {
+ *         "scope": ["https://api.github.com/repos/tauri-apps/*"]
+ *       }
+ *     }
+ *   }
+ * }
+ * ```
+ * Trying to execute any API with a URL not configured on the scope results in a promise rejection due to denied access.
+ *
  * @module
  */
 

+ 4 - 4
tooling/api/src/path.ts

@@ -2,10 +2,6 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: MIT
 
-import { invokeTauriCommand } from './helpers/tauri'
-import { BaseDirectory } from './fs'
-import { isWindows } from './helpers/os-check'
-
 /**
  * The path module provides utilities for working with file and directory paths.
  *
@@ -27,6 +23,10 @@ import { isWindows } from './helpers/os-check'
  * @module
  */
 
+import { invokeTauriCommand } from './helpers/tauri'
+import { BaseDirectory } from './fs'
+import { isWindows } from './helpers/os-check'
+
 /**
  * Returns the path to the suggested directory for your app config files.
  * Resolves to `${configDir}/${bundleIdentifier}`, where `bundleIdentifier` is the value configured on `tauri.conf.json > tauri > bundle > identifier`.

+ 2 - 2
tooling/api/src/process.ts

@@ -2,8 +2,6 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: MIT
 
-import { invokeTauriCommand } from './helpers/tauri'
-
 /**
  * Perform operations on the current process.
  *
@@ -11,6 +9,8 @@ import { invokeTauriCommand } from './helpers/tauri'
  * @module
  */
 
+import { invokeTauriCommand } from './helpers/tauri'
+
 /**
  * Exits immediately with the given `exitCode`.
  *

+ 60 - 6
tooling/api/src/shell.ts

@@ -2,9 +2,6 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: MIT
 
-import { invokeTauriCommand } from './helpers/tauri'
-import { transformCallback } from './tauri'
-
 /**
  * Access the system shell.
  * Allows you to spawn child processes and manage files and URLs using their default application.
@@ -19,6 +16,7 @@ import { transformCallback } from './tauri'
  *       "shell": {
  *         "all": true, // enable all shell APIs
  *         "execute": true, // enable process spawn APIs
+ *         "sidecar": true, // enable spawning sidecars
  *         "open": true // enable opening files/URLs using the default program
  *       }
  *     }
@@ -26,9 +24,60 @@ import { transformCallback } from './tauri'
  * }
  * ```
  * It is recommended to allowlist only the APIs you use for optimal bundle size and security.
+ *
+ * ## Security
+ *
+ * This API has a scope configuration that forces you to restrict the programs and arguments that can be used.
+ *
+ * ### Restricting access to the [[`open`]] API
+ *
+ * On the allowlist, `open: true` means that the [[open]] API can be used with any URL,
+ * as the argument is validated with the `^https?://` regex.
+ * You can change that regex by changing the boolean value to a string, e.g. `open: ^https://github.com/`.
+ *
+ * ### Restricting access to the [[`Command`]] APIs
+ *
+ * The `shell` allowlist object has a `scope` field that defines an array of CLIs that can be used.
+ * Each CLI is a configuration object `{ name: string, command: string, sidecar?: bool, args?: boolean | Arg[] }`.
+ *
+ * - `name`: the unique identifier of the command, passed to the [[Command.constructor | Command constructor]].
+ * If it's a sidecar, this must be the value defined on `tauri.conf.json > tauri > bundle > externalBin`.
+ * - `command`: the program that is executed on this configuration. If it's a sidecar, it must be the same as `name`.
+ * - `sidecar`: whether the object configures a sidecar or a system program.
+ * - `args`: the arguments that can be passed to the program. By default no arguments are allowed.
+ *   - `true` means that any argument list is allowed.
+ *   - `false` means that no arguments are allowed.
+ *   - otherwise an array can be configured. Each item is either a string representing the fixed argument value
+ *     or a `{ validator: string }` that defines a regex validating the argument value.
+ *
+ * #### Example scope configuration
+ *
+ * CLI: `git commit -m "the commit message"`
+ *
+ * Configuration:
+ * ```json
+ * {
+ *   "scope": {
+ *     "name": "run-git-commit",
+ *     "command": "git",
+ *     "args": ["commit", "-m", { "validator": "\\S+" }]
+ *   }
+ * }
+ * ```
+ * Usage:
+ * ```typescript
+ * import { Command } from '@tauri-apps/api/shell'
+ * new Command('run-git-commit', ['commit', '-m', 'the commit message'])
+ * ```
+ *
+ * Trying to execute any API with a program not configured on the scope results in a promise rejection due to denied access.
+ *
  * @module
  */
 
+import { invokeTauriCommand } from './helpers/tauri'
+import { transformCallback } from './tauri'
+
 interface SpawnOptions {
   /** Current working directory. */
   cwd?: string
@@ -209,7 +258,8 @@ class Command extends EventEmitter<'close' | 'error'> {
   /**
    * Creates a new `Command` instance.
    *
-   * @param program The program to execute.
+   * @param program The program name to execute.
+   * It must be configured on `tauri.conf.json > tauri > allowlist > shell > scope`.
    * @param args Program arguments.
    * @param options Spawn options.
    */
@@ -233,6 +283,7 @@ class Command extends EventEmitter<'close' | 'error'> {
    * ```
    *
    * @param program The program to execute.
+   * It must be configured on `tauri.conf.json > tauri > allowlist > shell > scope`.
    * @param args Program arguments.
    * @param options Spawn options.
    * @returns
@@ -356,7 +407,10 @@ type CommandEvent =
  * ```
  *
  * @param path The path or URL to open.
- * @param openWith The app to open the file or URL with. Defaults to the system default application for the specified path type.
+ * This value is matched against the string regex defined on `tauri.conf.json > tauri > allowlist > shell > open`,
+ * which defaults to `^https?://`.
+ * @param openWith The app to open the file or URL with.
+ * Defaults to the system default application for the specified path type.
  * @returns
  */
 async function open(path: string, openWith?: string): Promise<void> {
@@ -370,5 +424,5 @@ async function open(path: string, openWith?: string): Promise<void> {
   })
 }
 
-export { Command, Child, open }
+export { Command, Child, EventEmitter, open }
 export type { ChildProcess, SpawnOptions }

+ 21 - 5
tooling/api/src/tauri.ts

@@ -88,19 +88,35 @@ async function invoke<T>(cmd: string, args: InvokeArgs = {}): Promise<T> {
 /**
  * Convert a device file path to an URL that can be loaded by the webview.
  * Note that `asset:` and `https://asset.localhost` must be allowed on the `csp` value configured on `tauri.conf.json > tauri > security`.
- * Example CSP value: `"csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost"`.
+ * Example CSP value: `"csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost"` to use the asset protocol on image sources.
  *
  * Additionally, the `asset` must be allowlisted under `tauri.conf.json > tauri > allowlist > protocol`,
  * and its access scope must be defined on the `assetScope` array on the same `protocol` object.
  *
- * @param  filePath the file path.
+ * @param  filePath The file path.
+ * @param  protocol The protocol to use. Defaults to `asset`. You only need to set this when using a custom protocol.
+ * @example
+ * ```typescript
+ * import { appDir, join } from '@tauri-apps/api/path'
+ * import { convertFileSrc } from '@tauri-apps/api/tauri'
+ * const appDirPath = await appDir()
+ * const filePath = await join(appDir, 'assets/video.mp4')
+ * const assetUrl = convertFileSrc(filePath)
+ *
+ * const video = document.getElementById('my-video')
+ * const source = document.createElement('source')
+ * source.type = 'video/mp4'
+ * source.src = assetUrl
+ * video.appendChild(source)
+ * video.load()
+ * ```
  *
  * @return the URL that can be used as source on the webview.
  */
-function convertFileSrc(filePath: string): string {
+function convertFileSrc(filePath: string, protocol = 'asset'): string {
   return navigator.userAgent.includes('Windows')
-    ? `https://asset.localhost/${filePath}`
-    : `asset://${filePath}`
+    ? `https://${protocol}.localhost/${filePath}`
+    : `${protocol}://${filePath}`
 }
 
 export type { InvokeArgs }

+ 26 - 3
tooling/api/src/window.ts

@@ -14,7 +14,30 @@
  *     "allowlist": {
  *       "window": {
  *         "all": true, // enable all window APIs
- *         "create": true // enable window creation
+ *         "create": true, // enable window creation
+ *         "center": true,
+ *         "requestUserAttention": true,
+ *         "setResizable": true,
+ *         "setTitle": true,
+ *         "maximize": true,
+ *         "unmaximize": true,
+ *         "minimize": true,
+ *         "unminimize": true,
+ *         "show": true,
+ *         "hide": true,
+ *         "close": true,
+ *         "setDecorations": true,
+ *         "setAlwaysOnTop": true,
+ *         "setSize": true,
+ *         "setMinSize": true,
+ *         "setMaxSize": true,
+ *         "setPosition": true,
+ *         "setFullscreen": true,
+ *         "setFocus": true,
+ *         "setIcon": true,
+ *         "setSkipTaskbar": true,
+ *         "startDragging": true,
+ *         "print": true
  *       }
  *     }
  *   }
@@ -223,7 +246,7 @@ export type WindowLabel = string
  * A webview window handle allows emitting and listening to events from the backend that are tied to the window.
  */
 class WebviewWindowHandle {
-  /** Window label. */
+  /** The window label. It is a unique identifier for the window, can be used to reference it later. */
   label: WindowLabel
   /** Local event listeners. */
   listeners: { [key: string]: Array<EventCallback<any>> }
@@ -1094,7 +1117,7 @@ class WindowManager extends WebviewWindowHandle {
 class WebviewWindow extends WindowManager {
   /**
    * Creates a new WebviewWindow.
-   * * @param label The webview window label. It must be alphanumeric.
+   * * @param label The webview window label, a unique identifier that can be used to reference it later. It must be alphanumeric.
    * @returns The WebviewWindow instance to communicate with the webview.
    */
   constructor(label: WindowLabel, options: WindowOptions = {}) {

+ 2 - 2
tooling/cli/schema.json

@@ -994,7 +994,7 @@
       "additionalProperties": false
     },
     "FsAllowlistScope": {
-      "description": "Filesystem scope definition. It is a list of glob patterns that restrict the API access from the webview.\n\nEach pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$CWD`.",
+      "description": "Filesystem scope definition. It is a list of glob patterns that restrict the API access from the webview.\n\nEach pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`.",
       "type": "array",
       "items": {
         "type": "string"
@@ -1355,7 +1355,7 @@
           ]
         },
         "cmd": {
-          "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$CWD`.",
+          "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`.",
           "type": "string"
         },
         "name": {

部分文件因为文件数量过多而无法显示