@imazhar101/mcp-bigquery-server 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -54,7 +54,7 @@ class BigQueryServer {
|
|
|
54
54
|
isError: true,
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
-
const { rows, rowCount, schema, bytesProcessed, cacheHit, strippedFields } = result.data;
|
|
57
|
+
const { rows, rowCount, schema, bytesProcessed, cacheHit, strippedFields, } = result.data;
|
|
58
58
|
const headers = schema?.map((f) => f.name) ?? Object.keys(rows[0] ?? {});
|
|
59
59
|
const body = args.format === 'json'
|
|
60
60
|
? formatAsJson(rows)
|
|
@@ -26,7 +26,6 @@ export class BigQueryService {
|
|
|
26
26
|
query: sql,
|
|
27
27
|
dryRun: true,
|
|
28
28
|
location: this.config.location,
|
|
29
|
-
defaultDataset: { projectId: billingProject },
|
|
30
29
|
});
|
|
31
30
|
const statementType = dryRunJob.metadata?.statistics?.query?.statementType ?? '';
|
|
32
31
|
if (statementType !== 'SELECT') {
|
|
@@ -41,7 +40,6 @@ export class BigQueryService {
|
|
|
41
40
|
const [job] = await this.client.createQueryJob({
|
|
42
41
|
query: sql,
|
|
43
42
|
location: this.config.location,
|
|
44
|
-
defaultDataset: { projectId: billingProject },
|
|
45
43
|
maximumBytesBilled: String(10 * 1024 * 1024 * 1024), // 10 GB safety cap
|
|
46
44
|
});
|
|
47
45
|
const [rows] = await job.getQueryResults({ maxResults: clampedRows });
|
|
@@ -94,11 +92,23 @@ export class BigQueryService {
|
|
|
94
92
|
if (prevented.length === 0) {
|
|
95
93
|
return { filteredRows: rows, filteredSchema: schema, strippedFields: [] };
|
|
96
94
|
}
|
|
97
|
-
// Case-insensitive match
|
|
98
|
-
|
|
95
|
+
// Case-insensitive substring match: a prevented token strips any column
|
|
96
|
+
// whose name contains it (e.g. `grm_email` strips `grm_email_one`,
|
|
97
|
+
// `grm_email_primary`). Substring is the only semantics that reliably
|
|
98
|
+
// catches real column sprawl; the BIGQUERY_PREVENTED_FIELDS list is curated
|
|
99
|
+
// to avoid over-broad tokens (e.g. `first_name`/`last_name`, never bare
|
|
100
|
+
// `name`). NOTE: output-column filtering is an accident guard, not an
|
|
101
|
+
// access boundary — it is bypassed by aliasing and does not cover PII used
|
|
102
|
+
// only in WHERE/JOIN/aggregates. Use allowedTables + governed views for that.
|
|
103
|
+
const isBlocked = (name) => {
|
|
104
|
+
const n = name.toLowerCase();
|
|
105
|
+
return prevented.some((p) => n.includes(p.toLowerCase()));
|
|
106
|
+
};
|
|
99
107
|
const allFieldNames = schema.length > 0
|
|
100
108
|
? schema.map((f) => f.name)
|
|
101
|
-
: rows.length > 0
|
|
109
|
+
: rows.length > 0
|
|
110
|
+
? Object.keys(rows[0])
|
|
111
|
+
: [];
|
|
102
112
|
const strippedFields = allFieldNames.filter(isBlocked);
|
|
103
113
|
if (strippedFields.length === 0) {
|
|
104
114
|
return { filteredRows: rows, filteredSchema: schema, strippedFields: [] };
|
package/package.json
CHANGED