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 effective SLD (second-level domain) of the URL represented by the
271 // current node into working memory.
272 class StoreNodeEffectiveSLD
: public Operation
{
274 explicit StoreNodeEffectiveSLD(const std::string
& hashed_name
)
275 : hashed_name_(hashed_name
) {
276 DCHECK(IsStringUTF8(hashed_name
));
278 virtual ~StoreNodeEffectiveSLD() {}
279 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
280 std::string possibly_invalid_url
;
281 std::string effective_sld
;
282 if (!context
->current_node()->GetAsString(&possibly_invalid_url
) ||
283 !GetEffectiveSLD(possibly_invalid_url
, &effective_sld
))
285 context
->working_memory()->Set(
286 hashed_name_
, new base::StringValue(context
->GetHash(effective_sld
)));
287 return context
->ContinueExecution();
291 // If |possibly_invalid_url| is a valid URL that has an effective second-level
292 // domain part, outputs that in |effective_sld| and returns true.
293 // Returns false otherwise.
294 static bool GetEffectiveSLD(const std::string
& possibly_invalid_url
,
295 std::string
* effective_sld
) {
296 namespace domains
= net::registry_controlled_domains
;
297 DCHECK(effective_sld
);
298 GURL
url(possibly_invalid_url
);
301 std::string sld_and_registry
= domains::GetDomainAndRegistry(
302 url
.host(), domains::EXCLUDE_PRIVATE_REGISTRIES
);
303 size_t registry_length
= domains::GetRegistryLength(
305 domains::EXCLUDE_UNKNOWN_REGISTRIES
,
306 domains::EXCLUDE_PRIVATE_REGISTRIES
);
307 // Fail unless (1.) the URL has a host part; and (2.) that host part is a
308 // well-formed domain name that ends in, but is not in itself, as a whole,
309 // a recognized registry identifier that is acknowledged by ICANN.
310 if (registry_length
== std::string::npos
|| registry_length
== 0)
312 DCHECK_LT(registry_length
, sld_and_registry
.size());
313 // Subtract one to cut off the dot separating the SLD and the registry.
314 effective_sld
->assign(
315 sld_and_registry
, 0, sld_and_registry
.size() - registry_length
- 1);
319 std::string hashed_name_
;
320 DISALLOW_COPY_AND_ASSIGN(StoreNodeEffectiveSLD
);
323 class CompareNodeBool
: public Operation
{
325 explicit CompareNodeBool(bool value
) : value_(value
) {}
326 virtual ~CompareNodeBool() {}
327 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
328 bool actual_value
= false;
329 if (!context
->current_node()->GetAsBoolean(&actual_value
))
331 if (actual_value
!= value_
)
333 return context
->ContinueExecution();
338 DISALLOW_COPY_AND_ASSIGN(CompareNodeBool
);
341 class CompareNodeHash
: public Operation
{
343 explicit CompareNodeHash(const std::string
& hashed_value
)
344 : hashed_value_(hashed_value
) {}
345 virtual ~CompareNodeHash() {}
346 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
347 std::string actual_hash
;
348 if (!context
->GetValueHash(*context
->current_node(), &actual_hash
) ||
349 actual_hash
!= hashed_value_
)
351 return context
->ContinueExecution();
355 std::string hashed_value_
;
356 DISALLOW_COPY_AND_ASSIGN(CompareNodeHash
);
359 class CompareNodeHashNot
: public Operation
{
361 explicit CompareNodeHashNot(const std::string
& hashed_value
)
362 : hashed_value_(hashed_value
) {}
363 virtual ~CompareNodeHashNot() {}
364 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
365 std::string actual_hash
;
366 if (context
->GetValueHash(*context
->current_node(), &actual_hash
) &&
367 actual_hash
== hashed_value_
)
369 return context
->ContinueExecution();
373 std::string hashed_value_
;
374 DISALLOW_COPY_AND_ASSIGN(CompareNodeHashNot
);
377 template<bool ExpectedTypeIsBooleanNotHashable
>
378 class CompareNodeToStored
: public Operation
{
380 explicit CompareNodeToStored(const std::string
& hashed_name
)
381 : hashed_name_(hashed_name
) {}
382 virtual ~CompareNodeToStored() {}
383 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
384 const base::Value
* stored_value
= NULL
;
385 if (!context
->working_memory()->Get(hashed_name_
, &stored_value
))
387 if (ExpectedTypeIsBooleanNotHashable
) {
388 if (!context
->current_node()->IsType(base::Value::TYPE_BOOLEAN
) ||
389 !context
->current_node()->Equals(stored_value
))
392 std::string actual_hash
;
393 std::string stored_hash
;
394 if (!context
->GetValueHash(*context
->current_node(), &actual_hash
) ||
395 !stored_value
->GetAsString(&stored_hash
) ||
396 actual_hash
!= stored_hash
)
399 return context
->ContinueExecution();
403 std::string hashed_name_
;
404 DISALLOW_COPY_AND_ASSIGN(CompareNodeToStored
);
407 class CompareNodeSubstring
: public Operation
{
409 explicit CompareNodeSubstring(const std::string
& hashed_pattern
,
410 size_t pattern_length
,
412 : hashed_pattern_(hashed_pattern
),
413 pattern_length_(pattern_length
),
414 pattern_sum_(pattern_sum
) {
415 DCHECK(pattern_length_
);
417 virtual ~CompareNodeSubstring() {}
418 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
419 std::string value_as_string
;
420 if (!context
->current_node()->GetAsString(&value_as_string
) ||
421 !pattern_length_
|| value_as_string
.size() < pattern_length_
)
423 // Go over the string with a sliding window. Meanwhile, maintain the sum in
424 // an incremental fashion, and only calculate the SHA-256 hash when the sum
425 // checks out so as to improve performance.
426 std::string::const_iterator window_begin
= value_as_string
.begin();
427 std::string::const_iterator window_end
= window_begin
+ pattern_length_
- 1;
429 std::accumulate(window_begin
, window_end
, static_cast<uint32
>(0u));
430 while (window_end
!= value_as_string
.end()) {
431 window_sum
+= *window_end
++;
432 if (window_sum
== pattern_sum_
&& context
->GetHash(std::string(
433 window_begin
, window_end
)) == hashed_pattern_
)
434 return context
->ContinueExecution();
435 window_sum
-= *window_begin
++;
441 std::string hashed_pattern_
;
442 size_t pattern_length_
;
444 DISALLOW_COPY_AND_ASSIGN(CompareNodeSubstring
);
447 class StopExecutingSentenceOperation
: public Operation
{
449 StopExecutingSentenceOperation() {}
450 virtual ~StopExecutingSentenceOperation() {}
451 virtual bool Execute(ExecutionContext
* context
) OVERRIDE
{
456 DISALLOW_COPY_AND_ASSIGN(StopExecutingSentenceOperation
);
461 explicit Parser(const std::string
& program
)
463 next_instruction_index_(0u) {}
465 bool ParseNextSentence(ScopedVector
<Operation
>* output
) {
466 ScopedVector
<Operation
> operators
;
467 bool sentence_ended
= false;
468 while (next_instruction_index_
< program_
.size() && !sentence_ended
) {
470 if (!ReadOpCode(&op_code
))
472 switch (static_cast<jtl_foundation::OpCodes
>(op_code
)) {
473 case jtl_foundation::NAVIGATE
: {
474 std::string hashed_key
;
475 if (!ReadHash(&hashed_key
))
477 operators
.push_back(new NavigateOperation(hashed_key
));
480 case jtl_foundation::NAVIGATE_ANY
:
481 operators
.push_back(new NavigateAnyOperation
);
483 case jtl_foundation::NAVIGATE_BACK
:
484 operators
.push_back(new NavigateBackOperation
);
486 case jtl_foundation::STORE_BOOL
: {
487 std::string hashed_name
;
488 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
491 if (!ReadBool(&value
))
493 operators
.push_back(new StoreValue(
495 scoped_ptr
<base::Value
>(new base::FundamentalValue(value
))));
498 case jtl_foundation::COMPARE_STORED_BOOL
: {
499 std::string hashed_name
;
500 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
503 if (!ReadBool(&value
))
505 bool default_value
= false;
506 if (!ReadBool(&default_value
))
508 operators
.push_back(new CompareStoredValue(
510 scoped_ptr
<base::Value
>(new base::FundamentalValue(value
)),
511 scoped_ptr
<base::Value
>(
512 new base::FundamentalValue(default_value
))));
515 case jtl_foundation::STORE_HASH
: {
516 std::string hashed_name
;
517 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
519 std::string hashed_value
;
520 if (!ReadHash(&hashed_value
))
522 operators
.push_back(new StoreValue(
524 scoped_ptr
<base::Value
>(new base::StringValue(hashed_value
))));
527 case jtl_foundation::COMPARE_STORED_HASH
: {
528 std::string hashed_name
;
529 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
531 std::string hashed_value
;
532 if (!ReadHash(&hashed_value
))
534 std::string hashed_default_value
;
535 if (!ReadHash(&hashed_default_value
))
537 operators
.push_back(new CompareStoredValue(
539 scoped_ptr
<base::Value
>(new base::StringValue(hashed_value
)),
540 scoped_ptr
<base::Value
>(
541 new base::StringValue(hashed_default_value
))));
544 case jtl_foundation::STORE_NODE_BOOL
: {
545 std::string hashed_name
;
546 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
548 operators
.push_back(new StoreNodeValue
<true>(hashed_name
));
551 case jtl_foundation::STORE_NODE_HASH
: {
552 std::string hashed_name
;
553 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
555 operators
.push_back(new StoreNodeValue
<false>(hashed_name
));
558 case jtl_foundation::STORE_NODE_EFFECTIVE_SLD_HASH
: {
559 std::string hashed_name
;
560 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
562 operators
.push_back(new StoreNodeEffectiveSLD(hashed_name
));
565 case jtl_foundation::COMPARE_NODE_BOOL
: {
567 if (!ReadBool(&value
))
569 operators
.push_back(new CompareNodeBool(value
));
572 case jtl_foundation::COMPARE_NODE_HASH
: {
573 std::string hashed_value
;
574 if (!ReadHash(&hashed_value
))
576 operators
.push_back(new CompareNodeHash(hashed_value
));
579 case jtl_foundation::COMPARE_NODE_HASH_NOT
: {
580 std::string hashed_value
;
581 if (!ReadHash(&hashed_value
))
583 operators
.push_back(new CompareNodeHashNot(hashed_value
));
586 case jtl_foundation::COMPARE_NODE_TO_STORED_BOOL
: {
587 std::string hashed_name
;
588 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
590 operators
.push_back(new CompareNodeToStored
<true>(hashed_name
));
593 case jtl_foundation::COMPARE_NODE_TO_STORED_HASH
: {
594 std::string hashed_name
;
595 if (!ReadHash(&hashed_name
) || !IsStringUTF8(hashed_name
))
597 operators
.push_back(new CompareNodeToStored
<false>(hashed_name
));
600 case jtl_foundation::COMPARE_NODE_SUBSTRING
: {
601 std::string hashed_pattern
;
602 uint32 pattern_length
= 0, pattern_sum
= 0;
603 if (!ReadHash(&hashed_pattern
))
605 if (!ReadUint32(&pattern_length
) || pattern_length
== 0)
607 if (!ReadUint32(&pattern_sum
))
609 operators
.push_back(new CompareNodeSubstring(
610 hashed_pattern
, pattern_length
, pattern_sum
));
613 case jtl_foundation::STOP_EXECUTING_SENTENCE
:
614 operators
.push_back(new StopExecutingSentenceOperation
);
616 case jtl_foundation::END_OF_SENTENCE
:
617 sentence_ended
= true;
623 output
->swap(operators
);
627 bool HasNextSentence() const {
628 return next_instruction_index_
< program_
.size();
632 // Reads an uint8 and returns whether this operation was successful.
633 bool ReadUint8(uint8
* out
) {
635 if (next_instruction_index_
+ 1u > program_
.size())
637 *out
= static_cast<uint8
>(program_
[next_instruction_index_
]);
638 ++next_instruction_index_
;
642 // Reads an uint32 and returns whether this operation was successful.
643 bool ReadUint32(uint32
* out
) {
645 if (next_instruction_index_
+ 4u > program_
.size())
648 for (int i
= 0; i
< 4; ++i
) {
650 *out
|= static_cast<uint8
>(program_
[next_instruction_index_
]) << 24;
651 ++next_instruction_index_
;
656 // Reads an operator code and returns whether this operation was successful.
657 bool ReadOpCode(uint8
* out
) { return ReadUint8(out
); }
659 bool ReadHash(std::string
* out
) {
661 if (next_instruction_index_
+ jtl_foundation::kHashSizeInBytes
>
664 *out
= program_
.substr(next_instruction_index_
,
665 jtl_foundation::kHashSizeInBytes
);
666 next_instruction_index_
+= jtl_foundation::kHashSizeInBytes
;
667 DCHECK(jtl_foundation::Hasher::IsHash(*out
));
671 bool ReadBool(bool* out
) {
674 if (!ReadUint8(&value
))
685 std::string program_
;
686 size_t next_instruction_index_
;
687 DISALLOW_COPY_AND_ASSIGN(Parser
);
692 JtlInterpreter::JtlInterpreter(
693 const std::string
& hasher_seed
,
694 const std::string
& program
,
695 const base::DictionaryValue
* input
)
696 : hasher_seed_(hasher_seed
),
699 working_memory_(new base::DictionaryValue
),
701 DCHECK(input
->IsType(base::Value::TYPE_DICTIONARY
));
704 JtlInterpreter::~JtlInterpreter() {}
706 void JtlInterpreter::Execute() {
707 jtl_foundation::Hasher
hasher(hasher_seed_
);
708 Parser
parser(program_
);
709 while (parser
.HasNextSentence()) {
710 ScopedVector
<Operation
> sentence
;
711 if (!parser
.ParseNextSentence(&sentence
)) {
712 result_
= PARSE_ERROR
;
715 ExecutionContext
context(
716 &hasher
, sentence
.get(), input_
, working_memory_
.get());
717 context
.ContinueExecution();
718 if (context
.error()) {
719 result_
= RUNTIME_ERROR
;
725 bool JtlInterpreter::GetOutputBoolean(const std::string
& unhashed_key
,
726 bool* output
) const {
727 std::string hashed_key
=
728 jtl_foundation::Hasher(hasher_seed_
).GetHash(unhashed_key
);
729 return working_memory_
->GetBoolean(hashed_key
, output
);
732 bool JtlInterpreter::GetOutputString(const std::string
& unhashed_key
,
733 std::string
* output
) const {
734 std::string hashed_key
=
735 jtl_foundation::Hasher(hasher_seed_
).GetHash(unhashed_key
);
736 return working_memory_
->GetString(hashed_key
, output
);