Skip to content

Commit

Permalink
Merged Pull Request '#146 feature/ghev-override->version/4.5: FEAT: P…
Browse files Browse the repository at this point in the history
…revent C code returning GHEV JavaScript when the evidence already contains the required values.'

FEAT: Prevent C code returning GHEV JavaScript when the evidence already contains the required values.
  • Loading branch information
Automation51D authored Nov 5, 2024
2 parents 71e80f4 + 1944eec commit 1d620b6
Show file tree
Hide file tree
Showing 22 changed files with 1,110 additions and 234 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
<ClInclude Include="..\..\src\fiftyone.h" />
<ClInclude Include="..\..\src\results-dd.h" />
<ClInclude Include="..\..\src\transform.h" />
<ClInclude Include="..\..\src\gethighentropyvalues.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\dataset-dd.c" />
<ClCompile Include="..\..\src\results-dd.c" />
<ClCompile Include="..\..\src\transform.c" />
<ClCompile Include="..\..\src\gethighentropyvalues.c" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\common-cxx\VisualStudio\FiftyOne.Common.C\FiftyOne.Common.C.vcxproj">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<ClInclude Include="..\..\test\Constants.hpp" />
<ClInclude Include="..\..\test\EngineDeviceDetectionTests.hpp" />
<ClInclude Include="..\..\test\ExampleDeviceDetectionTests.hpp" />
<ClInclude Include="..\..\test\hash\SimpleEngineTestBase.hpp" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\common-cxx\tests\Base.cpp" />
Expand Down Expand Up @@ -50,6 +51,8 @@
<ClCompile Include="..\..\test\hash\HashMemLeakReloadFromFileTests.cpp" />
<ClCompile Include="..\..\test\hash\TransformTests.cpp" />
<ClCompile Include="..\..\test\hash\ResultsHashSerializerTests.cpp" />
<ClCompile Include="..\..\test\hash\SuppressSnippetTests.cpp" />
<ClCompile Include="..\..\test\hash\SimpleEngineTestBase.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
Expand Down
177 changes: 128 additions & 49 deletions examples/C/Hash/GettingStarted.c
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
/* *********************************************************************
* This Original Work is copyright of 51 Degrees Mobile Experts Limited.
* Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House,
* Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU.
*
* This Original Work is licensed under the European Union Public Licence
* (EUPL) v.1.2 and is subject to its terms as set out below.
*
* If a copy of the EUPL was not distributed with this file, You can obtain
* one at https://opensource.org/licenses/EUPL-1.2.
*
* The 'Compatible Licences' set out in the Appendix to the EUPL (as may be
* amended by the European Commission) shall be deemed incompatible for
* the purposes of the Work and the provisions of the compatibility
* clause in Article 5 of the EUPL shall not apply.
*
* If using the Work as, or as part of, a network application, by
* including the attribution notice(s) required under Article 5 of the EUPL
* in the end user terms of the application under an appropriate heading,
* such notice(s) shall fulfill the requirements of that article.
/* *********************************************************************
* This Original Work is copyright of 51 Degrees Mobile Experts Limited.
* Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House,
* Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU.
*
* This Original Work is licensed under the European Union Public Licence
* (EUPL) v.1.2 and is subject to its terms as set out below.
*
* If a copy of the EUPL was not distributed with this file, You can obtain
* one at https://opensource.org/licenses/EUPL-1.2.
*
* The 'Compatible Licences' set out in the Appendix to the EUPL (as may be
* amended by the European Commission) shall be deemed incompatible for
* the purposes of the Work and the provisions of the compatibility
* clause in Article 5 of the EUPL shall not apply.
*
* If using the Work as, or as part of, a network application, by
* including the attribution notice(s) required under Article 5 of the EUPL
* in the end user terms of the application under an appropriate heading,
* such notice(s) shall fulfill the requirements of that article.
* ********************************************************************* */

