//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ namespace System.Net { using System.Collections; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using System.Globalization; using System.Runtime.Serialization; //NOTE: While WebPermissionAttribute resides in System.DLL, // no classes from that DLL are able to make declarative usage of WebPermission. // THE syntax of this attribute is as followed // [WebPermission(SecurityAction.Assert, Connect="http://hostname/path/url", Accept="http://localhost/path/url")] // [WebPermission(SecurityAction.Assert, ConnectPattern="http://hostname/www\.microsoft\.*/url/*", AcceptPattern="http://localhost/*")] // WHERE: //======= // - 'Connect' and 'Accept' keywords allow you to specify the final URI // - 'ConnectPattern' and 'AcceptPattern' keywords allow you to specify a set of URI in escaped Regex form // - They take '.*' as special "everything" indicators, which are fast-pathed. [ AttributeUsage( AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Assembly, AllowMultiple = true, Inherited = false )] [Serializable()] sealed public class WebPermissionAttribute: CodeAccessSecurityAttribute { private object m_accept = null; private object m_connect = null; public WebPermissionAttribute( SecurityAction action ): base( action ) { } public string Connect { get { return m_connect as string;} set { if (m_connect != null) { throw new ArgumentException(SR.GetString(SR.net_perm_attrib_multi, "Connect", value), "value"); } m_connect = value; } } public string Accept { get { return m_accept as string; } set { if (m_accept != null) { throw new ArgumentException(SR.GetString(SR.net_perm_attrib_multi, "Accept", value), "value"); } m_accept = value; } } public string ConnectPattern { get { return m_connect is DelayedRegex ? m_connect.ToString() : m_connect is bool && (bool) m_connect ? WebPermission.MatchAll : null; } set { if (m_connect != null) { throw new ArgumentException(SR.GetString(SR.net_perm_attrib_multi, "ConnectPatern", value), "value"); } if (value == WebPermission.MatchAll) { m_connect = true; } else { m_connect = new DelayedRegex(value); } } } public string AcceptPattern { get { return m_accept is DelayedRegex ? m_accept.ToString() : m_accept is bool && (bool) m_accept ? WebPermission.MatchAll : null; } set { if (m_accept != null) { throw new ArgumentException(SR.GetString(SR.net_perm_attrib_multi, "AcceptPattern", value), "value"); } if (value == WebPermission.MatchAll) { m_accept = true; } else { m_accept = new DelayedRegex(value); } } } /* public bool ConnectAll { get { return m_connect is bool ? (bool) m_connect : false; } set { if (m_connect != null) { throw new ArgumentException(SR.GetString(SR.net_perm_attrib_multi, "ConnectAll", value), "value"); } m_connect = value; } } public bool AcceptAll { get { return m_accept is bool ? (bool) m_accept : false; } set { if (m_accept != null) { throw new ArgumentException(SR.GetString(SR.net_perm_attrib_multi, "AcceptAll", value), "value"); } m_accept = value; } } */ public override IPermission CreatePermission() { WebPermission perm = null; if (Unrestricted) { perm = new WebPermission( PermissionState.Unrestricted); } else { NetworkAccess access = (NetworkAccess) 0; if (m_connect is bool) { if ((bool) m_connect) { access |= NetworkAccess.Connect; } m_connect = null; } if (m_accept is bool) { if ((bool) m_accept) { access |= NetworkAccess.Accept; } m_accept = null; } perm = new WebPermission(access); if (m_accept != null) { if (m_accept is DelayedRegex) { perm.AddAsPattern(NetworkAccess.Accept, (DelayedRegex)m_accept); } else { perm.AddPermission(NetworkAccess.Accept, (string)m_accept); } } if (m_connect != null) { if (m_connect is DelayedRegex) { perm.AddAsPattern(NetworkAccess.Connect, (DelayedRegex)m_connect); } else { perm.AddPermission(NetworkAccess.Connect, (string)m_connect); } } } return perm; } } [Serializable] internal class DelayedRegex { private Regex _AsRegex; private string _AsString; internal DelayedRegex(string regexString) { if (regexString == null) throw new ArgumentNullException("regexString"); _AsString = regexString; } internal DelayedRegex(Regex regex) { if (regex == null) throw new ArgumentNullException("regex"); _AsRegex = regex; } internal Regex AsRegex { get { if (_AsRegex == null) { _AsRegex = new Regex(_AsString + "[/]?", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.CultureInvariant); } return _AsRegex; } } public override string ToString() { return _AsString != null ? _AsString : (_AsString = _AsRegex.ToString()); } } /// /// /// Controls rights to make or accept connections on a Web address. /// /// [Serializable] public sealed class WebPermission : CodeAccessPermission, IUnrestrictedPermission { private bool m_noRestriction; [OptionalField] private bool m_UnrestrictedConnect; [OptionalField] private bool m_UnrestrictedAccept; private ArrayList m_connectList = new ArrayList(); private ArrayList m_acceptList = new ArrayList(); internal const string MatchAll = ".*"; private static volatile Regex s_MatchAllRegex; internal static Regex MatchAllRegex { get { if (s_MatchAllRegex == null) { s_MatchAllRegex = new Regex(".*"); } return s_MatchAllRegex; } } /// /// /// Returns the enumeration of permissions to connect a remote URI. /// /// public IEnumerator ConnectList { get { if (m_UnrestrictedConnect) { return (new Regex[] { MatchAllRegex }).GetEnumerator(); } ArrayList cloned = new ArrayList(m_connectList.Count); for (int i = 0; i < m_connectList.Count; ++i) cloned.Add(m_connectList[i] is DelayedRegex? (object)((DelayedRegex)m_connectList[i]).AsRegex : m_connectList[i] is Uri? (object)((Uri)m_connectList[i]).GetComponents(UriComponents.HttpRequestUrl, UriFormat.UriEscaped) : m_connectList[i]); return cloned.GetEnumerator(); } } /// /// /// Returns the enumeration of permissions to export a local URI. /// /// public IEnumerator AcceptList { get { if (m_UnrestrictedAccept) { return (new Regex[] { MatchAllRegex }).GetEnumerator(); } ArrayList cloned = new ArrayList(m_acceptList.Count); for (int i = 0; i < m_acceptList.Count; ++i) cloned.Add(m_acceptList[i] is DelayedRegex? (object)((DelayedRegex)m_acceptList[i]).AsRegex : m_acceptList[i] is Uri? (object)((Uri)m_acceptList[i]).GetComponents(UriComponents.HttpRequestUrl, UriFormat.UriEscaped) : m_acceptList[i]); return cloned.GetEnumerator(); } } /// /// /// Creates a new instance of the /// class that passes all demands or /// that fails all demands. /// /// public WebPermission(PermissionState state) { m_noRestriction = (state == PermissionState.Unrestricted); } internal WebPermission(bool unrestricted) { m_noRestriction = unrestricted; } /// /// /// Creates a new instance of the class. /// /// public WebPermission() { } internal WebPermission(NetworkAccess access) { m_UnrestrictedConnect = (access & NetworkAccess.Connect) != 0; m_UnrestrictedAccept = (access & NetworkAccess.Accept) != 0; } /// /// /// Creates a new instance of the /// class with the specified access rights for /// the specified URI Pattern. /// Suitable only for WebPermission policy object construction /// /// public WebPermission(NetworkAccess access, Regex uriRegex) { AddPermission(access, uriRegex); } /// /// /// Creates a new instance of the /// class with the specified access rights for /// the specified Uniform Resource Identifier . /// Suitable for requesting particular WebPermission /// /// // < public WebPermission(NetworkAccess access, String uriString) { AddPermission(access, uriString); } // // < internal WebPermission(NetworkAccess access, Uri uri) { AddPermission(access, uri); } // Methods specific to this class /// /// /// Adds a new instance of the WebPermission /// class with the specified access rights for the particular Uniform Resource Identifier. /// /// // < public void AddPermission(NetworkAccess access, String uriString) { if (uriString == null) { throw new ArgumentNullException("uriString"); } if (m_noRestriction) { return; } Uri uri; if (Uri.TryCreate(uriString, UriKind.Absolute, out uri)) AddPermission(access, uri); else { ArrayList lists = new ArrayList(); if ((access & NetworkAccess.Connect) != 0 && !m_UnrestrictedConnect) lists.Add(m_connectList); if ((access & NetworkAccess.Accept) != 0 && !m_UnrestrictedAccept) lists.Add(m_acceptList); foreach (ArrayList list in lists) { // avoid duplicated uris in the list bool found = false; foreach (object obj in list) { string str = obj as string; if (str != null && string.Compare(str, uriString, StringComparison.OrdinalIgnoreCase ) == 0) { found = true; break; } } if (!found) { list.Add(uriString); } } } } // < internal void AddPermission(NetworkAccess access, Uri uri) { if (uri == null) { throw new ArgumentNullException("uri"); } if (m_noRestriction) { return; } ArrayList lists = new ArrayList(); if ((access & NetworkAccess.Connect) != 0 && !m_UnrestrictedConnect) lists.Add(m_connectList); if ((access & NetworkAccess.Accept) != 0 && !m_UnrestrictedAccept) lists.Add(m_acceptList); foreach (ArrayList list in lists) { // avoid duplicated uris in the list bool found = false; foreach (object permObj in list) { if ((permObj is Uri) && uri.Equals(permObj)) { found = true; break; } } if (!found) { list.Add(uri); } } } /// /// Adds a new instance of the /// class with the specified access rights for the specified URI Pattern. /// Should be used during a policy object creation and not for particular URI permission check /// public void AddPermission(NetworkAccess access, Regex uriRegex) { if (uriRegex == null) { throw new ArgumentNullException("uriRegex"); } if (m_noRestriction) { return; } if (uriRegex.ToString() == MatchAll) { if (!m_UnrestrictedConnect && (access & NetworkAccess.Connect) != 0) { m_UnrestrictedConnect = true; m_connectList.Clear(); } if (!m_UnrestrictedAccept && (access & NetworkAccess.Accept) != 0) { m_UnrestrictedAccept = true; m_acceptList.Clear(); } return; } AddAsPattern(access, new DelayedRegex(uriRegex)); } // Overloaded form using string inputs // Enforces case-insensitive matching /// Adds a new instance of the System.Net.WebPermission /// class with the specified access rights for the specified URI Pattern internal void AddAsPattern(NetworkAccess access, DelayedRegex uriRegexPattern) { ArrayList lists = new ArrayList(); if ((access & NetworkAccess.Connect) != 0 && !m_UnrestrictedConnect) lists.Add(m_connectList); if ((access & NetworkAccess.Accept) != 0 && !m_UnrestrictedAccept) lists.Add(m_acceptList); foreach (ArrayList list in lists) { // avoid duplicated regexes in the list bool found = false; foreach (object obj in list) { if ((obj is DelayedRegex) && (string.Compare(uriRegexPattern.ToString(), obj.ToString(), StringComparison.OrdinalIgnoreCase ) == 0)) { found = true; break; } } if (!found) { list.Add(uriRegexPattern); } } } // IUnrestrictedPermission interface methods /// /// /// Checks the overall permisison state of the object. /// /// public bool IsUnrestricted() { return m_noRestriction; } // IPermission interface methods /// /// /// Creates a copy of a instance. /// /// public override IPermission Copy() { if (m_noRestriction) { return new WebPermission(true); } WebPermission wp = new WebPermission((m_UnrestrictedConnect ? NetworkAccess.Connect : (NetworkAccess) 0) | (m_UnrestrictedAccept ? NetworkAccess.Accept : (NetworkAccess)0)); wp.m_acceptList = (ArrayList)m_acceptList.Clone(); wp.m_connectList = (ArrayList)m_connectList.Clone(); return wp; } /// /// Compares two instances. /// public override bool IsSubsetOf(IPermission target) { // Pattern suggested by security engine if (target == null) { return !m_noRestriction && !m_UnrestrictedConnect && !m_UnrestrictedAccept && m_connectList.Count == 0 && m_acceptList.Count == 0; } WebPermission other = target as WebPermission; if (other == null) { throw new ArgumentException(SR.GetString(SR.net_perm_target), "target"); } if (other.m_noRestriction) { return true; } else if (m_noRestriction) { return false; } // // Besides SPECIAL case, this method is restricted to only final URIs (strings) on // the current object. // The restriction comes from the problem of finding a Regex to be a subset of another Regex // DelayedRegex regex = null; if (!other.m_UnrestrictedAccept) { if (m_UnrestrictedAccept) { return false; } else if (m_acceptList.Count != 0) { if (other.m_acceptList.Count == 0) { return false; } foreach(object obj in this.m_acceptList) { regex = obj as DelayedRegex; if(regex != null) { if(isSpecialSubsetCase(obj.ToString(), other.m_acceptList)) continue; throw new NotSupportedException(SR.GetString(SR.net_perm_both_regex)); } if(!isMatchedURI(obj, other.m_acceptList)) return false; } } } if (!other.m_UnrestrictedConnect) { if (m_UnrestrictedConnect) { return false; } else if (m_connectList.Count != 0) { if (other.m_connectList.Count == 0) { return false; } foreach(object obj in this.m_connectList) { regex = obj as DelayedRegex; if(regex != null) { if(isSpecialSubsetCase(obj.ToString(), other.m_connectList)) continue; throw new NotSupportedException(SR.GetString(SR.net_perm_both_regex)); } if(!isMatchedURI(obj, other.m_connectList)) return false; } } } return true; } //Checks special case when testing Regex to be a subset of other Regex //Support only the case when both Regexes are identical as strings. private static bool isSpecialSubsetCase(String regexToCheck, ArrayList permList) { Uri uri; foreach(object uriPattern in permList) { DelayedRegex regex = uriPattern as DelayedRegex; if(regex != null) { //regex parameter against regex permission if (String.Compare(regexToCheck, regex.ToString(), StringComparison.OrdinalIgnoreCase ) == 0) return true; } else if ((uri = uriPattern as Uri) != null) { //regex parameter against Uri permission if (String.Compare(regexToCheck, Regex.Escape(uri.GetComponents(UriComponents.HttpRequestUrl, UriFormat.UriEscaped)), StringComparison.OrdinalIgnoreCase ) == 0) return true; } else if (String.Compare(regexToCheck, Regex.Escape(uriPattern.ToString()), StringComparison.OrdinalIgnoreCase ) == 0) { //regex parameter against string permission return true; } } return false; } // The union of two web permissions is formed by concatenating // the list of allowed regular expressions. There is no check // for duplicates/overlaps /// /// Returns the logical union between two instances. /// public override IPermission Union(IPermission target) { // Pattern suggested by Security engine if (target==null) { return this.Copy(); } WebPermission other = target as WebPermission; if(other == null) { throw new ArgumentException(SR.GetString(SR.net_perm_target), "target"); } if (m_noRestriction || other.m_noRestriction) { return new WebPermission(true); } WebPermission result = new WebPermission(); if (m_UnrestrictedConnect || other.m_UnrestrictedConnect) { result.m_UnrestrictedConnect = true; } else { result.m_connectList = (ArrayList) other.m_connectList.Clone(); for (int i = 0; i < m_connectList.Count; i++) { DelayedRegex uriPattern = m_connectList[i] as DelayedRegex; if(uriPattern == null) if (m_connectList[i] is string) result.AddPermission(NetworkAccess.Connect, (string)m_connectList[i]); else result.AddPermission(NetworkAccess.Connect, (Uri)m_connectList[i]); else result.AddAsPattern(NetworkAccess.Connect, uriPattern); } } if (m_UnrestrictedAccept || other.m_UnrestrictedAccept) { result.m_UnrestrictedAccept = true; } else { result.m_acceptList = (ArrayList) other.m_acceptList.Clone(); for (int i = 0; i < m_acceptList.Count; i++) { DelayedRegex uriPattern = m_acceptList[i] as DelayedRegex; if(uriPattern == null) if (m_acceptList[i] is string) result.AddPermission(NetworkAccess.Accept, (string)m_acceptList[i]); else result.AddPermission(NetworkAccess.Accept, (Uri)m_acceptList[i]); else result.AddAsPattern(NetworkAccess.Accept, uriPattern); } } return result; } /// /// Returns the logical intersection between two instances. /// public override IPermission Intersect(IPermission target) { // Pattern suggested by Security engine if (target == null) { return null; } WebPermission other = target as WebPermission; if(other == null) { throw new ArgumentException(SR.GetString(SR.net_perm_target), "target"); } if (m_noRestriction) { return other.Copy(); } if (other.m_noRestriction) { return Copy(); } WebPermission result = new WebPermission(); if (m_UnrestrictedConnect && other.m_UnrestrictedConnect) { result.m_UnrestrictedConnect = true; } else if (m_UnrestrictedConnect || other.m_UnrestrictedConnect) { result.m_connectList = (ArrayList) (m_UnrestrictedConnect ? other : this).m_connectList.Clone(); } else { intersectList(m_connectList, other.m_connectList, result.m_connectList); } if (m_UnrestrictedAccept && other.m_UnrestrictedAccept) { result.m_UnrestrictedAccept = true; } else if (m_UnrestrictedAccept || other.m_UnrestrictedAccept) { result.m_acceptList = (ArrayList) (m_UnrestrictedAccept ? other : this).m_acceptList.Clone(); } else { intersectList(m_acceptList, other.m_acceptList, result.m_acceptList); } // return null if resulting permission is restricted and empty if (!result.m_UnrestrictedConnect && !result.m_UnrestrictedAccept && result.m_connectList.Count == 0 && result.m_acceptList.Count == 0) { return null; } return result; } /// /// public override void FromXml(SecurityElement securityElement) { if (securityElement == null) { // // null SecurityElement // throw new ArgumentNullException("securityElement"); } if (!securityElement.Tag.Equals("IPermission")) { // // SecurityElement must be a permission element // throw new ArgumentException(SR.GetString(SR.net_not_ipermission), "securityElement"); } string className = securityElement.Attribute("class"); if (className == null) { // // SecurityElement must be a permission element for this type // throw new ArgumentException(SR.GetString(SR.net_no_classname), "securityElement"); } if (className.IndexOf(this.GetType().FullName) < 0) { // // SecurityElement must be a permission element for this type // throw new ArgumentException(SR.GetString(SR.net_no_typename), "securityElement"); } String str = securityElement.Attribute("Unrestricted"); m_connectList = new ArrayList(); m_acceptList = new ArrayList(); m_UnrestrictedAccept = m_UnrestrictedConnect = false; if (str != null && string.Compare(str, "true", StringComparison.OrdinalIgnoreCase ) == 0) { m_noRestriction = true; return; } m_noRestriction = false; SecurityElement et = securityElement.SearchForChildByTag("ConnectAccess"); string uriPattern; if (et != null) { foreach(SecurityElement uriElem in et.Children) { //NOTE: Any stuff coming from XML is treated as URI PATTERN! if (uriElem.Tag.Equals("URI")) { try { uriPattern = uriElem.Attribute("uri"); } catch { uriPattern = null; } if (uriPattern == null) { throw new ArgumentException(SR.GetString(SR.net_perm_invalid_val_in_element), "ConnectAccess"); } if (uriPattern == MatchAll) { m_UnrestrictedConnect = true; m_connectList = new ArrayList(); break; } else { AddAsPattern(NetworkAccess.Connect, new DelayedRegex(uriPattern)); } } else { // improper tag found, just ignore } } } et = securityElement.SearchForChildByTag("AcceptAccess"); if (et != null) { foreach(SecurityElement uriElem in et.Children) { //NOTE: Any stuff coming from XML is treated as URI PATTERN! if (uriElem.Tag.Equals("URI")) { try { uriPattern = uriElem.Attribute("uri"); } catch { uriPattern = null; } if (uriPattern == null) { throw new ArgumentException(SR.GetString(SR.net_perm_invalid_val_in_element), "AcceptAccess"); } if (uriPattern == MatchAll) { m_UnrestrictedAccept = true; m_acceptList = new ArrayList(); break; } else { AddAsPattern(NetworkAccess.Accept, new DelayedRegex(uriPattern)); } } else { // improper tag found, just ignore } } } } /// /// [To be supplied.] /// public override SecurityElement ToXml() { SecurityElement securityElement = new SecurityElement("IPermission"); securityElement.AddAttribute( "class", this.GetType().FullName + ", " + this.GetType().Module.Assembly.FullName.Replace( '\"', '\'' ) ); securityElement.AddAttribute( "version", "1" ); if (!IsUnrestricted()) { String tempStr=null; if (m_UnrestrictedConnect || m_connectList.Count > 0) { SecurityElement connectElement = new SecurityElement( "ConnectAccess" ); if (m_UnrestrictedConnect) { SecurityElement uripattern = new SecurityElement("URI"); uripattern.AddAttribute("uri", SecurityElement.Escape(MatchAll)); connectElement.AddChild(uripattern); } else { //NOTE All strings going to XML will become URI PATTERNS i.e. escaped to Regex foreach(object obj in m_connectList) { Uri uri = obj as Uri; if(uri != null) tempStr=Regex.Escape(uri.GetComponents(UriComponents.HttpRequestUrl, UriFormat.UriEscaped)); else tempStr=obj.ToString(); if (obj is string) tempStr = Regex.Escape(tempStr); SecurityElement uripattern = new SecurityElement("URI"); uripattern.AddAttribute("uri", SecurityElement.Escape(tempStr)); connectElement.AddChild(uripattern); } } securityElement.AddChild( connectElement ); } if (m_UnrestrictedAccept || m_acceptList.Count > 0) { SecurityElement acceptElement = new SecurityElement("AcceptAccess"); if (m_UnrestrictedAccept) { SecurityElement uripattern = new SecurityElement("URI"); uripattern.AddAttribute("uri", SecurityElement.Escape(MatchAll)); acceptElement.AddChild(uripattern); } else { //NOTE All strings going to XML will become URI PATTERNS i.e. escaped to Regex foreach(object obj in m_acceptList) { Uri uri = obj as Uri; if(uri != null) tempStr=Regex.Escape(uri.GetComponents(UriComponents.HttpRequestUrl, UriFormat.UriEscaped)); else tempStr=obj.ToString(); if (obj is string) tempStr = Regex.Escape(tempStr); SecurityElement uripattern = new SecurityElement("URI"); uripattern.AddAttribute("uri", SecurityElement.Escape(tempStr)); acceptElement.AddChild(uripattern); } } securityElement.AddChild( acceptElement ); } } else { securityElement.AddAttribute( "Unrestricted", "true" ); } return securityElement; } // Verifies a single Uri against a set of regular expressions private static bool isMatchedURI(object uriToCheck, ArrayList uriPatternList) { string stringUri = uriToCheck as string; foreach(object uriPattern in uriPatternList) { DelayedRegex R = uriPattern as DelayedRegex; //perform case insensitive comparison of final URIs or strings, a Uri is never equal compares a string (strings are invalid Uris) if(R == null) { if (uriToCheck.GetType() == uriPattern.GetType()) { if (stringUri != null && string.Compare(stringUri, (string)uriPattern, StringComparison.OrdinalIgnoreCase ) == 0) { return true; } else if(stringUri == null && uriToCheck.Equals(uriPattern)) { return true; } } continue; } //Otherwise trying match final URI against given Regex pattern string s = stringUri != null? stringUri: ((Uri)uriToCheck).GetComponents(UriComponents.HttpRequestUrl, UriFormat.UriEscaped); Match M = R.AsRegex.Match(s); if ((M != null) // Found match for the regular expression? && (M.Index == 0) // ... which starts at the begining && (M.Length == s.Length)) { // ... and the whole string matched return true; } if (stringUri != null) continue; // // check if the URI was presented in non-canonical form // s = ((Uri)uriToCheck).GetComponents(UriComponents.HttpRequestUrl, UriFormat.SafeUnescaped); M = R.AsRegex.Match(s); if ((M != null) // Found match for the regular expression? && (M.Index == 0) // ... which starts at the begining && (M.Length == s.Length)) { // ... and the whole string matched return true; } } return false; } // We should keep the result as compact as possible since otherwise even // simple scenarios in Policy Wizard won;t work due to repeated Union/Intersect calls // The issue comes from the "hard" Regex.IsSubsetOf(Regex) problem. private static void intersectList(ArrayList A, ArrayList B, ArrayList result) { bool[] aDone = new bool[A.Count]; bool[] bDone = new bool[B.Count]; int ia=0, ib; // The optimization is done according to the following truth // (A|B|C) intersect (B|C|E|D)) == B|C|(A inter E)|(A inter D) // // We also check on any duplicates in the result // Round 1st // Getting rid of same permissons in the input arrays (assuming X /\ X = X) foreach (object a in A) { ib = 0; foreach (object b in B) { // check to see if b is in the result already if (!bDone[ib]) { //if both are regexes or both are Uris or both are strings if (a.GetType() == b.GetType()) { if (a is Uri) { // both are uris if (a.Equals(b)) { result.Add(a); aDone[ia]=bDone[ib]=true; //since permissions are ORed we can break and go to the next A break; } } else { // regexes and strings uses ToString() output if (string.Compare(a.ToString(), b.ToString(), StringComparison.OrdinalIgnoreCase ) == 0) { result.Add(a); aDone[ia]=bDone[ib]=true; //since permissions are ORed we can break and go to the next A break; } } } } ++ib; } //foreach b in B ++ia; } //foreach a in A ia = 0; // Round second // Grab only intersections of objects not found in both A and B foreach (object a in A) { if (!aDone[ia]) { ib = 0; foreach(object b in B) { if (!bDone[ib]) { bool resultUri; object intesection = intersectPair(a, b, out resultUri); if (intesection != null) { bool found = false; // check to see if we already have the same result foreach (object obj in result) { if (resultUri == (obj is Uri)) { if(resultUri ? intesection.Equals(obj) : string.Compare(obj.ToString(), intesection.ToString(), StringComparison.OrdinalIgnoreCase ) == 0) { found = true; break; } } } if (!found) { result.Add(intesection); } } } ++ib; } } ++ia; } } private static object intersectPair(object L, object R, out bool isUri) { //VERY OLD OPTION: return new Regex("(?=(" + ((Regex)X[i]).ToString()+ "))(" + ((Regex)Y[j]).ToString() + ")","i"); //STILL OLD OPTION: return new Regex("(?=.*?(" + L.ToString() + "))" + "(?=.*?(" + R.ToString() + "))"); // check RegexSpec.doc //CURRENT OPTION: return new Regex("(?=(" + L.ToString() + "))(" + R.ToString() + ")", RegexOptions.IgnoreCase ); isUri = false; DelayedRegex L_Pattern =L as DelayedRegex; DelayedRegex R_Pattern =R as DelayedRegex; if(L_Pattern != null && R_Pattern != null) { //both are Regex return new DelayedRegex("(?=(" + L_Pattern.ToString() + "))(" + R_Pattern.ToString() + ")"); } else if(L_Pattern != null && R_Pattern == null) { //only L is a Regex isUri = R is Uri; string uriString = isUri? ((Uri)R).GetComponents(UriComponents.HttpRequestUrl, UriFormat.UriEscaped): R.ToString(); Match M = L_Pattern.AsRegex.Match(uriString); if ((M != null) // Found match for the regular expression? && (M.Index == 0) // ... which starts at the begining && (M.Length == uriString.Length)) { // ... and the whole string matched return R; } return null; } else if(L_Pattern == null && R_Pattern != null) { //only R is a Regex isUri = L is Uri; string uriString = isUri? ((Uri)L).GetComponents(UriComponents.HttpRequestUrl, UriFormat.UriEscaped): L.ToString(); Match M = R_Pattern.AsRegex.Match(uriString); if ((M != null) // Found match for the regular expression? && (M.Index == 0) // ... which starts at the begining && (M.Length == uriString.Length)) { // ... and the whole string matched return L; } return null; } //both are Uris or strings isUri = L is Uri; if (isUri) return L.Equals(R)? L : null; else return string.Compare(L.ToString(), R.ToString(), StringComparison.OrdinalIgnoreCase ) == 0? L : null; } } // class WebPermission } // namespace System.Net