From 8aa6968431b1e9a1ae1d5a5619c8f2f736d6e625 Mon Sep 17 00:00:00 2001 From: "jkeiser%netscape.com" Date: Wed, 28 Aug 2002 08:19:43 +0000 Subject: [PATCH] Make anonymous content inaccessible to web content (bug 96537), r=sicking@bigfoot.com, sr=jst@netscape.com --- caps/src/Makefile.in | 1 + caps/src/nsScriptSecurityManager.cpp | 71 +++++++++++++++---- content/base/public/nsIContent.h | 14 ++++ content/base/src/nsGenericDOMDataNode.cpp | 13 ++++ content/base/src/nsGenericDOMDataNode.h | 2 + content/base/src/nsGenericElement.cpp | 16 +++++ content/base/src/nsGenericElement.h | 30 +++++--- .../html/content/src/nsAttributeContent.cpp | 2 + content/xul/content/src/nsXULElement.cpp | 12 ++++ content/xul/content/src/nsXULElement.h | 2 + dom/src/base/nsDOMClassInfo.h | 2 + layout/base/nsCSSFrameConstructor.cpp | 1 + .../html/style/src/nsCSSFrameConstructor.cpp | 1 + xpcom/components/nsIClassInfo.idl | 5 ++ 14 files changed, 148 insertions(+), 24 deletions(-) diff --git a/caps/src/Makefile.in b/caps/src/Makefile.in index 6e0fa6dcc198..8b38f5e5bcae 100644 --- a/caps/src/Makefile.in +++ b/caps/src/Makefile.in @@ -45,6 +45,7 @@ REQUIRES = xpcom \ intl \ docshell \ windowwatcher \ + content \ $(NULL) CPPSRCS = \ diff --git a/caps/src/nsScriptSecurityManager.cpp b/caps/src/nsScriptSecurityManager.cpp index c1fa320b26c4..b090cdcd0658 100644 --- a/caps/src/nsScriptSecurityManager.cpp +++ b/caps/src/nsScriptSecurityManager.cpp @@ -79,6 +79,7 @@ #include "nsIPrefBranchInternal.h" #include "nsIJSRuntimeService.h" #include "nsIObserverService.h" +#include "nsIContent.h" static NS_DEFINE_IID(kIStringBundleServiceIID, NS_ISTRINGBUNDLESERVICE_IID); static NS_DEFINE_IID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID); @@ -99,21 +100,48 @@ JSValIDToString(JSContext *cx, const jsval idval) { return NS_REINTERPRET_CAST(PRUnichar*, JS_GetStringChars(str)); } -static inline PRBool -IsDOMClass(nsIClassInfo* aClassInfo) +// Helper class to get stuff from the ClassInfo and not waste extra time with +// virtual method calls for things it has already gotten +class ClassInfoData { - if (!aClassInfo) - return PR_FALSE; +public: + ClassInfoData(nsIClassInfo *aClassInfo) + : mClassInfo(aClassInfo), mDidGetFlags(PR_FALSE) + { + } - PRUint32 classFlags; - nsresult rv = aClassInfo->GetFlags(&classFlags); + PRUint32 GetFlags() + { + if (!mDidGetFlags) { + if (mClassInfo) { + mDidGetFlags = PR_TRUE; + nsresult rv = mClassInfo->GetFlags(&mFlags); + if (NS_FAILED(rv)) { + mFlags = 0; + } + } else { + mFlags = 0; + } + } - return NS_SUCCEEDED(rv) && (classFlags & nsIClassInfo::DOM_OBJECT); -} + return mFlags; + } -// Convenience method to get the current js context stack. -// Uses cached JSContextStack service instead of calling through -// to the service manager. + PRBool IsDOMClass() + { + return GetFlags() & nsIClassInfo::DOM_OBJECT; + } + PRBool IsContentNode() + { + return GetFlags() & nsIClassInfo::CONTENT_NODE; + } + +private: + nsIClassInfo *mClassInfo; // WEAK + PRBool mDidGetFlags; + PRUint32 mFlags; +}; + JSContext * nsScriptSecurityManager::GetCurrentJSContext() { @@ -634,13 +662,17 @@ nsScriptSecurityManager::CheckPropertyAccessImpl(PRUint32 aAction, securityLevel = GetPropertyPolicy(aProperty, cpolicy, aAction); } + // Hold the class info data here so we don't have to go back to virtual + // methods all the time + ClassInfoData classInfoData(aClassInfo); + if (securityLevel.level == SCRIPT_SECURITY_UNDEFINED_ACCESS) { // No policy found for this property so use the default of last resort. // If we were called from somewhere other than XPConnect // (no XPC call context), assume this is a DOM class. Otherwise, // ask the ClassInfo. - if (!aCallContext || IsDOMClass(aClassInfo)) + if (!aCallContext || classInfoData.IsDOMClass()) securityLevel.level = SCRIPT_SECURITY_SAME_ORIGIN_ACCESS; else securityLevel.level = SCRIPT_SECURITY_NO_ACCESS; @@ -715,7 +747,18 @@ nsScriptSecurityManager::CheckPropertyAccessImpl(PRUint32 aAction, rv = NS_OK; } - if NS_SUCCEEDED(rv) + if (NS_SUCCEEDED(rv) && classInfoData.IsContentNode()) + { + // No access to anonymous content from the web! (bug 164086) + nsCOMPtr content(do_QueryInterface(aObj)); + NS_ASSERTION(content, "classinfo had CONTENT_NODE set but node did not" + "implement nsIContent! Fasten your seat belt."); + if (content->IsNativeAnonymous()) { + rv = NS_ERROR_DOM_SECURITY_ERR; + } + } + + if (NS_SUCCEEDED(rv)) { #ifdef DEBUG_mstoltz printf(" GRANTED.\n"); @@ -2264,7 +2307,7 @@ nsScriptSecurityManager::CanCreateWrapper(JSContext *cx, PR_FREEIF(iidStr); #endif // XXX Special case for nsIXPCException ? - if (IsDOMClass(aClassInfo)) + if (ClassInfoData(aClassInfo).IsDOMClass()) { #if 0 printf("DOM class - GRANTED.\n"); diff --git a/content/base/public/nsIContent.h b/content/base/public/nsIContent.h index dd3ab2f7da6d..4f951a1082cf 100644 --- a/content/base/public/nsIContent.h +++ b/content/base/public/nsIContent.h @@ -98,6 +98,20 @@ public: */ NS_IMETHOD SetParent(nsIContent* aParent) = 0; + /** + * Get whether this content is C++-generated anonymous content + * @see nsIAnonymousContentCreator + * @return whether this content is anonymous + */ + NS_IMETHOD_(PRBool) IsNativeAnonymous() const = 0; + + /** + * Set whether this content is anonymous + * @see nsIAnonymousContentCreator + * @param aAnonymous whether this content is anonymous + */ + NS_IMETHOD_(void) SetNativeAnonymous(PRBool aAnonymous) = 0; + /** * Get the namespace that this element's tag is defined in * @param aResult the namespace [OUT] diff --git a/content/base/src/nsGenericDOMDataNode.cpp b/content/base/src/nsGenericDOMDataNode.cpp index cea3d571cee1..4f02e38523a0 100644 --- a/content/base/src/nsGenericDOMDataNode.cpp +++ b/content/base/src/nsGenericDOMDataNode.cpp @@ -678,6 +678,19 @@ nsGenericDOMDataNode::SetParent(nsIContent* aParent) return NS_OK; } +NS_IMETHODIMP_(PRBool) +nsGenericDOMDataNode::IsNativeAnonymous() const +{ + nsIContent* parent = GetParentWeak(); + return parent && parent->IsNativeAnonymous(); +} + +NS_IMETHODIMP_(void) +nsGenericDOMDataNode::SetNativeAnonymous(PRBool aAnonymous) +{ + // XXX Need to fix this to do something - bug 165110 +} + NS_IMETHODIMP nsGenericDOMDataNode::GetNameSpaceID(PRInt32& aID) const { diff --git a/content/base/src/nsGenericDOMDataNode.h b/content/base/src/nsGenericDOMDataNode.h index ed764cc1a4fb..dd82b7596e54 100644 --- a/content/base/src/nsGenericDOMDataNode.h +++ b/content/base/src/nsGenericDOMDataNode.h @@ -173,6 +173,8 @@ public: PRBool aCompileEventHandlers); NS_IMETHOD GetParent(nsIContent*& aResult) const; NS_IMETHOD SetParent(nsIContent* aParent); + NS_IMETHOD_(PRBool) IsNativeAnonymous() const; + NS_IMETHOD_(void) SetNativeAnonymous(PRBool aAnonymous); NS_IMETHOD GetNameSpaceID(PRInt32& aID) const; NS_IMETHOD NormalizeAttrString(const nsAString& aStr, nsINodeInfo*& aNodeInfo); diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index d25d1c6970d0..19f82d0397a8 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -1856,6 +1856,22 @@ nsGenericElement::SetParent(nsIContent* aParent) return NS_OK; } +NS_IMETHODIMP_(PRBool) +nsGenericElement::IsNativeAnonymous() const +{ + return !!(GetFlags() & GENERIC_ELEMENT_IS_ANONYMOUS); +} + +NS_IMETHODIMP_(void) +nsGenericElement::SetNativeAnonymous(PRBool aAnonymous) +{ + if (aAnonymous) { + SetFlags(GENERIC_ELEMENT_IS_ANONYMOUS); + } else { + UnsetFlags(GENERIC_ELEMENT_IS_ANONYMOUS); + } +} + nsresult nsGenericElement::GetNameSpaceID(PRInt32& aNameSpaceID) const { diff --git a/content/base/src/nsGenericElement.h b/content/base/src/nsGenericElement.h index 410712dfd773..c8f1f347f91a 100644 --- a/content/base/src/nsGenericElement.h +++ b/content/base/src/nsGenericElement.h @@ -71,26 +71,34 @@ class nsINodeInfo; typedef unsigned long PtrBits; -// This bit will be set if the nsGenericElement has nsDOMSlots +/** This bit will be set if the nsGenericElement has nsDOMSlots */ #define GENERIC_ELEMENT_DOESNT_HAVE_DOMSLOTS 0x00000001U -// This bit will be set if the element has a range list in the range -// list hash +/** + * This bit will be set if the element has a range list in the range list hash + */ #define GENERIC_ELEMENT_HAS_RANGELIST 0x00000002U -// This bit will be set if the element has a listener manager in the -// listener manager hash +/** + * This bit will be set if the element has a listener manager in the listener + * manager hash + */ #define GENERIC_ELEMENT_HAS_LISTENERMANAGER 0x00000004U -// The number of bits to shift the bit field to get at the content ID -#define GENERIC_ELEMENT_CONTENT_ID_BITS_OFFSET 3 +/** Whether this content is anonymous */ +#define GENERIC_ELEMENT_IS_ANONYMOUS 0x00000008U -// This mask masks out the bits that are used for the content ID +/** The number of bits to shift the bit field to get at the content ID */ +#define GENERIC_ELEMENT_CONTENT_ID_BITS_OFFSET 4 + +/** This mask masks out the bits that are used for the content ID */ #define GENERIC_ELEMENT_CONTENT_ID_MASK \ ((~PtrBits(0)) << GENERIC_ELEMENT_CONTENT_ID_BITS_OFFSET) -// The largest value for content ID that fits in -// GENERIC_ELEMENT_CONTENT_ID_MASK +/** + * The largest value for content ID that fits in + * GENERIC_ELEMENT_CONTENT_ID_MASK + */ #define GENERIC_ELEMENT_CONTENT_ID_MAX_VALUE \ ((~PtrBits(0)) >> GENERIC_ELEMENT_CONTENT_ID_BITS_OFFSET) @@ -339,6 +347,8 @@ public: PRBool aCompileEventHandlers); NS_IMETHOD GetParent(nsIContent*& aResult) const; NS_IMETHOD SetParent(nsIContent* aParent); + NS_IMETHOD_(PRBool) IsNativeAnonymous() const; + NS_IMETHOD_(void) SetNativeAnonymous(PRBool aAnonymous); NS_IMETHOD GetNameSpaceID(PRInt32& aNameSpaceID) const; NS_IMETHOD GetTag(nsIAtom*& aResult) const; NS_IMETHOD GetNodeInfo(nsINodeInfo*& aResult) const; diff --git a/content/html/content/src/nsAttributeContent.cpp b/content/html/content/src/nsAttributeContent.cpp index 109c0ee8cf87..e7ae1c07b460 100644 --- a/content/html/content/src/nsAttributeContent.cpp +++ b/content/html/content/src/nsAttributeContent.cpp @@ -97,6 +97,8 @@ public: NS_IMETHOD SetDocument(nsIDocument* aDocument, PRBool aDeep, PRBool aCompileEventHandlers); NS_IMETHOD GetParent(nsIContent*& aResult) const; NS_IMETHOD SetParent(nsIContent* aParent); + NS_IMETHOD_(PRBool) IsNativeAnonymous() const { return PR_TRUE; } + NS_IMETHOD_(void) SetNativeAnonymous(PRBool aAnonymous) { } NS_IMETHOD GetNameSpaceID(PRInt32& aID) const { aID = kNameSpaceID_None; diff --git a/content/xul/content/src/nsXULElement.cpp b/content/xul/content/src/nsXULElement.cpp index 946f12bff453..e56b0da7650e 100644 --- a/content/xul/content/src/nsXULElement.cpp +++ b/content/xul/content/src/nsXULElement.cpp @@ -2170,6 +2170,18 @@ nsXULElement::SetParent(nsIContent* aParent) return NS_OK; } +NS_IMETHODIMP_(PRBool) +nsXULElement::IsNativeAnonymous() const +{ + return PR_FALSE; +} + +NS_IMETHODIMP_(void) +nsXULElement::SetNativeAnonymous(PRBool aAnonymous) +{ + // XXX Need to make this actually do something - bug 165110 +} + NS_IMETHODIMP nsXULElement::CanContainChildren(PRBool& aResult) const { diff --git a/content/xul/content/src/nsXULElement.h b/content/xul/content/src/nsXULElement.h index 8a6b458b9c9f..8fc13281f602 100644 --- a/content/xul/content/src/nsXULElement.h +++ b/content/xul/content/src/nsXULElement.h @@ -411,6 +411,8 @@ public: NS_IMETHOD SetDocument(nsIDocument* aDocument, PRBool aDeep, PRBool aCompileEventHandlers); NS_IMETHOD GetParent(nsIContent*& aResult) const; NS_IMETHOD SetParent(nsIContent* aParent); + NS_IMETHOD_(PRBool) IsNativeAnonymous() const; + NS_IMETHOD_(void) SetNativeAnonymous(PRBool aAnonymous); NS_IMETHOD CanContainChildren(PRBool& aResult) const; NS_IMETHOD ChildCount(PRInt32& aResult) const; NS_IMETHOD ChildAt(PRInt32 aIndex, nsIContent*& aResult) const; diff --git a/dom/src/base/nsDOMClassInfo.h b/dom/src/base/nsDOMClassInfo.h index 70c8aca5f5df..441edb5bc5da 100644 --- a/dom/src/base/nsDOMClassInfo.h +++ b/dom/src/base/nsDOMClassInfo.h @@ -383,6 +383,7 @@ public: JSObject *globalObj, JSObject **parentObj); NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsval id, jsval *vp, PRBool *_retval); + NS_IMETHOD GetFlags(PRUint32 *aFlags); static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) { @@ -600,6 +601,7 @@ public: JSObject *obj, jsval id, jsval *vp, PRBool *_retval); NS_IMETHOD SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsval id, jsval *vp, PRBool *_retval); + NS_IMETHOD GetFlags(PRUint32* aFlags); static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) { diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 9fbc16e20060..925e7f1ba12e 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -5211,6 +5211,7 @@ nsCSSFrameConstructor::CreateAnonymousFrames(nsIPresShell* aPresShell if (NS_FAILED(anonymousItems->QueryElementAt(i, NS_GET_IID(nsIContent), getter_AddRefs(content)))) continue; + content->SetNativeAnonymous(PR_TRUE); content->SetParent(aParent); content->SetDocument(aDocument, PR_TRUE, PR_TRUE); diff --git a/layout/html/style/src/nsCSSFrameConstructor.cpp b/layout/html/style/src/nsCSSFrameConstructor.cpp index 9fbc16e20060..925e7f1ba12e 100644 --- a/layout/html/style/src/nsCSSFrameConstructor.cpp +++ b/layout/html/style/src/nsCSSFrameConstructor.cpp @@ -5211,6 +5211,7 @@ nsCSSFrameConstructor::CreateAnonymousFrames(nsIPresShell* aPresShell if (NS_FAILED(anonymousItems->QueryElementAt(i, NS_GET_IID(nsIContent), getter_AddRefs(content)))) continue; + content->SetNativeAnonymous(PR_TRUE); content->SetParent(aParent); content->SetDocument(aDocument, PR_TRUE, PR_TRUE); diff --git a/xpcom/components/nsIClassInfo.idl b/xpcom/components/nsIClassInfo.idl index 1a9ecfef479f..3f6ee755e675 100644 --- a/xpcom/components/nsIClassInfo.idl +++ b/xpcom/components/nsIClassInfo.idl @@ -101,6 +101,11 @@ interface nsIClassInfo : nsISupports const PRUint32 DOM_OBJECT = 1 << 3; const PRUint32 PLUGIN_OBJECT = 1 << 4; const PRUint32 EAGER_CLASSINFO = 1 << 5; + /** + * 'flags' attribute bitflag: whether objects of this type implement + * nsIContent. + */ + const PRUint32 CONTENT_NODE = 1 << 6; // The high order bit is RESERVED for consumers of these flags. // No implementor of this interface should ever return flags