/**
Expand All @@ -43,7 +43,7 @@ This example is available in full on [GitHub](https://github.com/51Degrees/devic
// which requires to be included before 'malloc.h'.
#include "ExampleBase.h"

#define MAX_EVIDENCE 7
#define MAX_EVIDENCE 8

static const char *dataDir = "device-detection-data";

Expand All @@ -58,7 +58,8 @@ static const char *dataDir = "device-detection-data";
static const char *dataFileName = "51Degrees-LiteV4.1.hash";

static char valueBuffer[1024] = "";
static const size_t valueBufferLength = sizeof(valueBuffer) / sizeof(valueBuffer[0]);
static const size_t valueBufferLength =
sizeof(valueBuffer) / sizeof(valueBuffer[0]);

typedef struct {
uint32_t count;
Expand Down Expand Up @@ -128,13 +129,22 @@ static evidence userAgentWithMobileID = {

static evidence headersWithWebView = {
7,
{ {FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "user-agent", "Mozilla/5.0 (Linux; Android 13; RMX3762 Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/122.0.6261.106 Mobile Safari/537.36 TwitterAndroid"},
{ {FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "user-agent",
"Mozilla/5.0 (Linux; Android 13; RMX3762 Build/TP1A.220624.014; wv) "
"AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/122.0.6261.106 "
"Mobile Safari/537.36 TwitterAndroid"},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-mobile", "?1"},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua", "\"Chromium\";v=\"122\", \"Not(A:Brand\";v=\"24\", \"Android WebView\";v=\"122\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-platform", "\"Android\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-platform-version", "\"13.0.0\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-model", "\"RMX3762\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-full-version", "\"122.0.6261.106\""} }
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua",
"\"Chromium\";v=\"122\", \"Not(A:Brand\";v=\"24\", "
"\"Android WebView\";v=\"122\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-platform",
"\"Android\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-platform-version",
"\"13.0.0\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-model",
"\"RMX3762\""},
{FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "sec-ch-ua-full-version",
"\"122.0.6261.106\""} }
};

// Base 64 string for the JSON returned from a call to getHighEntropyValues
Expand All @@ -143,10 +153,15 @@ static evidence headersWithWebView = {
// 64 being returned to a second server request for device detection. Providing
// the value direct from user code would not be an expected use of the feature.
// The example is included to help those starting to work with the project
// understand that evidence is not always HTTP headers.
// understand that evidence is not always HTTP headers. A normal user-agent is
// included to show how this is ignored when better evidence is available.
static evidence getHighEntropyValues = {
1,
{ {FIFTYONE_DEGREES_EVIDENCE_QUERY, FIFTYONE_DEGREES_EVIDENCE_HIGH_ENTROPY_VALUES,
2,
{ {FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "user-agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, "
"like Gecko) Chrome/98.0.4758.102 Safari/537.36"},
{FIFTYONE_DEGREES_EVIDENCE_QUERY,
FIFTYONE_DEGREES_EVIDENCE_HIGH_ENTROPY_VALUES,
"eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh"
"bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t"
"ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv"
Expand Down Expand Up @@ -220,6 +235,33 @@ static void reportStatus(StatusCode status,
Free((void*)message);
}

/**
* Report the header value from the evidence iteration. May not be the same as
* the original input values.
*/
static bool reportHeader(
void* state,
EvidenceKeyValuePair* pair) {

// Copy the key and value into null terminated strings for output.
char* key = (char*)Malloc(pair->item.keyLength + 1);
char* value = (char*)Malloc(pair->item.valueLength + 1);
strncpy(key, pair->item.key, pair->item.keyLength);
strncpy(value, pair->item.value, pair->item.valueLength);
key[pair->item.keyLength] = '\0';
value[pair->item.valueLength] = '\0';

// Output the key and value.
fprintf((FILE*)state, "\n\t%s: %s", key, value);

// Free memory.
Free(key);
Free(value);

// Keep iterating the evidence.
return true;
}

