Skip to content

Commit cbee747

Browse files
committed
#972: add trustInsecureDownloadRoot parameter to plugins
While adding unit tests by default, a bug was discovered when downloading via proxy with BasicAuth. This has also been fixed.
1 parent f4675f8 commit cbee747

File tree

13 files changed

+331
-45
lines changed

13 files changed

+331
-45
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ Last public release: [![Maven Central](https://maven-badges.herokuapp.com/maven-
44

55
## Changelog
66

7+
### 1.12.2
8+
9+
* add Dependency: wiremock-jre8 (2.32.0)
10+
* add code coverage to ``DefaultFileDownloader``
11+
* fix bug on download files over proxy with Basic Auth
12+
* add new configuration ``trustInsecureDownloadRoot`` to ignore insecure HTTPS downloads
13+
714
### 1.12.1
815

916
* update Dependency: Jackson (2.13.0), Mockito (4.1.0), JUnit (5.8.1), Hamcrest (2.2; now a direct dependency)

README.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ present).
104104

105105
<!-- optional: where to download node and npm from. Defaults to https://nodejs.org/dist/ -->
106106
<downloadRoot>http://myproxy.example.org/nodejs/</downloadRoot>
107+
108+
<!-- optional: ignore insecure HTTPS download connections -->
109+
<trustInsecureDownloadRoot>false</trustInsecureDownloadRoot>
107110
</configuration>
108111
</plugin>
109112
```
@@ -120,6 +123,8 @@ You can also specify separate download roots for npm and node as they are stored
120123
<serverId>server001</serverId>
121124
<!-- optional: where to download npm from. Defaults to https://registry.npmjs.org/npm/-/ -->
122125
<npmDownloadRoot>https://myproxy.example.org/npm/</npmDownloadRoot>
126+
<!-- optional: ignore insecure HTTPS download connections -->
127+
<trustInsecureDownloadRoot>false</trustInsecureDownloadRoot>
123128
</configuration>
124129
</plugin>
125130
```
@@ -166,7 +171,9 @@ https://github.com/eirslett/frontend-maven-plugin/blob/master/frontend-maven-plu
166171
<!-- optional: where to download node from. Defaults to https://nodejs.org/dist/ -->
167172
<nodeDownloadRoot>http://myproxy.example.org/nodejs/</nodeDownloadRoot>
168173
<!-- optional: where to download yarn from. Defaults to https://github.com/yarnpkg/yarn/releases/download/ -->
169-
<yarnDownloadRoot>http://myproxy.example.org/yarn/</yarnDownloadRoot>
174+
<yarnDownloadRoot>http://myproxy.example.org/yarn/</yarnDownloadRoot>
175+
<!-- optional: ignore insecure HTTPS download connections -->
176+
<trustInsecureDownloadRoot>false</trustInsecureDownloadRoot>
170177
</configuration>
171178
</plugin>
172179
```

frontend-maven-plugin/src/main/java/com/github/eirslett/maven/plugins/frontend/mojo/InstallNodeAndNpmMojo.java

+10
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ public final class InstallNodeAndNpmMojo extends AbstractFrontendMojo {
3636
@Deprecated
3737
private String downloadRoot;
3838

39+
/**
40+
* Will trust insecure download targets. This flag applies to both the {@link #nodeDownloadRoot} and {@link #npmDownloadRoot}.
41+
*/
42+
@Parameter(property = "trustInsecureDownloadRoot", required = false, defaultValue = "false")
43+
private boolean trustInsecureDownloadRoot;
44+
3945
/**
4046
* The version of Node.js to install. IMPORTANT! Most Node.js version names start with 'v', for example 'v0.10.18'
4147
*/
@@ -81,6 +87,7 @@ public void execute(FrontendPluginFactory factory) throws InstallationException
8187
factory.getNodeInstaller(proxyConfig)
8288
.setNodeVersion(nodeVersion)
8389
.setNodeDownloadRoot(nodeDownloadRoot)
90+
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
8491
.setNpmVersion(npmVersion)
8592
.setUserName(server.getUsername())
8693
.setPassword(server.getPassword())
@@ -89,19 +96,22 @@ public void execute(FrontendPluginFactory factory) throws InstallationException
8996
.setNodeVersion(nodeVersion)
9097
.setNpmVersion(npmVersion)
9198
.setNpmDownloadRoot(npmDownloadRoot)
99+
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
92100
.setUserName(server.getUsername())
93101
.setPassword(server.getPassword())
94102
.install();
95103
} else {
96104
factory.getNodeInstaller(proxyConfig)
97105
.setNodeVersion(nodeVersion)
98106
.setNodeDownloadRoot(nodeDownloadRoot)
107+
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
99108
.setNpmVersion(npmVersion)
100109
.install();
101110
factory.getNPMInstaller(proxyConfig)
102111
.setNodeVersion(this.nodeVersion)
103112
.setNpmVersion(this.npmVersion)
104113
.setNpmDownloadRoot(npmDownloadRoot)
114+
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
105115
.install();
106116
}
107117
}

frontend-maven-plugin/src/main/java/com/github/eirslett/maven/plugins/frontend/mojo/InstallNodeAndPnpmMojo.java

+10
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ public final class InstallNodeAndPnpmMojo extends AbstractFrontendMojo {
3636
@Deprecated
3737
private String downloadRoot;
3838

39+
/**
40+
* Will trust insecure download targets. This flag applies to both the {@link #nodeDownloadRoot} and {@link #pnpmDownloadRoot}.
41+
*/
42+
@Parameter(property = "trustInsecureDownloadRoot", required = false, defaultValue = "false")
43+
private boolean trustInsecureDownloadRoot;
44+
3945
/**
4046
* The version of Node.js to install. IMPORTANT! Most Node.js version names start with 'v', for example 'v0.10.18'
4147
*/
@@ -81,25 +87,29 @@ public void execute(FrontendPluginFactory factory) throws InstallationException
8187
factory.getNodeInstaller(proxyConfig)
8288
.setNodeVersion(nodeVersion)
8389
.setNodeDownloadRoot(nodeDownloadRoot)
90+
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
8491
.setNpmVersion(pnpmVersion)
8592
.setUserName(server.getUsername())
8693
.setPassword(server.getPassword())
8794
.install();
8895
factory.getPNPMInstaller(proxyConfig)
8996
.setPnpmVersion(pnpmVersion)
9097
.setPnpmDownloadRoot(npmDownloadRoot)
98+
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
9199
.setUserName(server.getUsername())
92100
.setPassword(server.getPassword())
93101
.install();
94102
} else {
95103
factory.getNodeInstaller(proxyConfig)
96104
.setNodeVersion(nodeVersion)
97105
.setNodeDownloadRoot(nodeDownloadRoot)
106+
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
98107
.setNpmVersion(pnpmVersion)
99108
.install();
100109
factory.getPNPMInstaller(proxyConfig)
101110
.setPnpmVersion(this.pnpmVersion)
102111
.setPnpmDownloadRoot(npmDownloadRoot)
112+
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
103113
.install();
104114
}
105115
}

frontend-maven-plugin/src/main/java/com/github/eirslett/maven/plugins/frontend/mojo/InstallNodeAndYarnMojo.java

+8
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ public final class InstallNodeAndYarnMojo extends AbstractFrontendMojo {
3333
defaultValue = YarnInstaller.DEFAULT_YARN_DOWNLOAD_ROOT)
3434
private String yarnDownloadRoot;
3535

36+
/**
37+
* Will trust insecure download targets. This flag applies to both the {@link #nodeDownloadRoot} and {@link #yarnDownloadRoot}.
38+
*/
39+
@Parameter(property = "trustInsecureDownloadRoot", required = false, defaultValue = "false")
40+
private boolean trustInsecureDownloadRoot;
41+
3642
/**
3743
* The version of Node.js to install. IMPORTANT! Most Node.js version names start with 'v', for example
3844
* 'v0.10.18'
@@ -91,9 +97,11 @@ public void execute(FrontendPluginFactory factory) throws InstallationException
9197
Server server = MojoUtils.decryptServer(this.serverId, this.session, this.decrypter);
9298
if (null != server) {
9399
factory.getNodeInstaller(proxyConfig).setNodeDownloadRoot(this.nodeDownloadRoot)
100+
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
94101
.setNodeVersion(this.nodeVersion).setPassword(server.getPassword())
95102
.setUserName(server.getUsername()).install();
96103
factory.getYarnInstaller(proxyConfig).setYarnDownloadRoot(this.yarnDownloadRoot)
104+
.setTrustInsecureDownloadRoot(trustInsecureDownloadRoot)
97105
.setYarnVersion(this.yarnVersion).setUserName(server.getUsername())
98106
.setPassword(server.getPassword()).setIsYarnBerry(isYarnrcYamlFilePresent()).install();
99107
} else {

frontend-plugin-core/pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@
7979
<version>4.1.0</version>
8080
<scope>test</scope>
8181
</dependency>
82+
<dependency>
83+
<groupId>com.github.tomakehurst</groupId>
84+
<artifactId>wiremock-jre8</artifactId>
85+
<version>2.32.0</version>
86+
<scope>test</scope>
87+
</dependency>
8288
</dependencies>
8389

8490
<build>

frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/FileDownloader.java

+46-22
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,31 @@
88
import java.net.URL;
99
import java.nio.channels.Channels;
1010
import java.nio.channels.ReadableByteChannel;
11+
import java.security.KeyManagementException;
12+
import java.security.KeyStoreException;
13+
import java.security.NoSuchAlgorithmException;
14+
15+
import javax.net.ssl.SSLContext;
1116

1217
import org.apache.commons.io.FileUtils;
1318
import org.apache.commons.io.FilenameUtils;
1419
import org.apache.http.HttpHost;
1520
import org.apache.http.auth.AuthScope;
21+
import org.apache.http.auth.AuthState;
1622
import org.apache.http.auth.UsernamePasswordCredentials;
1723
import org.apache.http.client.AuthCache;
1824
import org.apache.http.client.CredentialsProvider;
1925
import org.apache.http.client.config.RequestConfig;
2026
import org.apache.http.client.methods.CloseableHttpResponse;
2127
import org.apache.http.client.methods.HttpGet;
2228
import org.apache.http.client.protocol.HttpClientContext;
29+
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
2330
import org.apache.http.impl.auth.BasicScheme;
2431
import org.apache.http.impl.client.BasicAuthCache;
2532
import org.apache.http.impl.client.BasicCredentialsProvider;
2633
import org.apache.http.impl.client.CloseableHttpClient;
2734
import org.apache.http.impl.client.HttpClients;
35+
import org.apache.http.ssl.SSLContextBuilder;
2836
import org.codehaus.plexus.util.StringUtils;
2937
import org.slf4j.Logger;
3038
import org.slf4j.LoggerFactory;
@@ -41,7 +49,7 @@ public DownloadException(String message){
4149
}
4250

4351
interface FileDownloader {
44-
void download(String downloadUrl, String destination, String userName, String password) throws DownloadException;
52+
void download(String downloadUrl, String destination, String userName, String password, boolean trustInsecureDownloadRoot) throws DownloadException;
4553
}
4654

4755
final class DefaultFileDownloader implements FileDownloader {
@@ -54,7 +62,7 @@ public DefaultFileDownloader(ProxyConfig proxyConfig){
5462
}
5563

5664
@Override
57-
public void download(String downloadUrl, String destination, String userName, String password) throws DownloadException {
65+
public void download(String downloadUrl, String destination, String userName, String password, boolean trustInsecureDownloadRoot) throws DownloadException {
5866
// force tls to 1.2 since github removed weak cryptographic standards
5967
// https://blog.github.com/2018-02-02-weak-cryptographic-standards-removal-notice/
6068
System.setProperty("https.protocols", "TLSv1.2");
@@ -66,7 +74,7 @@ public void download(String downloadUrl, String destination, String userName, St
6674
FileUtils.copyFile(new File(downloadURI), new File(destination));
6775
}
6876
else {
69-
CloseableHttpResponse response = execute(fixedDownloadUrl, userName, password);
77+
CloseableHttpResponse response = execute(fixedDownloadUrl, userName, password, trustInsecureDownloadRoot);
7078
int statusCode = response.getStatusLine().getStatusCode();
7179
if(statusCode != 200){
7280
throw new DownloadException("Got error code "+ statusCode +" from the server.");
@@ -82,12 +90,18 @@ public void download(String downloadUrl, String destination, String userName, St
8290
}
8391
}
8492

85-
private CloseableHttpResponse execute(String requestUrl, String userName, String password) throws IOException {
93+
private CloseableHttpResponse execute(String requestUrl, String userName, String password, boolean trustInsecureDownloadRoot)
94+
throws IOException, DownloadException {
8695
CloseableHttpResponse response;
96+
SSLContext sslContext = null;
97+
if (trustInsecureDownloadRoot) {
98+
LOGGER.info("Trust insecure download enabled.");
99+
sslContext = makeSSLContextForTrustedInsecureDownloads();
100+
}
87101
Proxy proxy = proxyConfig.getProxyForUrl(requestUrl);
88102
if (proxy != null) {
89103
LOGGER.info("Downloading via proxy " + proxy.toString());
90-
return executeViaProxy(proxy, requestUrl);
104+
return executeViaProxy(proxy, sslContext, requestUrl);
91105
} else {
92106
LOGGER.info("No proxy was configured, downloading directly");
93107
if (StringUtils.isNotEmpty(userName) && StringUtils.isNotEmpty(password)) {
@@ -100,40 +114,50 @@ private CloseableHttpResponse execute(String requestUrl, String userName, String
100114
aURL.getPort(),
101115
userName,
102116
password);
103-
response = buildHttpClient(credentialsProvider).execute(new HttpGet(requestUrl),localContext);
117+
response = buildHttpClient(sslContext, credentialsProvider).execute(new HttpGet(requestUrl),localContext);
104118
} else {
105-
response = buildHttpClient(null).execute(new HttpGet(requestUrl));
119+
response = buildHttpClient(sslContext, null).execute(new HttpGet(requestUrl));
106120
}
107121
}
108122
return response;
109123
}
110124

111-
private CloseableHttpResponse executeViaProxy(Proxy proxy, String requestUrl) throws IOException {
112-
final CloseableHttpClient proxyClient;
113-
if (proxy.useAuthentication()){
114-
proxyClient = buildHttpClient(makeCredentialsProvider(proxy.host,proxy.port,proxy.username,proxy.password));
125+
private CloseableHttpResponse executeViaProxy(Proxy proxy, SSLContext sslContext, String requestUrl) throws IOException {
126+
final HttpGet request = new HttpGet(requestUrl);
127+
request.setConfig(RequestConfig.custom()
128+
.setProxy(new HttpHost(proxy.host, proxy.port))
129+
.build());
130+
final CloseableHttpClient proxyClient = buildHttpClient(sslContext, null);
131+
if (proxy.useAuthentication()) {
132+
final AuthState authState = new AuthState();
133+
authState.update(new BasicScheme(), new UsernamePasswordCredentials(proxy.username, proxy.password));
134+
final HttpClientContext httpContext = HttpClientContext.create();
135+
httpContext.setAttribute(HttpClientContext.PROXY_AUTH_STATE, authState);
136+
return proxyClient.execute(request, httpContext);
115137
} else {
116-
proxyClient = buildHttpClient(null);
138+
return proxyClient.execute(request);
117139
}
118-
119-
final HttpHost proxyHttpHost = new HttpHost(proxy.host, proxy.port);
120-
121-
final RequestConfig requestConfig = RequestConfig.custom().setProxy(proxyHttpHost).build();
122-
123-
final HttpGet request = new HttpGet(requestUrl);
124-
request.setConfig(requestConfig);
125-
126-
return proxyClient.execute(request);
127140
}
128141

129-
private CloseableHttpClient buildHttpClient(CredentialsProvider credentialsProvider) {
142+
private CloseableHttpClient buildHttpClient(SSLContext sslContext, CredentialsProvider credentialsProvider) {
130143
return HttpClients.custom()
144+
.setSSLContext(sslContext)
131145
.disableContentCompression()
132146
.useSystemProperties()
133147
.setDefaultCredentialsProvider(credentialsProvider)
134148
.build();
135149
}
136150

151+
private SSLContext makeSSLContextForTrustedInsecureDownloads() throws DownloadException {
152+
try {
153+
return SSLContextBuilder.create()
154+
.loadTrustMaterial(TrustSelfSignedStrategy.INSTANCE)
155+
.build();
156+
} catch (final NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
157+
throw new DownloadException("Unable to create SSLContext to trust insecure downloads.", e);
158+
}
159+
}
160+
137161
private CredentialsProvider makeCredentialsProvider(String host, int port, String username, String password) {
138162
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
139163
credentialsProvider.setCredentials(

frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/NPMInstaller.java

+12-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public class NPMInstaller {
2020

2121
private String nodeVersion, npmVersion, npmDownloadRoot, userName, password;
2222

23+
private boolean trustInsecureDownloadRoot;
24+
2325
private final Logger logger;
2426

2527
private final InstallConfig config;
@@ -50,6 +52,11 @@ public NPMInstaller setNpmDownloadRoot(String npmDownloadRoot) {
5052
return this;
5153
}
5254

55+
public NPMInstaller setTrustInsecureDownloadRoot(boolean trustInsecureDownloadRoot) {
56+
this.trustInsecureDownloadRoot = trustInsecureDownloadRoot;
57+
return this;
58+
}
59+
5360
public NPMInstaller setUserName(String userName) {
5461
this.userName = userName;
5562
return this;
@@ -122,7 +129,7 @@ private void installNpm() throws InstallationException {
122129

123130
File archive = this.config.getCacheResolver().resolve(cacheDescriptor);
124131

125-
downloadFileIfMissing(downloadUrl, archive, this.userName, this.password);
132+
downloadFileIfMissing(downloadUrl, archive, this.userName, this.password, this.trustInsecureDownloadRoot);
126133

127134
File installDirectory = getNodeInstallDirectory();
128135
File nodeModulesDirectory = new File(installDirectory, "node_modules");
@@ -218,16 +225,16 @@ private void extractFile(File archive, File destinationDirectory) throws Archive
218225
this.archiveExtractor.extract(archive.getPath(), destinationDirectory.getPath());
219226
}
220227

221-
private void downloadFileIfMissing(String downloadUrl, File destination, String userName, String password)
228+
private void downloadFileIfMissing(String downloadUrl, File destination, String userName, String password, boolean trustInsecureDownloadRoot)
222229
throws DownloadException {
223230
if (!destination.exists()) {
224-
downloadFile(downloadUrl, destination, userName, password);
231+
downloadFile(downloadUrl, destination, userName, password, trustInsecureDownloadRoot);
225232
}
226233
}
227234

228-
private void downloadFile(String downloadUrl, File destination, String userName, String password)
235+
private void downloadFile(String downloadUrl, File destination, String userName, String password, boolean trustInsecureDownloadRoot)
229236
throws DownloadException {
230237
this.logger.info("Downloading {} to {}", downloadUrl, destination);
231-
this.fileDownloader.download(downloadUrl, destination.getPath(), userName, password);
238+
this.fileDownloader.download(downloadUrl, destination.getPath(), userName, password, trustInsecureDownloadRoot);
232239
}
233240
}

0 commit comments

Comments
 (0)