Skip to content

Commit 7a44b26

Browse files
Merge branch 'master' into prepare-10-1-1
2 parents 5e97d3f + bfe5cd5 commit 7a44b26

7 files changed

Lines changed: 194 additions & 43 deletions

File tree

docs/The-starting-of-an-app-using-Appium-node-server-started-programmatically.md

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Requirements
2-
- Installed Node.js 7 or greater.
2+
- Installed Node.js 7 or greater.
33

4-
- At least an appium server instance installed via __npm__.
4+
- At least an appium server instance installed via __npm__.
55

66
# The basic principle.
77

@@ -10,7 +10,7 @@ It works the similar way as common [ChromeDriver](https://seleniumhq.github.io/s
1010
# How to prepare the local service before the starting
1111

1212

13-
## If there is no specific parameters then
13+
## If there is no specific parameters then
1414

1515
```java
1616
import io.appium.java_client.service.local.AppiumDriverLocalService;
@@ -20,7 +20,7 @@ It works the similar way as common [ChromeDriver](https://seleniumhq.github.io/s
2020
service.start();
2121
...
2222
service.stop();
23-
```
23+
```
2424

2525
### FYI
2626

@@ -32,20 +32,23 @@ AppiumDriverLocalService service = AppiumDriverLocalService.buildDefaultService(
3232

3333
It is more usual for UNIX/LINUX-like OS's. Also there are situations when should be used an another Node.JS instance, e.g. the instance which is installed in the directory that differs from one defined at the PATH environmental variable. The same may be true for Appium node server (it is related to _appium.js_ file (v <= 1.4.16) and _main.js_ (v >= 1.5.0)).
3434

35-
At this case user is able to set up values of the **NODE_BINARY_PATH** (The environmental variable used to define the path to executable NodeJS file (node.exe for WIN and node for Linux/MacOS X)) and the **APPIUM_BINARY_PATH** (The environmental variable used to define the path to executable appium.js (1.4.x and lower) or main.js (1.5.x and higher)) environmental variables/system properties. Also it is possible to define these values programmatically:
35+
At this case user is able to set up values of the **NODE_BINARY_PATH** (The environmental variable used to define the path to executable NodeJS file (node.exe for WIN and node for Linux/MacOS X)) and the **APPIUM_BINARY_PATH** (The environmental variable used to define the path to executable appium.js (1.4.x and lower) or main.js (1.5.x and higher)) environmental variables/system properties. Also it is possible to define these values programmatically:
3636

3737
```java
38+
import io.appium.java_client.service.local.AppiumDriverLocalService;
39+
import io.appium.java_client.service.local.AppiumServiceBuilder;
40+
3841
//appium.node.js.exec.path
39-
System.setProperty(AppiumServiceBuilder.NODE_PATH ,
42+
System.setProperty(AppiumServiceBuilder.NODE_PATH,
4043
"the path to the desired node.js executable");
4144

42-
System.setProperty(AppiumServiceBuilder.APPIUM_PATH ,
45+
System.setProperty(AppiumServiceBuilder.APPIUM_PATH,
4346
"the path to the desired appium.js or main.js");
4447

4548
AppiumDriverLocalService service = AppiumDriverLocalService.buildDefaultService();
4649
```
4750

48-
## If there should be non default parameters specified then
51+
## If there should be non default parameters specified then
4952

5053
```java
5154
import io.appium.java_client.service.local.AppiumDriverLocalService;
@@ -57,9 +60,9 @@ AppiumDriverLocalService service = AppiumDriverLocalService.
5760
buildService(new AppiumServiceBuilder().
5861
withArgument(GeneralServerFlag.TEMP_DIRECTORY,
5962
"The_path_to_the_temporary_directory"));
60-
```
63+
```
6164

62-
or
65+
or
6366

6467
```java
6568
import io.appium.java_client.service.local.AppiumDriverLocalService;
@@ -70,64 +73,64 @@ import io.appium.java_client.service.local.flags.GeneralServerFlag;
7073
AppiumDriverLocalService service = new AppiumServiceBuilder().
7174
withArgument(GeneralServerFlag.TEMP_DIRECTORY,
7275
"The_path_to_the_temporary_directory").build();
73-
```
76+
```
7477

7578
Lists of available server side flags are here:
7679

7780
- io.appium.java_client.service.local.flags.GeneralServerFlag;
7881
- io.appium.java_client.service.local.flags.AndroidServerFlag;
7982
- io.appium.java_client.service.local.flags.IOSServerFlag
8083

81-
84+
8285
## Which parameters also can be defined
8386

8487
- If it is necessary to define some specific port or any free port
8588

8689
```java
8790
new AppiumServiceBuilder().usingPort(4000);
88-
```
91+
```
8992

90-
or
93+
or
9194

9295
```java
9396
new AppiumServiceBuilder().usingAnyFreePort();
94-
```
97+
```
9598

9699
- If it is necessary to use another IP address
97100

98101
```java
99102
new AppiumServiceBuilder().withIPAddress("127.0.0.1");
100-
```
103+
```
101104

102105
- If it is necessary to define output log file
103106

104107
```java
105-
import java.io.File;
108+
import java.io.File;
106109
...
107110

108111
new AppiumServiceBuilder().withLogFile(logFile);
109-
```
112+
```
110113

111114
- If it is necessary to define another Node.js executable file
112115

113116
```java
114117
import java.io.File;
115-
118+
116119
...
117120

118121
new AppiumServiceBuilder().usingDriverExecutable(nodeJSExecutable);
119-
```
122+
```
120123

121124
- If it is necessary to define another appium.js/main.js file
122125

123126
```java
124127
import java.io.File;
125-
128+
126129
...
127-
//appiumJS is the full or relative path to
130+
//appiumJS is the full or relative path to
128131
//the appium.js (v<=1.4.16) or main.js (v>=1.5.0)
129132
new AppiumServiceBuilder().withAppiumJS(new File(appiumJS));
130-
```
133+
```
131134

132135
- It is possible to define server capabilities (node server v >= 1.5.0)
133136

@@ -152,39 +155,39 @@ serverCapabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emul
152155
serverCapabilities.setCapability(MobileCapabilityType.FULL_RESET, true);
153156
serverCapabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 60);
154157
serverCapabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath());
155-
serverCapabilities.setCapability(AndroidMobileCapabilityType.CHROMEDRIVER_EXECUTABLE,
158+
serverCapabilities.setCapability(AndroidMobileCapabilityType.CHROMEDRIVER_EXECUTABLE,
156159
chrome.getAbsolutePath()); //this capability set can be used for all cases
157160

158161
AppiumServiceBuilder builder = new AppiumServiceBuilder().
159162
withCapabilities(serverCapabilities);
160163
AppiumDriverLocalService service = builder.build();
161164

162165
DesiredCapabilities clientCapabilities = new DesiredCapabilities();
163-
clientCapabilities.setCapability(AndroidMobileCapabilityType.APP_PACKAGE,
166+
clientCapabilities.setCapability(AndroidMobileCapabilityType.APP_PACKAGE,
164167
"io.appium.android.apis");
165-
clientCapabilities.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY,
168+
clientCapabilities.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY,
166169
".view.WebView1");
167170
```
168171

169172
then
170173

171174
```java
172-
AndroidDriver<MobileElement> driver =
175+
AndroidDriver<MobileElement> driver =
173176
new AndroidDriver<>(service, clientCapabilities);
174177
```
175178

176-
or
179+
or
177180

178181
```java
179-
AndroidDriver<MobileElement> driver =
182+
AndroidDriver<MobileElement> driver =
180183
new AndroidDriver<>(builder, clientCapabilities);
181184
```
182185

183-
or
186+
or
184187

185188
```java
186189
service.start();
187-
AndroidDriver<MobileElement> driver =
190+
AndroidDriver<MobileElement> driver =
188191
new AndroidDriver<>(service.getUrl(), clientCapabilities);
189192
```
190193

@@ -219,7 +222,7 @@ public AndroidDriver(org.openqa.selenium.remote.http.HttpClient.Factory httpClie
219222
org.openqa.selenium.Capabilities desiredCapabilities)
220223

221224
public AndroidDriver(org.openqa.selenium.Capabilities desiredCapabilities)
222-
```
225+
```
223226

224227
```java
225228
public IOSDriver(URL remoteAddress,
@@ -247,20 +250,20 @@ public IOSDriver(org.openqa.selenium.remote.http.HttpClient.Factory httpClientFa
247250
org.openqa.selenium.Capabilities desiredCapabilities)
248251

249252
public IOSDriver(org.openqa.selenium.Capabilities desiredCapabilities)
250-
```
253+
```
251254

252255
An instance of __AppiumDriverLocalService__ which has passed through constructors will be stopped when
253256

254257
```java
255258
driver.quit();
256-
```
259+
```
257260

258261
If it is necessary to keep the service alive during a long time then something like that is available
259262

260263
```java
261264
service.start();
262-
265+
263266
....
264267

265268
new IOSDriver<MobileElement>(service.getUrl(), capabilities)
266-
```
269+
```
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
15+
package io.appium.java_client.internal;
16+
17+
import org.openqa.selenium.SessionNotCreatedException;
18+
19+
import java.net.InetAddress;
20+
import java.net.URL;
21+
import java.net.UnknownHostException;
22+
23+
/**
24+
* Validates URLs used with {@code overrideServerUrl} (for example after a {@code directConnect}
25+
* response). Refuses the override when any resolved address is loopback, link-local (including
26+
* typical cloud metadata IPv4 link-local space), unspecified, or multicast.
27+
*
28+
* <p>This is not a full &quot;public internet only&quot; policy: RFC 1918 private space, shared
29+
* address space ({@code 100.64.0.0/10}), IPv6 unique-local ({@code fc00::/7}), and other addresses
30+
* outside the checks above are still accepted. Stricter control belongs at the application or
31+
* network layer (allowlists, egress rules, etc.).
32+
*/
33+
public final class DirectConnectUrlSafety {
34+
35+
private DirectConnectUrlSafety() {
36+
}
37+
38+
/**
39+
* Ensures the given URL's host does not resolve to loopback, link-local, unspecified, or
40+
* multicast addresses (see class documentation for what is still allowed).
41+
*
42+
* @param url candidate server URL
43+
* @throws SessionNotCreatedException if the host is missing, cannot be resolved, or resolves
44+
* to any address in the disallowed categories
45+
*/
46+
public static void requireSafeOverrideTarget(URL url) throws SessionNotCreatedException {
47+
String host = url.getHost();
48+
if (host == null || host.isEmpty()) {
49+
throw new SessionNotCreatedException(
50+
"Refusing to override the server URL: the URL must include a non-empty host.");
51+
}
52+
53+
final InetAddress[] addresses;
54+
try {
55+
addresses = InetAddress.getAllByName(host);
56+
} catch (UnknownHostException e) {
57+
throw new SessionNotCreatedException(
58+
"Refusing to override the server URL: cannot resolve host '" + host + "'.", e);
59+
}
60+
61+
for (InetAddress address : addresses) {
62+
if (isDisallowed(address)) {
63+
throw new SessionNotCreatedException(String.format(
64+
"Refusing to override the server URL: host '%s' resolves to %s, which is not "
65+
+ "allowed (loopback, link-local, unspecified, or multicast address).",
66+
host,
67+
address.getHostAddress()));
68+
}
69+
}
70+
}
71+
72+
private static boolean isDisallowed(InetAddress address) {
73+
return address.isLoopbackAddress()
74+
|| address.isLinkLocalAddress()
75+
|| address.isAnyLocalAddress()
76+
|| address.isMulticastAddress();
77+
}
78+
}

src/main/java/io/appium/java_client/remote/AppiumCommandExecutor.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.google.common.base.Throwables;
2020
import io.appium.java_client.AppiumClientConfig;
21+
import io.appium.java_client.internal.DirectConnectUrlSafety;
2122
import io.appium.java_client.internal.ReflectionHelpers;
2223
import lombok.Getter;
2324
import org.jspecify.annotations.NullMarked;
@@ -155,9 +156,16 @@ protected HttpClient getClient() {
155156
* Override the http client in the HttpCommandExecutor class with a new http client instance with the given URL.
156157
* It uses the same http client factory and client config for the new http client instance
157158
* if the constructor got them.
158-
* @param serverUrl A url to override.
159+
*
160+
* @param serverUrl URL to use for subsequent HTTP requests. Before switching clients, the host is
161+
* resolved and the override is refused if any resolved address is loopback,
162+
* link-local (including IPv4 link-local such as metadata-service ranges),
163+
* unspecified ({@code 0.0.0.0} / {@code ::}), or multicast. Private (RFC 1918),
164+
* carrier-grade NAT ({@code 100.64.0.0/10}), IPv6 unique-local ({@code fc00::/7}),
165+
* and other non-special addresses are not rejected by this check.
159166
*/
160167
protected void overrideServerUrl(URL serverUrl) {
168+
DirectConnectUrlSafety.requireSafeOverrideTarget(serverUrl);
161169
HttpClient newClient = getHttpClientFactory().createClient(appiumClientConfig.baseUrl(serverUrl));
162170
setPrivateFieldValue(HttpCommandExecutor.class, "client", newClient);
163171
}

src/main/java/io/appium/java_client/service/local/AppiumDriverLocalService.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,24 @@ public URL getUrl() {
118118
return basePath == null ? url : addSuffix(url, basePath);
119119
}
120120

121+
/**
122+
* System property checked by Selenium {@code DriverFinder} when the executable path is not set
123+
* explicitly. Matches {@link AppiumServiceBuilder#NODE_PATH}.
124+
*/
125+
@Override
126+
public String getDriverProperty() {
127+
return AppiumServiceBuilder.NODE_PATH;
128+
}
129+
130+
/**
131+
* Environment variable checked by Selenium {@code DriverFinder} when the executable path is not
132+
* set explicitly. Matches {@link AppiumServiceBuilder#NODE_PATH}.
133+
*/
134+
@Override
135+
public String getDriverEnvironmentVariable() {
136+
return AppiumServiceBuilder.NODE_PATH;
137+
}
138+
121139
@Override
122140
public boolean isRunning() {
123141
lock.lock();

src/main/java/io/appium/java_client/service/local/AppiumServiceBuilder.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,10 @@ public final class AppiumServiceBuilder
6464
public static final String APPIUM_PATH = "APPIUM_BINARY_PATH";
6565

6666
/**
67-
* The environmental variable used to define
68-
* the path to executable NodeJS file (node.exe for WIN and
69-
* node for Linux/MacOS X).
67+
* System property and environment variable name for the Node.js executable path
68+
* (node.exe on Windows, node on Linux/macOS).
7069
*/
71-
private static final String NODE_PATH = "NODE_BINARY_PATH";
70+
public static final String NODE_PATH = "NODE_BINARY_PATH";
7271

7372
public static final String BROADCAST_IP4_ADDRESS = "0.0.0.0";
7473
public static final String BROADCAST_IP6_ADDRESS = "::";

0 commit comments

Comments
 (0)