changeset 477:ee228e860ea2

ST-60 in progress
author Devel 1
date Tue, 08 Aug 2017 09:33:22 +0200
parents a49913dd5064
children ce8d1a1cda94
files stress-tester/pom.xml stress-tester/src/main/java/com/passus/st/client/http/filter/HttpCsrfFormExtractor.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpCsrfFormExtractorTest.java
diffstat 3 files changed, 237 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/stress-tester/pom.xml	Tue Aug 08 09:30:02 2017 +0200
+++ b/stress-tester/pom.xml	Tue Aug 08 09:33:22 2017 +0200
@@ -126,6 +126,12 @@
         </dependency>
 
         <dependency>
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+            <version>1.9.2</version>
+        </dependency>
+
+        <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
             <version>6.9.10</version>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpCsrfFormExtractor.java	Tue Aug 08 09:33:22 2017 +0200
@@ -0,0 +1,134 @@
+package com.passus.st.client.http.filter;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.nodes.FormElement;
+import org.jsoup.select.Elements;
+
+/**
+ *
+ * @author mikolaj.podbielski
+ */
+public class HttpCsrfFormExtractor {
+
+    private static final String TAG_FORM = "form";
+
+    private static final String FORM_START = "<form";
+    private static final String FORM_END = "</form>";
+
+    private static final String ATTR_ACTION = "action";
+    private static final String ATTR_METHOD = "method";
+    private static final String ATTR_NAME = "name";
+    private static final String ATTR_VALUE = "value";
+
+    // TODO: obsługiwać '< form' itp
+    static List<FormBoundary> scan(String document) {
+        document = document.toLowerCase();
+        List<FormBoundary> result = new ArrayList<>();
+
+        int startPos = 0;
+        while (true) {
+            startPos = document.indexOf(FORM_START, startPos);
+            if (startPos < 0) {
+                break;
+            }
+
+            int endPos = document.indexOf(FORM_END, startPos);
+            if (endPos < 0) {
+                break;
+            }
+            endPos += FORM_END.length();
+
+            result.add(new FormBoundary(startPos, endPos));
+            startPos = endPos;
+        }
+
+        return result;
+    }
+
+    static TokenEntry extract(String formString, String inputId, String inputName) {
+        Document root = Jsoup.parse(formString);
+        Elements forms = root.getElementsByTag(TAG_FORM);
+        if (forms.size() != 1) {
+            throw new IllegalArgumentException("Given fragment is not a form.");
+        }
+        FormElement form = (FormElement) forms.get(0);
+        Elements inputs = form.elements();
+
+        Element tokenInput = null;
+        for (Element input : inputs) {
+            if (!input.attr("type").equals("hidden")) {
+                continue;
+            }
+            if (inputId != null) {
+                if (!inputId.equals(input.id())) {
+                    continue;
+                }
+            }
+            if (inputName != null) {
+                if (!inputName.equals(input.attr("name"))) {
+                    continue;
+                }
+            }
+            tokenInput = input;
+            break;
+        }
+
+        if (tokenInput == null) {
+            return null;
+        }
+
+        String action = form.attr(ATTR_ACTION);
+        String method = form.attr(ATTR_METHOD);
+        String name = form.attr(ATTR_NAME);
+        String value = tokenInput.attr(ATTR_VALUE);
+        return new TokenEntry(action, method, name, value);
+    }
+
+    public static List<TokenEntry> extractAll(String document, String inputId, String inputName) {
+        List<FormBoundary> forms = scan(document);
+        List<TokenEntry> result = new ArrayList<>(forms.size());
+        for (FormBoundary form : forms) {
+            TokenEntry entry = extract(document.substring(form.start, form.end), inputId, inputName);
+            if (entry != null) {
+                result.add(entry);
+            }
+        }
+        return result;
+    }
+
+    public static class FormBoundary {
+
+        int start;
+        int end;
+
+        public FormBoundary(int start, int end) {
+            this.start = start;
+            this.end = end;
+        }
+    }
+
+    public static class TokenEntry {
+
+        final String action;
+        final String method;
+        final String name;
+        final String value;
+
+        public TokenEntry(String action, String method, String name, String value) {
+            this.action = action;
+            this.method = method;
+            this.name = name;
+            this.value = value;
+        }
+
+        @Override
+        public String toString() {
+            return "TokenEntry{" + "action=" + action + ", method=" + method + ", name=" + name + ", value=" + value + '}';
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpCsrfFormExtractorTest.java	Tue Aug 08 09:33:22 2017 +0200
@@ -0,0 +1,97 @@
+package com.passus.st.client.http.filter;
+
+import com.passus.commons.utils.ResourceUtils;
+import com.passus.st.client.http.filter.HttpCsrfFormExtractor.TokenEntry;
+import com.passus.st.client.http.filter.HttpCsrfFormExtractor.FormBoundary;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+import static org.testng.AssertJUnit.*;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author mikolaj.podbielski
+ */
+public class HttpCsrfFormExtractorTest {
+
+    String document;
+
+    @BeforeClass
+    public void setup() throws IOException {
+        File file = ResourceUtils.getFile("com/passus/st/client/http/filter/form_csrf1.html");
+        byte[] bytes = Files.readAllBytes(Paths.get(file.toURI()));
+        document = new String(bytes);
+    }
+
+    @Test
+    public void testScan() {
+        List<FormBoundary> bounds = HttpCsrfFormExtractor.scan(document);
+        assertEquals(4, bounds.size());
+        bounds.forEach((fb) -> assertTrue(fb.start < fb.end));
+        assertEquals(bounds.get(1).end, bounds.get(2).start);
+    }
+
+    @Test
+    public void testExtractFromComplexHtml() {
+        FormBoundary fb = HttpCsrfFormExtractor.scan(document).get(0);
+        String form = document.substring(fb.start, fb.end);
+
+        // no action
+        TokenEntry entry = HttpCsrfFormExtractor.extract(form, "form__token", "form[_token]");
+        assertEntryEquals(entry, "", "post", "form1", "token-1qwerty");
+    }
+
+    @Test
+    public void testExtract() {
+        String tag;
+        TokenEntry entry;
+
+        // absolute action, no method
+        tag = "<form name=\"f1\" action=\"/svc/save\"><input type=\"hidden\" id=\"t_id\" name=\"t_n\" value=\"t4val\" /></form>";
+        entry = HttpCsrfFormExtractor.extract(tag, "t_id", "t_n");
+        assertEntryEquals(entry, "/svc/save", "", "f1", "t4val");
+
+        // relative action, no form name
+        tag = "<form action=\"save\" method=\"post\"><input type=\"hidden\" id=\"t_id\" name=\"t_n\" value=\"t4val\" /></form>";
+        entry = HttpCsrfFormExtractor.extract(tag, "t_id", "t_n");
+        assertEntryEquals(entry, "save", "post", "", "t4val");
+    }
+
+    @Test
+    public void testExtractByIdName() {
+        String tag = "<form>"
+                + "<input type=\"hidden\"                          value=\"t4val1\" />"
+                + "<input type=\"hidden\"             name=\"t_n\" value=\"t4val2\" />"
+                + "<input type=\"hidden\" id=\"t_id\"              value=\"t4val3\" />"
+                + "<input type=\"hidden\" id=\"t_id\" name=\"t_n\" value=\"t4val4\" />"
+                + "</form>";
+        TokenEntry entry;
+
+// token by name and id
+        entry = HttpCsrfFormExtractor.extract(tag, "t_id", "t_n");
+        assertEquals("t4val4", entry.value);
+
+// token by id
+        entry = HttpCsrfFormExtractor.extract(tag, "t_id", null);
+        assertEquals("t4val3", entry.value);
+        
+// token by name
+        entry = HttpCsrfFormExtractor.extract(tag, null, "t_n");
+        assertEquals("t4val2", entry.value);
+        
+// token is first hidden input
+        entry = HttpCsrfFormExtractor.extract(tag, null, null);
+        assertEquals("t4val1", entry.value);
+    }
+
+    private static void assertEntryEquals(TokenEntry entry, String action, String method, String name, String value) {
+        assertEquals(action, entry.action);
+        assertEquals(method, entry.method);
+        assertEquals(name, entry.name);
+        assertEquals(value, entry.value);
+    }
+}