Sniffing Code in Form Templates

With the introduction of InfoPath Forms Services for MOSS 2007, clever management of form template deployment will probably become a must for most IT departments.  You'll want to be sure that form templates are not draining server resources.  You'll especially want to keep an eye on administrator-deployed form templates, as those can achieve fully trusted status and execute arbitrary code on the server.

With that in mind, it's probably a good idea to set up a code review process for InfoPath form templates that will be deployed by the administrator.  To facilitate the process, it might be nice to have a tool around that will tell you whether or not a form template uses custom code.  This is pretty easy to determine, and a tool can be coded up rather quickly, but we figured we'd facilitate the process and provide a sample to get you going.  And here it is…

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.IO;
using System.Diagnostics;

namespace CheckForCode

{

class Program

{

/// <summary>

/// The Main method entry point for the code-checking algorithm.

/// </summary>

/// <param name="args">The command-line arguments for the code-checking algorithm.</param>

public static void Main(string[] args)

{

if (args == null || args.Length == 0)

{

Console.WriteLine("No arguments specified.");

}

else

{

string filePath = args[0];

try

{

Uri fileUri = new Uri(filePath, UriKind.Absolute);

filePath = fileUri.AbsoluteUri;

// If the file is an http URL, download it to the local machine.

if (fileUri.Scheme.Equals(Uri.UriSchemeHttp))

{

filePath = CopyFormTemplateLocally(filePath);

}

if (File.Exists(filePath))

{

string extension = Path.GetExtension(filePath);

if (extension.Equals(".xsn", StringComparison.CurrentCultureIgnoreCase))

{

// Extract the xsf and check for the root assembly.

string pathToXSF = ExpandManifest(filePath);

CheckXSFForCode(pathToXSF);

}

else if (extension.Equals(".xsf", StringComparison.CurrentCultureIgnoreCase))

{

// Check for the root assembly.

CheckXSFForCode(filePath);

}

}

}

catch(ArgumentException exn)

{

Console.WriteLine("The file path argument is invalid.");

Console.WriteLine("Exception message: " + exn.Message);

}

catch(UriFormatException exn)

{

Console.WriteLine("The file path is not a valid Uri: '" + filePath + "'");

Console.WriteLine("Exception message: " + exn.Message);

}

}

Console.WriteLine("Hit any key to exit.");

Console.ReadKey();

}

/// <summary>

/// Get the path to the temp folder for the current user.

/// </summary>

private static string TempFolder

{

get

{

string tempFolder = Environment.ExpandEnvironmentVariables("%temp%");

return tempFolder;

}

}

/// <summary>

/// Copy a form template at an http URL to a local path.

/// </summary>

/// <param name="absoluteFileUri">The absolute Uri of the form template.</param>

/// <returns>The local path where the form template was downloaded.</returns>

private static string CopyFormTemplateLocally(string absoluteFileUri)

{

string fileName = Path.GetFileName(absoluteFileUri);

string tempFilePath = Path.Combine(TempFolder, fileName);

// Download the form template source from the server.

System.Net.WebClient client = new System.Net.WebClient();

client.UseDefaultCredentials = true;

client.Headers.Add("Translate:f");

client.DownloadFile(absoluteFileUri, tempFilePath);

return tempFilePath;

}

/// <summary>

/// Expand the manifest.xsf file from the specified xsn.

/// </summary>

/// <param name="pathToXSN">The absolute path to the xsn.</param>

/// <returns>The path to the expanded xsf.</returns>

private static string ExpandManifest(string pathToXSN)

{

string tempFolder = TempFolder;

Process expand = new Process();

expand.StartInfo.ErrorDialog = false;

expand.StartInfo.UseShellExecute = false;

expand.StartInfo.RedirectStandardOutput = true;

expand.StartInfo.CreateNoWindow = true;

expand.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

expand.StartInfo.FileName = "expand.exe";

expand.StartInfo.Arguments = pathToXSN + " -F:manifest.xsf " + tempFolder;

expand.Start();

// Note that the xsf may not be named "manifest" so this could fail if the user

// has extracted the form template files and changed the name of the file.

return Path.Combine(tempFolder, "manifest.xsf");

}

/// <summary>

/// Check the xsf at the specified path for a root assembly dll.

/// </summary>

/// <param name="pathToXSF">The absolute path to the xsf document.</param>

private static void CheckXSFForCode(string pathToXSF)

{

string rootAssemblyNameXPath = "/xsf:xDocumentClass/xsf:package/xsf:files/xsf:file[xsf:fileProperties/xsf:property/@value='rootAssembly']/@name";

// Load the xsf document.

XmlDocument xsfDocument = new XmlDocument();

xsfDocument.Load(pathToXSF);

// Load the xsf namespace.

XmlNamespaceManager nameSpaceManager = new XmlNamespaceManager(xsfDocument.NameTable);

nameSpaceManager.AddNamespace("xsf", "http://schemas.microsoft.com/office/infopath/2003/solutionDefinition");

// Navigate to the root assembly, if it exists.

XPathNavigator xsfRootNavigator = xsfDocument.CreateNavigator();

XPathNavigator assemblyFile = xsfRootNavigator.SelectSingleNode(rootAssemblyNameXPath, nameSpaceManager);

// Alert for code review.

if (null == assemblyFile)

{

Console.WriteLine("No custom code in form template.");

}

else

{

Console.WriteLine("Code review required! Root assembly name: " + assemblyFile.Value);

}

}

}

}

Just slap this into a console application in Visual Studio 2005, and you'll have a simple application that will accept the Uri of the form template or xsf as input and output "No code" or "Code found!"  With a little customization, something like this could be worked into a workflow that would govern the deployment of administrator-deployed form templates.

And taking it a bit further, you could automatically seek out the source code folder in the "manifest.xsf" file, in the "projectPath" attribute of the "xsf2:managedCode" element.  Note here, though, that it will only be accessible if the form designer stored the VSTA project in a shared location, so you may have to implement some administrator policies to guarantee that this location is accessible.

Forrest Dillaway
Software Design Engineer in Test