1
1
package apoc .util ;
2
2
3
3
import apoc .ApocConfig ;
4
+ import inet .ipaddr .IPAddressString ;
5
+ import junit .framework .TestCase ;
4
6
import org .apache .commons .io .IOUtils ;
5
- import org .junit .After ;
7
+ import org .junit .Assert ;
6
8
import org .junit .Assume ;
7
- import org .junit .Before ;
8
- import org .junit .Ignore ;
9
- import org .junit .Rule ;
10
9
import org .junit .Test ;
11
- import org .junit .rules .TestName ;
10
+ import org .junit .jupiter .api .AfterEach ;
11
+ import org .neo4j .configuration .Config ;
12
+ import org .neo4j .configuration .GraphDatabaseInternalSettings ;
12
13
import org .testcontainers .containers .GenericContainer ;
13
- import org .testcontainers .containers .wait .strategy .Wait ;
14
14
15
15
import java .io .IOException ;
16
+ import java .net .HttpURLConnection ;
17
+ import java .net .URL ;
16
18
import java .nio .charset .Charset ;
19
+ import java .util .ArrayList ;
20
+ import java .util .List ;
17
21
18
22
import static org .junit .Assert .assertEquals ;
19
23
import static org .junit .Assert .assertTrue ;
24
+ import static org .mockito .Mockito .mock ;
25
+ import static org .mockito .Mockito .when ;
20
26
21
27
public class UtilIT {
22
-
23
- @ Rule
24
- public TestName testName = new TestName ();
25
-
26
28
private GenericContainer httpServer ;
27
29
28
- private static final String WITH_URL_LOCATION = "WithUrlLocation" ;
29
- private static final String WITH_FILE_LOCATION = "WithFileLocation" ;
30
-
31
- @ Before
32
- public void setUp () {
33
- new ApocConfig (); // empty test configuration, ensure ApocConfig.apocConfig() can be used
34
- TestUtil .ignoreException (() -> {
35
- httpServer = new GenericContainer ("alpine" )
36
- .withCommand ("/bin/sh" , "-c" , String .format ("while true; do { echo -e 'HTTP/1.1 301 Moved Permanently\\ r\\ nLocation: %s'; echo ; } | nc -l -p 8000; done" ,
37
- testName .getMethodName ().endsWith (WITH_URL_LOCATION ) ? "http://www.google.com" : "file:/etc/passwd" ))
38
- .withExposedPorts (8000 );
39
- httpServer .waitingFor (Wait .forHttp ("/" )
40
- .forStatusCode (301 ));
41
- httpServer .start ();
42
- }, Exception .class );
30
+ private GenericContainer setUpServer (Config neo4jConfig , String redirectURL ) {
31
+ new ApocConfig (neo4jConfig );
32
+ GenericContainer httpServer = new GenericContainer ("alpine" )
33
+ .withCommand ("/bin/sh" , "-c" , String .format ("while true; do { echo -e 'HTTP/1.1 301 Moved Permanently\\ r\\ nLocation: %s'; echo ; } | nc -l -p 8000; done" ,
34
+ redirectURL ))
35
+ .withExposedPorts (8000 );
36
+ httpServer .start ();
43
37
Assume .assumeNotNull (httpServer );
44
38
Assume .assumeTrue (httpServer .isRunning ());
39
+ return httpServer ;
45
40
}
46
41
47
- @ After
42
+ @ AfterEach
48
43
public void tearDown () {
49
44
if (httpServer != null ) {
50
45
httpServer .stop ();
@@ -53,8 +48,42 @@ public void tearDown() {
53
48
54
49
@ Test
55
50
public void redirectShouldWorkWhenProtocolNotChangesWithUrlLocation () throws IOException {
51
+ httpServer = setUpServer (null , "http://www.google.com" );
56
52
// given
57
- String url = getServerUrl ();
53
+ String url = getServerUrl (httpServer );
54
+
55
+ // when
56
+ String page = IOUtils .toString (Util .openInputStream (url , null , null , null ), Charset .forName ("UTF-8" ));
57
+
58
+ // then
59
+ assertTrue (page .contains ("<title>Google</title>" ));
60
+ }
61
+
62
+ @ Test
63
+ public void redirectWithBlockedIPsWithUrlLocation () {
64
+ List <IPAddressString > blockedIPs = List .of (new IPAddressString ("127.168.0.1/8" ));
65
+
66
+ Config neo4jConfig = mock (Config .class );
67
+ when (neo4jConfig .get (GraphDatabaseInternalSettings .cypher_ip_blocklist )).thenReturn (blockedIPs );
68
+
69
+ httpServer = setUpServer (neo4jConfig , "http://127.168.0.1" );
70
+ String url = getServerUrl (httpServer );
71
+
72
+ IOException e = Assert .assertThrows (IOException .class ,
73
+ () -> Util .openInputStream (url , null , null , null )
74
+ );
75
+ TestCase .assertTrue (e .getMessage ().contains ("access to /127.168.0.1 is blocked via the configuration property internal.dbms.cypher_ip_blocklist" ));
76
+ }
77
+
78
+ @ Test
79
+ public void redirectWithProtocolUpgradeIsAllowed () throws IOException {
80
+ List <IPAddressString > blockedIPs = List .of (new IPAddressString ("127.168.0.1/8" ));
81
+
82
+ Config neo4jConfig = mock (Config .class );
83
+ when (neo4jConfig .get (GraphDatabaseInternalSettings .cypher_ip_blocklist )).thenReturn (blockedIPs );
84
+
85
+ httpServer = setUpServer (neo4jConfig , "https://www.google.com" );
86
+ String url = getServerUrl (httpServer );
58
87
59
88
// when
60
89
String page = IOUtils .toString (Util .openInputStream (url , null , null , null ), Charset .forName ("UTF-8" ));
@@ -63,12 +92,52 @@ public void redirectShouldWorkWhenProtocolNotChangesWithUrlLocation() throws IOE
63
92
assertTrue (page .contains ("<title>Google</title>" ));
64
93
}
65
94
66
- @ Ignore
95
+ @ Test
96
+ public void redirectWithProtocolDowngradeIsNotAllowed () throws IOException {
97
+ HttpURLConnection mockCon = mock (HttpURLConnection .class );
98
+ when (mockCon .getResponseCode ()).thenReturn (302 );
99
+ when (mockCon .getHeaderField ("Location" )).thenReturn ("http://127.168.0.1" );
100
+ when (mockCon .getURL ()).thenReturn (new URL ("https://127.0.0.0" ));
101
+
102
+ RuntimeException e = Assert .assertThrows (RuntimeException .class ,
103
+ () -> Util .isRedirect (mockCon )
104
+ );
105
+
106
+ TestCase .assertTrue (e .getMessage ().contains ("The redirect URI has a different protocol: http://127.168.0.1" ));
107
+ }
108
+
109
+ @ Test
110
+ public void shouldFailForExceedingRedirectLimit () {
111
+ Config neo4jConfig = mock (Config .class );
112
+
113
+ httpServer = setUpServer (neo4jConfig , "https://127.0.0.0" );
114
+ String url = getServerUrl (httpServer );
115
+
116
+ ArrayList <GenericContainer > servers = new ArrayList <>();
117
+ for (int i = 1 ; i <= 10 ; i ++) {
118
+ GenericContainer server = setUpServer (neo4jConfig , url );
119
+ servers .add (server );
120
+ url = getServerUrl (server );
121
+ }
122
+
123
+ String finalUrl = url ;
124
+ IOException e = Assert .assertThrows (IOException .class ,
125
+ () -> Util .openInputStream (finalUrl , null , null , null )
126
+ );
127
+
128
+ TestCase .assertTrue (e .getMessage ().contains ("Redirect limit exceeded" ));
129
+
130
+ for (GenericContainer server : servers ) {
131
+ server .stop ();
132
+ }
133
+ }
134
+
67
135
@ Test (expected = RuntimeException .class )
68
136
public void redirectShouldThrowExceptionWhenProtocolChangesWithFileLocation () throws IOException {
69
137
try {
138
+ httpServer = setUpServer (null , "file:/etc/passwd" );
70
139
// given
71
- String url = getServerUrl ();
140
+ String url = getServerUrl (httpServer );
72
141
73
142
// when
74
143
Util .openInputStream (url , null , null , null );
@@ -79,7 +148,7 @@ public void redirectShouldThrowExceptionWhenProtocolChangesWithFileLocation() th
79
148
}
80
149
}
81
150
82
- private String getServerUrl () {
151
+ private String getServerUrl (GenericContainer httpServer ) {
83
152
return String .format ("http://%s:%s" , httpServer .getContainerIpAddress (), httpServer .getMappedPort (8000 ));
84
153
}
85
154
}
0 commit comments