static void analyse(
ResultsHash* results,
EvidenceKeyValuePairArray* evidence,
Expand All @@ -231,13 +273,8 @@ static void analyse(

// list the evidence
fprintf(output, "Input values:");
for (uint32_t i = 0; i < evidence->count; i++) {
EvidenceKeyValuePair e = evidence->items[i];
fprintf(output,
"\n\t%s%s: %s",
EvidencePrefixString(e.prefix), e.field, (char *)e.originalValue);
}
fprintf(output, "\n");
EvidenceIterate(evidence, INT_MAX, output, reportHeader);
fprintf(output, "\n\n");

EXCEPTION_CREATE
ResultsHashFromEvidence(results, evidence, exception);
Expand All @@ -250,6 +287,10 @@ static void analyse(
outputValue(results, "Browser Name", "BrowserName", output);
outputValue(results, "Browser Version", "BrowserVersion", output);

// Shows the Device Id that can be used to look up the properties via
// reference tables.
// Note: Reference tables not shown and are available for on premise
// subscribers using the CSV data format.
HashGetDeviceIdFromResults(
results,
valueBuffer,
Expand All @@ -258,12 +299,41 @@ static void analyse(
EXCEPTION_THROW;
fprintf(output, "\n\tDevice ID: %s\n", valueBuffer);

// Shows how to get all the required properties as a single JSON string.
ResultsHashGetValuesJson(results,
valueBuffer,
sizeof(valueBuffer),
exception);
fprintf(output, "\n\tJSON: %s\n", valueBuffer);

// Shows the JavaScript that can be run in a User Agent Client Hint
// compatible web browsers to return evidence needed for device detection
// as a base64 string. See
// https://51degrees.com/documentation/4.4/_device_detection__features__u_a_c_h__overview.html
outputValue(
results,
"GetHighEntropyValues JS",
"JavascriptGetHighEntropyValues",
output);

fprintf(output, "\n\n");

// Iterate the evidence to show pseudo headers and GHEV results which is
// only going to be exposed after the call to ResultsHashFromEvidence.
if (((DataSetHash*)results->b.b.dataSet)->b.ghevHeaders != NULL) {
fprintf(output, "UACH evidence:");
EvidenceIterateForHeaders(
evidence,
INT_MAX,
((DataSetHash*)results->b.b.dataSet)->b.ghevHeaders,
NULL,
0,
output,
reportHeader);
fprintf(output, "\n\n");
}

fprintf(output, "---###---");
fprintf(output, "\n\n");
}

Expand All @@ -275,12 +345,17 @@ void fiftyoneDegreesHashGettingStarted(
EXCEPTION_CREATE;

// Set the properties to be returned for each User-Agent. Specifying the
// properties that will later be retrieved at initialisation time improves
// performance.
// properties that will later be retrieved or used during device detection
// at initialisation time improves performance.
// Note: The Accept-CH properties are used to prevent the
// JavascriptGetHighEntropyValues value being returned when all the data is
// already present in the evidence.
PropertiesRequired properties = {
NULL,
0,
"IsMobile,PlatformName,PlatformVersion,BrowserName,BrowserVersion,HardwareImages",
"IsMobile,PlatformName,PlatformVersion,BrowserName,BrowserVersion,"
"HardwareImages,SetHeaderBrowserAccept-CH,SetHeaderHardwareAccept-CH,"
"SetHeaderPlatformAccept-CH,JavascriptGetHighEntropyValues",
NULL };

// Initialise the manager for device detection.
Expand All @@ -299,15 +374,19 @@ void fiftyoneDegreesHashGettingStarted(

// Create a results instance to store and process evidence.
// The capacity of the results should be the same as the maximum potential
// evidence that can be provided.
ResultsHash *results = ResultsHashCreate(&manager, 0);

for (int i = 0; i < (int)(sizeof(evidenceValues)/sizeof(evidence *)); i++) {
// Create an evidence collection and add the evidence to the collection
EvidenceKeyValuePairArray* evidenceArray = EvidenceCreate(MAX_EVIDENCE);
evidence *evs = evidenceValues[i];
// evidence that can be provided.
// Note: Capacity of overriding values dynamically is required to ensure
// that the JavascriptGetHighEntropyValues code can be blocked from being
// returned when all the required evidence is already present.
ResultsHash *results = ResultsHashCreate(&manager, 1);

for (int i = 0;
i < (int)sizeof(evidenceValues)/(int)sizeof(evidence*);
i++) {
// Create an evidence collection and add the evidence.
evidence* evs = evidenceValues[i];
EvidenceKeyValuePairArray* evidenceArray = EvidenceCreate(evs->count);
for (uint32_t j = 0; j < evs->count; j++) {
// Add the evidence as string
EvidenceAddString(
evidenceArray,
evs->items[j].prefix,
Expand Down
4 changes: 3 additions & 1 deletion examples/C/Hash/OfflineProcessing.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,9 @@ static void analyse(
EvidenceKeyValuePair e = evidence->items[i];
fprintf(outputFile,
"%s%s: %s\n",
EvidencePrefixString(e.prefix), e.field, (char *)e.originalValue);
EvidencePrefixString(e.prefix),
e.item.key,
e.item.value);
}

EXCEPTION_CREATE
Expand Down
46 changes: 26 additions & 20 deletions examples/C/Hash/Performance.c
Original file line number Diff line number Diff line change
Expand Up @@ -276,38 +276,43 @@ static void storeEvidence(KeyValuePair* pairs, uint16_t size, void* state) {
// Get a shared string for the field name without the prefix and
// use this. Reduces memory consumption as there are only a limited
// number of keys.
evidence->items[i].field = getOrAddSharedString(
evidence->items[i].item.key = getOrAddSharedString(
perfState,
pairs[i].key + prefix->prefixLength);
if (evidence->items[i].field == NULL) {
if (evidence->items[i].item.key == NULL) {
EXCEPTION_THROW
}
evidence->items[i].fieldLength = strlen(evidence->items[i].field);
evidence->items[i].item.keyLength =
strlen(evidence->items[i].item.key);
}
else {
evidence->items[i].prefix = FIFTYONE_DEGREES_EVIDENCE_IGNORE;
evidence->items[i].field = NULL;
evidence->items[i].fieldLength = 0;
evidence->items[i].item.key = NULL;
evidence->items[i].item.keyLength = 0;
}

// If the field is User-Agent or NULL then create new memory for the
// string value, otherwise use shared strings.
if (evidence->items[i].field == NULL ||
strcmp(evidence->items[i].field, userAgent) == 0) {
if (evidence->items[i].item.key == NULL ||
strcmp(evidence->items[i].item.key, userAgent) == 0) {

// Copy the value to new memory at the original value, and then set
// the parsed value to point to the original value. This memory
// will be freed after the test.
evidence->items[i].originalValue = (const char*)Malloc(
sizeof(char) * (pairs[i].valueLength + 1));
evidence->items[i].item.valueLength =
evidence->items[i].parsedLength =
strlen(pairs[i].value);
evidence->items[i].item.value = (const char*)Malloc(
sizeof(char) * (evidence->items[i].item.valueLength + 1));
ptr = strncpy(
(char*)evidence->items[i].originalValue,
(char*)evidence->items[i].item.value,
pairs[i].value,
pairs[i].valueLength);
evidence->items[i].item.valueLength);
if (ptr == NULL) {
EXCEPTION_THROW
}
evidence->items[i].parsedValue = evidence->items[i].originalValue;
*(ptr + evidence->items[i].item.valueLength) = '\0';
evidence->items[i].parsedValue = evidence->items[i].item.value;
}
else {

Expand All @@ -320,12 +325,13 @@ static void storeEvidence(KeyValuePair* pairs, uint16_t size, void* state) {
if (evidence->items[i].parsedValue == NULL) {
EXCEPTION_THROW
}
evidence->items[i].originalValue = NULL;
}

// Set the length of the parsed value.
evidence->items[i].parsedLength = strlen(
(char*)evidence->items[i].parsedValue);
// Set the length of the parsed value.
evidence->items[i].parsedLength = strlen(
(char*)evidence->items[i].parsedValue);

evidence->items[i].item.value = NULL;
}
}
evidence->count = size;

Expand Down Expand Up @@ -396,7 +402,7 @@ void runPerformanceThread(void* state) {
j,
exception) != NULL && EXCEPTION_OKAY) {
value = (String*)results->values.items[0].data.ptr;
if (value != NULL) {
if (results->values.count > 0 && value != NULL) {
// Increase the checksum with the size of the string to
// provide a crude checksum.
thisState->checkSum += value->size;
Expand Down Expand Up @@ -594,8 +600,8 @@ void freeEvidence(performanceState* state) {
while (node != NULL) {
EvidenceKeyValuePairArray* evidence = node->array;
for (uint32_t j = 0; j < evidence->count; j++) {
if (evidence->items[j].originalValue != NULL) {
Free((void*)evidence->items[j].originalValue);
if (evidence->items[j].item.value != NULL) {
Free((void*)evidence->items[j].item.value);
}
}
EvidenceFree(evidence);
Expand Down
Loading

0 comments on commit 1d620b6

Please sign in to comment.