1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/profile_resetter/jtl_interpreter.h"
9 #include "base/memory/scoped_vector.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "chrome/browser/profile_resetter/jtl_foundation.h"
13 #include "crypto/hmac.h"
14 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
19 class ExecutionContext
;
21 // An operation in an interpreted program.
24 virtual ~Operation() {}
25 // Executes the operation on the specified context and instructs the context
26 // to continue execution with the next instruction if appropriate.
27 // Returns true if we should continue with any potential backtracking that
29 virtual bool Execute(ExecutionContext
* context
) = 0;
32 // An execution context of operations.
33 class ExecutionContext
{
35 // |input| is the root of a dictionary that stores the information the
36 // sentence is evaluated on.
37 ExecutionContext(const jtl_foundation::Hasher
* hasher
,
38 const std::vector
<Operation
*>& sentence
,
39 const base::DictionaryValue
* input
,
40 base::DictionaryValue
* working_memory
)
43 next_instruction_index_(0u),
44 working_memory_(working_memory
),
46 stack_
.push_back(input
);
48 ~ExecutionContext() {}
50 // Returns true in case of success.
51 bool ContinueExecution() {
52 if (error_
|| stack_
.empty()) {
56 if (next_instruction_index_
>= sentence_
.size())
59 Operation
* op
= sentence_
[next_instruction_index_
];
60 next_instruction_index_
++;
61 bool continue_traversal
= op
->Execute(this);
62 next_instruction_index_
--;
63 return continue_traversal
;
66 std::string
GetHash(const std::string
& input
) {
67 return hasher_
->GetHash(input
);
70 // Calculates the |hash| of a string, integer or double |value|, and returns
71 // true. Returns false otherwise.
72 bool GetValueHash(const base::Value
& value
, std::string
* hash
) {
74 std::string value_as_string
;
76 double tmp_double
= 0.0;
77 if (value
.GetAsInteger(&tmp_int
))
78 value_as_string
= base::IntToString(tmp_int
);
79 else if (value
.GetAsDouble(&tmp_double
))
80 value_as_string
= base::DoubleToString(tmp_double
);
81 else if (!value
.GetAsString(&value_as_string
))
83 *hash
= GetHash(value_as_string
);
87 const base::Value
* current_node() const { return stack_
.back(); }
88 std::vector
<const base::Value
*>* stack() { return &stack_
; }
89 base::DictionaryValue
* working_memory() { return working_memory_
; }
90 bool error() const { return error_
; }
93 // A hasher used to hash node names in a dictionary.
94 const jtl_foundation::Hasher
* hasher_
;
95 // The sentence to be executed.
96 const std::vector
<Operation
*> sentence_
;
97 // Position in |sentence_|.
98 size_t next_instruction_index_
;
99 // A stack of Values, indicating a navigation path from the root node of
100 // |input| (see constructor) to the current node on which the
101 // sentence_[next_instruction_index_] is evaluated.
102 std::vector
<const base::Value
*> stack_
;
103 // Memory into which values can be stored by the program.
104 base::DictionaryValue
* working_memory_
;
105 // Whether a runtime error occurred.
107 DISALLOW_COPY_AND_ASSIGN(ExecutionContext
);
110 class NavigateOperation
: public Operation
{
112 explicit NavigateOperation(const std::string
& hashed_key
)
113 : hashed_key_(hashed_key
) {}
114 virtual ~NavigateOperation() {}
115 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
116 const base::DictionaryValue
* dict
= NULL
;
117 if (!context
->current_node()->GetAsDictionary(&dict
)) {
118 // Just ignore this node gracefully as this navigation is a dead end.
119 // If this NavigateOperation occurred after a NavigateAny operation, those
120 // may still be fulfillable, so we allow continuing the execution of the
121 // sentence on other nodes.
124 for (base::DictionaryValue::Iterator
i(*dict
); !i
.IsAtEnd(); i
.Advance()) {
125 if (context
->GetHash(i
.key()) != hashed_key_
)
127 context
->stack()->push_back(&i
.value());
128 bool continue_traversal
= context
->ContinueExecution();
129 context
->stack()->pop_back();
130 if (!continue_traversal
)
137 std::string hashed_key_
;
138 DISALLOW_COPY_AND_ASSIGN(NavigateOperation
);
141 class NavigateAnyOperation
: public Operation
{
143 NavigateAnyOperation() {}
144 virtual ~NavigateAnyOperation() {}
145 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
146 const base::DictionaryValue
* dict
= NULL
;
147 const base::ListValue
* list
= NULL
;
148 if (context
->current_node()->GetAsDictionary(&dict
)) {
149 for (base::DictionaryValue::Iterator
i(*dict
);
150 !i
.IsAtEnd(); i
.Advance()) {
151 context
->stack()->push_back(&i
.value());
152 bool continue_traversal
= context
->ContinueExecution();
153 context
->stack()->pop_back();
154 if (!continue_traversal
)
157 } else if (context
->current_node()->GetAsList(&list
)) {
158 for (base::ListValue::const_iterator i
= list
->begin();
159 i
!= list
->end(); ++i
) {
160 context
->stack()->push_back(*i
);
161 bool continue_traversal
= context
->ContinueExecution();
162 context
->stack()->pop_back();
163 if (!continue_traversal
)
167 // Do nothing, just ignore this node.
173 DISALLOW_COPY_AND_ASSIGN(NavigateAnyOperation
);
176 class NavigateBackOperation
: public Operation
{
178 NavigateBackOperation() {}
179 virtual ~NavigateBackOperation() {}
180 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
181 const base::Value
* current_node
= context
->current_node();
182 context
->stack()->pop_back();
183 bool continue_traversal
= context
->ContinueExecution();
184 context
->stack()->push_back(current_node
);
185 return continue_traversal
;
189 DISALLOW_COPY_AND_ASSIGN(NavigateBackOperation
);
192 class StoreValue
: public Operation
{
194 StoreValue(const std::string
& hashed_name
, scoped_ptr
<base::Value
> value
)
195 : hashed_name_(hashed_name
),
196 value_(value
.Pass()) {
197 DCHECK(IsStringUTF8(hashed_name
));
200 virtual ~StoreValue() {}
201 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
202 context
->working_memory()->Set(hashed_name_
, value_
->DeepCopy());
203 return context
->ContinueExecution();
207 std::string hashed_name_
;
208 scoped_ptr
<base::Value
> value_
;
209 DISALLOW_COPY_AND_ASSIGN(StoreValue
);
212 class CompareStoredValue
: public Operation
{
214 CompareStoredValue(const std::string
& hashed_name
,
215 scoped_ptr
<base::Value
> value
,
216 scoped_ptr
<base::Value
> default_value
)
217 : hashed_name_(hashed_name
),
218 value_(value
.Pass()),
219 default_value_(default_value
.Pass()) {
220 DCHECK(IsStringUTF8(hashed_name
));
222 DCHECK(default_value_
);
224 virtual ~CompareStoredValue() {}
225 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
226 const base::Value
* actual_value
= NULL
;
227 if (!context
->working_memory()->Get(hashed_name_
, &actual_value
))
228 actual_value
= default_value_
.get();
229 if (!value_
->Equals(actual_value
))
231 return context
->ContinueExecution();
235 std::string hashed_name_
;
236 scoped_ptr
<base::Value
> value_
;
237 scoped_ptr
<base::Value
> default_value_
;
238 DISALLOW_COPY_AND_ASSIGN(CompareStoredValue
);
241 template<bool ExpectedTypeIsBooleanNotHashable
>
242 class StoreNodeValue
: public Operation
{
244 explicit StoreNodeValue(const std::string
& hashed_name
)
245 : hashed_name_(hashed_name
) {
246 DCHECK(IsStringUTF8(hashed_name
));
248 virtual ~StoreNodeValue() {}
249 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
250 scoped_ptr
<base::Value
> value
;
251 if (ExpectedTypeIsBooleanNotHashable
) {
252 if (!context
->current_node()->IsType(base::Value::TYPE_BOOLEAN
))
254 value
.reset(context
->current_node()->DeepCopy());
257 if (!context
->GetValueHash(*context
->current_node(), &hash
))
259 value
.reset(new base::StringValue(hash
));
261 context
->working_memory()->Set(hashed_name_
, value
.release());
262 return context
->ContinueExecution();
266 std::string hashed_name_
;
267 DISALLOW_COPY_AND_ASSIGN(StoreNodeValue
);
270 // Stores the hash of the registerable domain name -- as in, the portion of the
271 // domain that is registerable, as opposed to controlled by a registrar; without
272 // subdomains -- of the URL represented by the current node into working memory.
273 class StoreNodeRegisterableDomain
: public Operation
{
275 explicit StoreNodeRegisterableDomain(const std::string
& hashed_name
)
276 : hashed_name_(hashed_name
) {
277 DCHECK(IsStringUTF8(hashed_name
));
279 virtual ~StoreNodeRegisterableDomain() {}
280 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
281 std::string possibly_invalid_url
;
283 if (!context
->current_node()->GetAsString(&possibly_invalid_url
) ||
284 !GetRegisterableDomain(possibly_invalid_url
, &domain
))
286 context
->working_memory()->Set(
287 hashed_name_
, new base::StringValue(context
->GetHash(domain
)));
288 return context
->ContinueExecution();
292 // If |possibly_invalid_url| is a valid URL having a registerable domain name
293 // part, outputs that in |registerable_domain| and returns true. Otherwise,
295 static bool GetRegisterableDomain(const std::string
& possibly_invalid_url
,
296 std::string
* registerable_domain
) {
297 namespace domains
= net::registry_controlled_domains
;
298 DCHECK(registerable_domain
);
299 GURL
url(possibly_invalid_url
);
302 std::string registry_plus_one
= domains::GetDomainAndRegistry(
303 url
.host(), domains::INCLUDE_PRIVATE_REGISTRIES
);
304 size_t registry_length
= domains::GetRegistryLength(
306 domains::INCLUDE_UNKNOWN_REGISTRIES
,
307 domains::INCLUDE_PRIVATE_REGISTRIES
);
308 // Fail unless (1.) the URL has a host part; and (2.) that host part is a
309 // well-formed domain name consisting of at least one subcomponent; followed
310 // by either a recognized registry identifier, or exactly one subcomponent,
311 // which is then assumed to be the unknown registry identifier.
312 if (registry_length
== std::string::npos
|| registry_length
== 0)
314 DCHECK_LT(registry_length
, registry_plus_one
.size());
315 // Subtract one to cut off the dot separating the SLD and the registry.
316 registerable_domain
->assign(
317 registry_plus_one
, 0, registry_plus_one
.size() - registry_length
- 1);
321 std::string hashed_name_
;
322 DISALLOW_COPY_AND_ASSIGN(StoreNodeRegisterableDomain
);
325 class CompareNodeBool
: public Operation
{
327 explicit CompareNodeBool(bool value
) : value_(value
) {}
328 virtual ~CompareNodeBool() {}
329 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
330 bool actual_value
= false;
331 if (!context
->current_node()->GetAsBoolean(&actual_value
))
333 if (actual_value
!= value_
)
335 return context
->ContinueExecution();
340 DISALLOW_COPY_AND_ASSIGN(CompareNodeBool
);
343 class CompareNodeHash
: public Operation
{
345 explicit CompareNodeHash(const std::string
& hashed_value
)
346 : hashed_value_(hashed_value
) {}
347 virtual ~CompareNodeHash() {}
348 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
349 std::string actual_hash
;
350 if (!context
->GetValueHash(*context
->current_node(), &actual_hash
) ||
351 actual_hash
!= hashed_value_
)
353 return context
->ContinueExecution();
357 std::string hashed_value_
;
358 DISALLOW_COPY_AND_ASSIGN(CompareNodeHash
);
361 class CompareNodeHashNot
: public Operation
{
363 explicit CompareNodeHashNot(const std::string
& hashed_value
)
364 : hashed_value_(hashed_value
) {}
365 virtual ~CompareNodeHashNot() {}
366 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
367 std::string actual_hash
;
368 if (context
->GetValueHash(*context
->current_node(), &actual_hash
) &&
369 actual_hash
== hashed_value_
)
371 return context
->ContinueExecution();
375 std::string hashed_value_
;
376 DISALLOW_COPY_AND_ASSIGN(CompareNodeHashNot
);
379 template<bool ExpectedTypeIsBooleanNotHashable
>
380 class CompareNodeToStored
: public Operation
{
382 explicit CompareNodeToStored(const std::string
& hashed_name
)
383 : hashed_name_(hashed_name
) {}
384 virtual ~CompareNodeToStored() {}
385 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
386 const base::Value
* stored_value
= NULL
;
387 if (!context
->working_memory()->Get(hashed_name_
, &stored_value
))
389 if (ExpectedTypeIsBooleanNotHashable
) {
390 if (!context
->current_node()->IsType(base::Value::TYPE_BOOLEAN
) ||
391 !context
->current_node()->Equals(stored_value
))
394 std::string actual_hash
;
395 std::string stored_hash
;
396 if (!context
->GetValueHash(*context
->current_node(), &actual_hash
) ||
397 !stored_value
->GetAsString(&stored_hash
) ||
398 actual_hash
!= stored_hash
)
401 return context
->ContinueExecution();
405 std::string hashed_name_
;
406 DISALLOW_COPY_AND_ASSIGN(CompareNodeToStored
);
409 class CompareNodeSubstring
: public Operation
{
411 explicit CompareNodeSubstring(const std::string
& hashed_pattern
,
412 size_t pattern_length
,
414 : hashed_pattern_(hashed_pattern
),
415 pattern_length_(pattern_length
),
416 pattern_sum_(pattern_sum
) {
417 DCHECK(pattern_length_
);
419 virtual ~CompareNodeSubstring() {}
420 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
421 std::string value_as_string
;
422 if (!context
->current_node()->GetAsString(&value_as_string
) ||
423 !pattern_length_
|| value_as_string
.size() < pattern_length_
)
425 // Go over the string with a sliding window. Meanwhile, maintain the sum in
426 // an incremental fashion, and only calculate the SHA-256 hash when the sum
427 // checks out so as to improve performance.
428 std::string::const_iterator window_begin
= value_as_string
.begin();
429 std::string::const_iterator window_end
= window_begin
+ pattern_length_
- 1;
431 std::accumulate(window_begin
, window_end
, static_cast<uint32
>(0u));
432 while (window_end
!= value_as_string
.end()) {
433 window_sum
+= *window_end
++;
434 if (window_sum
== pattern_sum_
&& context
->GetHash(std::string(
435 window_begin
, window_end
)) == hashed_pattern_
)
436 return context
->ContinueExecution();
437 window_sum
-= *window_begin
++;
443 std::string hashed_pattern_
;
444 size_t pattern_length_
;
446 DISALLOW_COPY_AND_ASSIGN(CompareNodeSubstring
);
449 class StopExecutingSentenceOperation
: public Operation
{
451 StopExecutingSentenceOperation() {}
452 virtual ~StopExecutingSentenceOperation() {}
453 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
458 DISALLOW_COPY_AND_ASSIGN(StopExecutingSentenceOperation
);
463 explicit Parser(const std::string
& program
)
465 next_instruction_index_(0u) {}
467 bool ParseNextSentence(ScopedVector
<Operation
>* output
) {
468 ScopedVector
<Operation
> operators
;
469 bool sentence_ended
= false;
470 while (next_instruction_index_
< program_
.size() && !sentence_ended
) {
472 if (!ReadOpCode(&op_code
))
474 switch (static_cast<jtl_foundation::OpCodes
>(op_code
)) {
475 case jtl_foundation::NAVIGATE
: {
476 std::string hashed_key
;
477 if (!ReadHash(&hashed_key
))
479 operators
.push_back(new NavigateOperation(hashed_key
));
482 case jtl_foundation::NAVIGATE_ANY
:
483 operators
.push_back(new NavigateAnyOperation
);
485 case jtl_foundation::NAVIGATE_BACK
:
486 operators
.push_back(new NavigateBackOperation
);
488 case jtl_foundation::STORE_BOOL
: {
489 std::string hashed_name
;
490 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
493 if (!ReadBool(&value
))
495 operators
.push_back(new StoreValue(
497 scoped_ptr
<base::Value
>(new base::FundamentalValue(value
))));
500 case jtl_foundation::COMPARE_STORED_BOOL
: {
501 std::string hashed_name
;
502 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
505 if (!ReadBool(&value
))
507 bool default_value
= false;
508 if (!ReadBool(&default_value
))
510 operators
.push_back(new CompareStoredValue(
512 scoped_ptr
<base::Value
>(new base::FundamentalValue(value
)),
513 scoped_ptr
<base::Value
>(
514 new base::FundamentalValue(default_value
))));
517 case jtl_foundation::STORE_HASH
: {
518 std::string hashed_name
;
519 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
521 std::string hashed_value
;
522 if (!ReadHash(&hashed_value
))
524 operators
.push_back(new StoreValue(
526 scoped_ptr
<base::Value
>(new base::StringValue(hashed_value
))));
529 case jtl_foundation::COMPARE_STORED_HASH
: {
530 std::string hashed_name
;
531 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
533 std::string hashed_value
;
534 if (!ReadHash(&hashed_value
))
536 std::string hashed_default_value
;
537 if (!ReadHash(&hashed_default_value
))
539 operators
.push_back(new CompareStoredValue(
541 scoped_ptr
<base::Value
>(new base::StringValue(hashed_value
)),
542 scoped_ptr
<base::Value
>(
543 new base::StringValue(hashed_default_value
))));
546 case jtl_foundation::STORE_NODE_BOOL
: {
547 std::string hashed_name
;
548 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
550 operators
.push_back(new StoreNodeValue
<true>(hashed_name
));
553 case jtl_foundation::STORE_NODE_HASH
: {
554 std::string hashed_name
;
555 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
557 operators
.push_back(new StoreNodeValue
<false>(hashed_name
));
560 case jtl_foundation::STORE_NODE_REGISTERABLE_DOMAIN_HASH
: {
561 std::string hashed_name
;
562 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
564 operators
.push_back(new StoreNodeRegisterableDomain(hashed_name
));
567 case jtl_foundation::COMPARE_NODE_BOOL
: {
569 if (!ReadBool(&value
))
571 operators
.push_back(new CompareNodeBool(value
));
574 case jtl_foundation::COMPARE_NODE_HASH
: {
575 std::string hashed_value
;
576 if (!ReadHash(&hashed_value
))
578 operators
.push_back(new CompareNodeHash(hashed_value
));
581 case jtl_foundation::COMPARE_NODE_HASH_NOT
: {
582 std::string hashed_value
;
583 if (!ReadHash(&hashed_value
))
585 operators
.push_back(new CompareNodeHashNot(hashed_value
));
588 case jtl_foundation::COMPARE_NODE_TO_STORED_BOOL
: {
589 std::string hashed_name
;
590 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
592 operators
.push_back(new CompareNodeToStored
<true>(hashed_name
));
595 case jtl_foundation::COMPARE_NODE_TO_STORED_HASH
: {
596 std::string hashed_name
;
597 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
599 operators
.push_back(new CompareNodeToStored
<false>(hashed_name
));
602 case jtl_foundation::COMPARE_NODE_SUBSTRING
: {
603 std::string hashed_pattern
;
604 uint32 pattern_length
= 0, pattern_sum
= 0;
605 if (!ReadHash(&hashed_pattern
))
607 if (!ReadUint32(&pattern_length
) || pattern_length
== 0)
609 if (!ReadUint32(&pattern_sum
))
611 operators
.push_back(new CompareNodeSubstring(
612 hashed_pattern
, pattern_length
, pattern_sum
));
615 case jtl_foundation::STOP_EXECUTING_SENTENCE
:
616 operators
.push_back(new StopExecutingSentenceOperation
);
618 case jtl_foundation::END_OF_SENTENCE
:
619 sentence_ended
= true;
625 output
->swap(operators
);
629 bool HasNextSentence() const {
630 return next_instruction_index_
< program_
.size();
634 // Reads an uint8 and returns whether this operation was successful.
635 bool ReadUint8(uint8
* out
) {
637 if (next_instruction_index_
+ 1u > program_
.size())
639 *out
= static_cast<uint8
>(program_
[next_instruction_index_
]);
640 ++next_instruction_index_
;
644 // Reads an uint32 and returns whether this operation was successful.
645 bool ReadUint32(uint32
* out
) {
647 if (next_instruction_index_
+ 4u > program_
.size())
650 for (int i
= 0; i
< 4; ++i
) {
652 *out
|= static_cast<uint8
>(program_
[next_instruction_index_
]) << 24;
653 ++next_instruction_index_
;
658 // Reads an operator code and returns whether this operation was successful.
659 bool ReadOpCode(uint8
* out
) { return ReadUint8(out
); }
661 bool ReadHash(std::string
* out
) {
663 if (next_instruction_index_
+ jtl_foundation::kHashSizeInBytes
>
666 *out
= program_
.substr(next_instruction_index_
,
667 jtl_foundation::kHashSizeInBytes
);
668 next_instruction_index_
+= jtl_foundation::kHashSizeInBytes
;
669 DCHECK(jtl_foundation::Hasher::IsHash(*out
));
673 bool ReadBool(bool* out
) {
676 if (!ReadUint8(&value
))
687 std::string program_
;
688 size_t next_instruction_index_
;
689 DISALLOW_COPY_AND_ASSIGN(Parser
);
694 JtlInterpreter::JtlInterpreter(
695 const std::string
& hasher_seed
,
696 const std::string
& program
,
697 const base::DictionaryValue
* input
)
698 : hasher_seed_(hasher_seed
),
701 working_memory_(new base::DictionaryValue
),
703 DCHECK(input
->IsType(base::Value::TYPE_DICTIONARY
));
706 JtlInterpreter::~JtlInterpreter() {}
708 void JtlInterpreter::Execute() {
709 jtl_foundation::Hasher
hasher(hasher_seed_
);
710 Parser
parser(program_
);
711 while (parser
.HasNextSentence()) {
712 ScopedVector
<Operation
> sentence
;
713 if (!parser
.ParseNextSentence(&sentence
)) {
714 result_
= PARSE_ERROR
;
717 ExecutionContext
context(
718 &hasher
, sentence
.get(), input_
, working_memory_
.get());
719 context
.ContinueExecution();
720 if (context
.error()) {
721 result_
= RUNTIME_ERROR
;
727 bool JtlInterpreter::GetOutputBoolean(const std::string
& unhashed_key
,
728 bool* output
) const {
729 std::string hashed_key
=
730 jtl_foundation::Hasher(hasher_seed_
).GetHash(unhashed_key
);
731 return working_memory_
->GetBoolean(hashed_key
, output
);
734 bool JtlInterpreter::GetOutputString(const std::string
& unhashed_key
,
735 std::string
* output
) const {
736 std::string hashed_key
=
737 jtl_foundation::Hasher(hasher_seed_
).GetHash(unhashed_key
);
738 return working_memory_
->GetString(hashed_key
, output
);