Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(209)

Side by Side Diff: chrome/browser/extensions/active_script_controller_unittest.cc

Issue 348313003: Create withheld permissions (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Test fix Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <map> 5 #include <map>
6 6
7 #include "base/values.h" 7 #include "base/values.h"
8 #include "chrome/browser/extensions/active_script_controller.h" 8 #include "chrome/browser/extensions/active_script_controller.h"
9 #include "chrome/browser/extensions/active_tab_permission_granter.h" 9 #include "chrome/browser/extensions/active_tab_permission_granter.h"
10 #include "chrome/browser/extensions/extension_util.h" 10 #include "chrome/browser/extensions/extension_util.h"
11 #include "chrome/browser/extensions/tab_helper.h" 11 #include "chrome/browser/extensions/tab_helper.h"
12 #include "chrome/test/base/chrome_render_view_host_test_harness.h" 12 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
13 #include "chrome/test/base/testing_profile.h" 13 #include "chrome/test/base/testing_profile.h"
14 #include "content/public/browser/navigation_controller.h" 14 #include "content/public/browser/navigation_controller.h"
15 #include "content/public/browser/navigation_entry.h" 15 #include "content/public/browser/navigation_entry.h"
16 #include "content/public/browser/web_contents.h" 16 #include "content/public/browser/web_contents.h"
17 #include "extensions/browser/extension_registry.h" 17 #include "extensions/browser/extension_registry.h"
18 #include "extensions/common/constants.h"
18 #include "extensions/common/extension.h" 19 #include "extensions/common/extension.h"
19 #include "extensions/common/extension_builder.h" 20 #include "extensions/common/extension_builder.h"
20 #include "extensions/common/feature_switch.h" 21 #include "extensions/common/feature_switch.h"
21 #include "extensions/common/id_util.h" 22 #include "extensions/common/id_util.h"
22 #include "extensions/common/manifest.h" 23 #include "extensions/common/manifest.h"
23 #include "extensions/common/value_builder.h" 24 #include "extensions/common/value_builder.h"
24 25
25 namespace extensions { 26 namespace extensions {
26 27
27 namespace { 28 namespace {
28 29
29 const char kAllHostsPermission[] = "*://*/*"; 30 const char kAllHostsPermission[] = "*://*/*";
30 31
31 } // namespace 32 } // namespace
32 33
33 // Unittests for the ActiveScriptController mostly test the internal logic 34 // Unittests for the ActiveScriptController mostly test the internal logic
34 // of the controller itself (when to allow/deny extension script injection). 35 // of the controller itself (when to allow/deny extension script injection).
35 // Testing real injection is allowed/denied as expected (i.e., that the 36 // Testing real injection is allowed/denied as expected (i.e., that the
36 // ActiveScriptController correctly interfaces in the system) is done in the 37 // ActiveScriptController correctly interfaces in the system) is done in the
37 // ActiveScriptControllerBrowserTests. 38 // ActiveScriptControllerBrowserTests.
38 class ActiveScriptControllerUnitTest : public ChromeRenderViewHostTestHarness { 39 class ActiveScriptControllerUnitTest : public ChromeRenderViewHostTestHarness {
39 protected: 40 protected:
40 ActiveScriptControllerUnitTest(); 41 ActiveScriptControllerUnitTest();
41 virtual ~ActiveScriptControllerUnitTest(); 42 virtual ~ActiveScriptControllerUnitTest();
42 43
43 // Creates an extension with all hosts permission and adds it to the registry. 44 // Creates an extension with all hosts permission and adds it to the registry.
44 const Extension* AddExtension(); 45 const Extension* AddExtension();
45 46
47 // Returns true if the |extension| requires user consent before injecting
48 // a script.
49 bool RequiresUserConsent(const Extension* extension) const;
50
51 // Request an injection for the given |extension|.
52 void RequestInjection(const Extension* extension);
53
54 // Returns the number of times a given extension has had a script execute.
55 size_t GetExecutionCountForExtension(const std::string& extension_id) const;
56
57 ActiveScriptController* controller() const {
58 return active_script_controller_;
59 }
60
61 private:
46 // Returns the current page id. 62 // Returns the current page id.
47 int GetPageId(); 63 int GetPageId();
48 64
49 // Returns a closure to use as a script execution for a given extension. 65 // Returns a closure to use as a script execution for a given extension.
50 base::Closure GetExecutionCallbackForExtension( 66 base::Closure GetExecutionCallbackForExtension(
51 const std::string& extension_id); 67 const std::string& extension_id);
52 68
53 // Returns the number of times a given extension has had a script execute.
54 size_t GetExecutionCountForExtension(const std::string& extension_id) const;
55
56 ActiveScriptController* controller() { return active_script_controller_; }
57
58 private:
59 // Increment the number of executions for the given |extension_id|. 69 // Increment the number of executions for the given |extension_id|.
60 void IncrementExecutionCount(const std::string& extension_id); 70 void IncrementExecutionCount(const std::string& extension_id);
61 71
62 virtual void SetUp() OVERRIDE; 72 virtual void SetUp() OVERRIDE;
63 73
64 // Since ActiveScriptController's behavior is behind a flag, override the 74 // Since ActiveScriptController's behavior is behind a flag, override the
65 // feature switch. 75 // feature switch.
66 FeatureSwitch::ScopedOverride feature_override_; 76 FeatureSwitch::ScopedOverride feature_override_;
67 77
68 // The associated ActiveScriptController. 78 // The associated ActiveScriptController.
(...skipping 25 matching lines...) Expand all
94 .Set("permissions", 104 .Set("permissions",
95 ListBuilder().Append(kAllHostsPermission))) 105 ListBuilder().Append(kAllHostsPermission)))
96 .SetLocation(Manifest::INTERNAL) 106 .SetLocation(Manifest::INTERNAL)
97 .SetID(kId) 107 .SetID(kId)
98 .Build(); 108 .Build();
99 109
100 ExtensionRegistry::Get(profile())->AddEnabled(extension); 110 ExtensionRegistry::Get(profile())->AddEnabled(extension);
101 return extension; 111 return extension;
102 } 112 }
103 113
114 bool ActiveScriptControllerUnitTest::RequiresUserConsent(
115 const Extension* extension) const {
116 PermissionsData::AccessType access_type =
117 controller()->RequiresUserConsentForScriptInjectionForTesting(
118 extension, extension_misc::PROGRAMMATIC_SCRIPT);
119 // We should never downright refuse access in these tests.
120 DCHECK_NE(PermissionsData::DENY_ACCESS, access_type);
121 return access_type == PermissionsData::REQUEST_ACCESS;
122 }
123
124 void ActiveScriptControllerUnitTest::RequestInjection(
125 const Extension* extension) {
126 controller()->RequestScriptInjectionForTesting(
127 extension,
128 GetPageId(),
129 GetExecutionCallbackForExtension(extension->id()));
130 }
131
132 size_t ActiveScriptControllerUnitTest::GetExecutionCountForExtension(
133 const std::string& extension_id) const {
134 std::map<std::string, int>::const_iterator iter =
135 extension_executions_.find(extension_id);
136 if (iter != extension_executions_.end())
137 return iter->second;
138 return 0u;
139 }
140
104 int ActiveScriptControllerUnitTest::GetPageId() { 141 int ActiveScriptControllerUnitTest::GetPageId() {
105 content::NavigationEntry* navigation_entry = 142 content::NavigationEntry* navigation_entry =
106 web_contents()->GetController().GetVisibleEntry(); 143 web_contents()->GetController().GetVisibleEntry();
107 DCHECK(navigation_entry); // This should never be NULL. 144 DCHECK(navigation_entry); // This should never be NULL.
108 return navigation_entry->GetPageID(); 145 return navigation_entry->GetPageID();
109 } 146 }
110 147
111 base::Closure ActiveScriptControllerUnitTest::GetExecutionCallbackForExtension( 148 base::Closure ActiveScriptControllerUnitTest::GetExecutionCallbackForExtension(
112 const std::string& extension_id) { 149 const std::string& extension_id) {
113 // We use base unretained here, but if this ever gets executed outside of 150 // We use base unretained here, but if this ever gets executed outside of
114 // this test's lifetime, we have a major problem anyway. 151 // this test's lifetime, we have a major problem anyway.
115 return base::Bind(&ActiveScriptControllerUnitTest::IncrementExecutionCount, 152 return base::Bind(&ActiveScriptControllerUnitTest::IncrementExecutionCount,
116 base::Unretained(this), 153 base::Unretained(this),
117 extension_id); 154 extension_id);
118 } 155 }
119 156
120 size_t ActiveScriptControllerUnitTest::GetExecutionCountForExtension(
121 const std::string& extension_id) const {
122 std::map<std::string, int>::const_iterator iter =
123 extension_executions_.find(extension_id);
124 if (iter != extension_executions_.end())
125 return iter->second;
126 return 0u;
127 }
128
129 void ActiveScriptControllerUnitTest::IncrementExecutionCount( 157 void ActiveScriptControllerUnitTest::IncrementExecutionCount(
130 const std::string& extension_id) { 158 const std::string& extension_id) {
131 ++extension_executions_[extension_id]; 159 ++extension_executions_[extension_id];
132 } 160 }
133 161
134 void ActiveScriptControllerUnitTest::SetUp() { 162 void ActiveScriptControllerUnitTest::SetUp() {
135 ChromeRenderViewHostTestHarness::SetUp(); 163 ChromeRenderViewHostTestHarness::SetUp();
136 164
137 TabHelper::CreateForWebContents(web_contents()); 165 TabHelper::CreateForWebContents(web_contents());
138 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents()); 166 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents());
(...skipping 11 matching lines...) Expand all
150 const Extension* extension = AddExtension(); 178 const Extension* extension = AddExtension();
151 ASSERT_TRUE(extension); 179 ASSERT_TRUE(extension);
152 180
153 NavigateAndCommit(GURL("https://www.google.com")); 181 NavigateAndCommit(GURL("https://www.google.com"));
154 182
155 // Ensure that there aren't any executions pending. 183 // Ensure that there aren't any executions pending.
156 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id())); 184 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
157 ASSERT_FALSE(controller()->GetActionForExtension(extension)); 185 ASSERT_FALSE(controller()->GetActionForExtension(extension));
158 186
159 // Since the extension requests all_hosts, we should require user consent. 187 // Since the extension requests all_hosts, we should require user consent.
160 EXPECT_TRUE( 188 EXPECT_TRUE(RequiresUserConsent(extension));
161 controller()->RequiresUserConsentForScriptInjection(extension));
162 189
163 // Request an injection. There should be an action visible, but no executions. 190 // Request an injection. There should be an action visible, but no executions.
164 controller()->RequestScriptInjection( 191 RequestInjection(extension);
165 extension,
166 GetPageId(),
167 GetExecutionCallbackForExtension(extension->id()));
168 EXPECT_TRUE(controller()->GetActionForExtension(extension)); 192 EXPECT_TRUE(controller()->GetActionForExtension(extension));
169 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id())); 193 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
170 194
171 // Click to accept the extension executing. 195 // Click to accept the extension executing.
172 controller()->OnClicked(extension); 196 controller()->OnClicked(extension);
173 197
174 // The extension should execute, and the action should go away. 198 // The extension should execute, and the action should go away.
175 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id())); 199 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
176 EXPECT_FALSE(controller()->GetActionForExtension(extension)); 200 EXPECT_FALSE(controller()->GetActionForExtension(extension));
177 201
178 // Since we already executed on the given page, we shouldn't need permission 202 // Since we already executed on the given page, we shouldn't need permission
179 // for a second time. 203 // for a second time.
180 EXPECT_FALSE( 204 EXPECT_FALSE(RequiresUserConsent(extension));
181 controller()->RequiresUserConsentForScriptInjection(extension));
182 205
183 // Reloading should clear those permissions, and we should again require user 206 // Reloading should clear those permissions, and we should again require user
184 // consent. 207 // consent.
185 Reload(); 208 Reload();
186 EXPECT_TRUE( 209 EXPECT_TRUE(RequiresUserConsent(extension));
187 controller()->RequiresUserConsentForScriptInjection(extension));
188 210
189 // Grant access. 211 // Grant access.
190 controller()->RequestScriptInjection( 212 RequestInjection(extension);
191 extension,
192 GetPageId(),
193 GetExecutionCallbackForExtension(extension->id()));
194 controller()->OnClicked(extension); 213 controller()->OnClicked(extension);
195 EXPECT_EQ(2u, GetExecutionCountForExtension(extension->id())); 214 EXPECT_EQ(2u, GetExecutionCountForExtension(extension->id()));
196 EXPECT_FALSE(controller()->GetActionForExtension(extension)); 215 EXPECT_FALSE(controller()->GetActionForExtension(extension));
197 216
198 // Navigating to another site should also clear the permissions. 217 // Navigating to another site should also clear the permissions.
199 NavigateAndCommit(GURL("https://www.foo.com")); 218 NavigateAndCommit(GURL("https://www.foo.com"));
200 EXPECT_TRUE( 219 EXPECT_TRUE(RequiresUserConsent(extension));
201 controller()->RequiresUserConsentForScriptInjection(extension));
202 } 220 }
203 221
204 // Test that injections that are not executed by the time the user navigates are 222 // Test that injections that are not executed by the time the user navigates are
205 // ignored and never execute. 223 // ignored and never execute.
206 TEST_F(ActiveScriptControllerUnitTest, PendingInjectionsRemovedAtNavigation) { 224 TEST_F(ActiveScriptControllerUnitTest, PendingInjectionsRemovedAtNavigation) {
207 const Extension* extension = AddExtension(); 225 const Extension* extension = AddExtension();
208 ASSERT_TRUE(extension); 226 ASSERT_TRUE(extension);
209 227
210 NavigateAndCommit(GURL("https://www.google.com")); 228 NavigateAndCommit(GURL("https://www.google.com"));
211 229
212 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id())); 230 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
213 231
214 // Request an injection. There should be an action visible, but no executions. 232 // Request an injection. There should be an action visible, but no executions.
215 controller()->RequestScriptInjection( 233 RequestInjection(extension);
216 extension,
217 GetPageId(),
218 GetExecutionCallbackForExtension(extension->id()));
219 EXPECT_TRUE(controller()->GetActionForExtension(extension)); 234 EXPECT_TRUE(controller()->GetActionForExtension(extension));
220 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id())); 235 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
221 236
222 // Reload. This should remove the pending injection, and we should not 237 // Reload. This should remove the pending injection, and we should not
223 // execute anything. 238 // execute anything.
224 Reload(); 239 Reload();
225 EXPECT_FALSE(controller()->GetActionForExtension(extension)); 240 EXPECT_FALSE(controller()->GetActionForExtension(extension));
226 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id())); 241 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
227 242
228 // Request and accept a new injection. 243 // Request and accept a new injection.
229 controller()->RequestScriptInjection( 244 RequestInjection(extension);
230 extension,
231 GetPageId(),
232 GetExecutionCallbackForExtension(extension->id()));
233 controller()->OnClicked(extension); 245 controller()->OnClicked(extension);
234 246
235 // The extension should only have executed once, even though a grand total 247 // The extension should only have executed once, even though a grand total
236 // of two executions were requested. 248 // of two executions were requested.
237 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id())); 249 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
238 EXPECT_FALSE(controller()->GetActionForExtension(extension)); 250 EXPECT_FALSE(controller()->GetActionForExtension(extension));
239 } 251 }
240 252
241 // Test that queueing multiple pending injections, and then accepting, triggers 253 // Test that queueing multiple pending injections, and then accepting, triggers
242 // them all. 254 // them all.
243 TEST_F(ActiveScriptControllerUnitTest, MultiplePendingInjection) { 255 TEST_F(ActiveScriptControllerUnitTest, MultiplePendingInjection) {
244 const Extension* extension = AddExtension(); 256 const Extension* extension = AddExtension();
245 ASSERT_TRUE(extension); 257 ASSERT_TRUE(extension);
246 NavigateAndCommit(GURL("https://www.google.com")); 258 NavigateAndCommit(GURL("https://www.google.com"));
247 259
248 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id())); 260 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
249 261
250 const size_t kNumInjections = 3u; 262 const size_t kNumInjections = 3u;
251 // Queue multiple pending injections. 263 // Queue multiple pending injections.
252 for (size_t i = 0u; i < kNumInjections; ++i) { 264 for (size_t i = 0u; i < kNumInjections; ++i)
253 controller()->RequestScriptInjection( 265 RequestInjection(extension);
254 extension, 266
255 GetPageId(),
256 GetExecutionCallbackForExtension(extension->id()));
257 }
258 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id())); 267 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
259 268
260 controller()->OnClicked(extension); 269 controller()->OnClicked(extension);
261 270
262 // All pending injections should have executed. 271 // All pending injections should have executed.
263 EXPECT_EQ(kNumInjections, GetExecutionCountForExtension(extension->id())); 272 EXPECT_EQ(kNumInjections, GetExecutionCountForExtension(extension->id()));
264 EXPECT_FALSE(controller()->GetActionForExtension(extension)); 273 EXPECT_FALSE(controller()->GetActionForExtension(extension));
265 } 274 }
266 275
267 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsUseActiveTabPermissions) { 276 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsUseActiveTabPermissions) {
268 const Extension* extension = AddExtension(); 277 const Extension* extension = AddExtension();
269 NavigateAndCommit(GURL("https://www.google.com")); 278 NavigateAndCommit(GURL("https://www.google.com"));
270 279
271 ActiveTabPermissionGranter* active_tab_permission_granter = 280 ActiveTabPermissionGranter* active_tab_permission_granter =
272 TabHelper::FromWebContents(web_contents()) 281 TabHelper::FromWebContents(web_contents())
273 ->active_tab_permission_granter(); 282 ->active_tab_permission_granter();
274 ASSERT_TRUE(active_tab_permission_granter); 283 ASSERT_TRUE(active_tab_permission_granter);
275 // Grant the extension active tab permissions. This normally happens, e.g., 284 // Grant the extension active tab permissions. This normally happens, e.g.,
276 // if the user clicks on a browser action. 285 // if the user clicks on a browser action.
277 active_tab_permission_granter->GrantIfRequested(extension); 286 active_tab_permission_granter->GrantIfRequested(extension);
278 287
279 // Since we have active tab permissions, we shouldn't need user consent 288 // Since we have active tab permissions, we shouldn't need user consent
280 // anymore. 289 // anymore.
281 EXPECT_FALSE(controller()->RequiresUserConsentForScriptInjection(extension)); 290 EXPECT_FALSE(RequiresUserConsent(extension));
282 291
283 // Also test that granting active tab runs any pending tasks. 292 // Also test that granting active tab runs any pending tasks.
284 Reload(); 293 Reload();
285 // Navigating should mean we need permission again. 294 // Navigating should mean we need permission again.
286 EXPECT_TRUE(controller()->RequiresUserConsentForScriptInjection(extension)); 295 EXPECT_TRUE(RequiresUserConsent(extension));
287 296
288 controller()->RequestScriptInjection( 297 RequestInjection(extension);
289 extension,
290 GetPageId(),
291 GetExecutionCallbackForExtension(extension->id()));
292 EXPECT_TRUE(controller()->GetActionForExtension(extension)); 298 EXPECT_TRUE(controller()->GetActionForExtension(extension));
293 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id())); 299 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
294 300
295 // Grant active tab. 301 // Grant active tab.
296 active_tab_permission_granter->GrantIfRequested(extension); 302 active_tab_permission_granter->GrantIfRequested(extension);
297 303
298 // The pending injections should have run since active tab permission was 304 // The pending injections should have run since active tab permission was
299 // granted. 305 // granted.
300 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id())); 306 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
301 EXPECT_FALSE(controller()->GetActionForExtension(extension)); 307 EXPECT_FALSE(controller()->GetActionForExtension(extension));
302 } 308 }
303 309
304 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsCanHaveAllUrlsPref) { 310 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsCanHaveAllUrlsPref) {
305 const Extension* extension = AddExtension(); 311 const Extension* extension = AddExtension();
306 ASSERT_TRUE(extension); 312 ASSERT_TRUE(extension);
307 313
308 NavigateAndCommit(GURL("https://www.google.com")); 314 NavigateAndCommit(GURL("https://www.google.com"));
309 EXPECT_TRUE(controller()->RequiresUserConsentForScriptInjection(extension)); 315 EXPECT_TRUE(RequiresUserConsent(extension));
310 316
311 // Enable the extension on all urls. 317 // Enable the extension on all urls.
312 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), true); 318 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), true);
313 319
314 EXPECT_FALSE(controller()->RequiresUserConsentForScriptInjection(extension)); 320 EXPECT_FALSE(RequiresUserConsent(extension));
315 // This should carry across navigations, and websites. 321 // This should carry across navigations, and websites.
316 NavigateAndCommit(GURL("http://www.foo.com")); 322 NavigateAndCommit(GURL("http://www.foo.com"));
317 EXPECT_FALSE(controller()->RequiresUserConsentForScriptInjection(extension)); 323 EXPECT_FALSE(RequiresUserConsent(extension));
318 324
319 // Turning off the preference should have instant effect. 325 // Turning off the preference should have instant effect.
320 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), false); 326 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), false);
321 EXPECT_TRUE(controller()->RequiresUserConsentForScriptInjection(extension)); 327 EXPECT_TRUE(RequiresUserConsent(extension));
322 328
323 // And should also persist across navigations and websites. 329 // And should also persist across navigations and websites.
324 NavigateAndCommit(GURL("http://www.bar.com")); 330 NavigateAndCommit(GURL("http://www.bar.com"));
325 EXPECT_TRUE(controller()->RequiresUserConsentForScriptInjection(extension)); 331 EXPECT_TRUE(RequiresUserConsent(extension));
326 } 332 }
327 333
328 } // namespace extensions 334 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698