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