// npm package
@antv/s2
effective spreadsheet render core lib
weekly
7,690
monthly
21,225
versions
336
maintainers
51
license
MIT
first publish
2021-06-02
publisher
GitHub Actions
tarball
15,480,694 B
AUTO-PUBLISHED·1 version indexed·latest published 2026-05-18
// exfil path
what is read → where it shipssteals
- ○ clipboard
sends to
(no destination string extracted — payload may be dynamic / obfuscated)
evidence in excerpt
> "homepage": "https://s2.antv.antgroup.com", > "url": "https://github.com/antvis/S2/issues" > "url": "https://github.com/antvis/S2.git" > "author": "https://github.com/orgs/antvis/people", > * https://github.com/antvis/g
// publisher campaignby GitHub Actions
5 caught packages from this accountThis is not an isolated catch. The same publisher has shipped 4 other packages that our pipeline flagged — the shape of a coordinated campaign, not a one-off. Each link below opens that sibling's analysis.
// offending code· @2.7.1· 3 files flagged
llm: benign · 0.85→ No suspicious destination, no remote-exec shape — 1 known-vendor host(s), 1 other host(s).
- @2.7.1··AUTO-PUBLISHED·publisher: GitHub Actionsheuristic 75/100static flags 3llm benign (0.85) via ollamapopularity:highmature-packageosv-flagged:MAL-2026-4077public-github-pushclipboard-accessreads-env-vars
→ No suspicious destination, no remote-exec shape — 1 known-vendor host(s), 1 other host(s).
// offending code· 3 files flaggedpatterns: 3
--- package/package.json (excerpt) --- { "name": "@antv/s2", "version": "2.7.1", "private": false, "description": "effective spreadsheet render core lib", "keywords": [ "antv", "s2", "S2", "s2-core", "spreadsheet", "pivot table", "table" ], "homepage": "https://s2.antv.antgroup.com", "bugs": { "url": "https://github.com/antvis/S2/issues" }, "repository": { "type": "git", "url": "https://github.com/antvis/S2.git" }, "license": "MIT", "author": "https://github.com/orgs/antvis/people", "sideEffects": [ "*.css", "dist/*" ], "exports": { ".": { "import": "./esm/index.js", "require": "./lib/index.js" }, "./extends": { "import": "./esm/extends/index.js", "require": "./lib/extends/index.js" }, "./*": "./*" }, "main": "lib/index.js", "unpkg": "dist/s2.min.js", "module": "esm/index.js", "types": "esm/index.d.ts", "directories": { "lib": "lib", "test": "tests" }, "files": [ "esm", "lib", "dist", "README.md" ], "scripts": { "build": "npm-run-all clean --parallel build:cjs build:esm build:umd", "build:analysis": "cross-env FORMAT=esm ANALYSIS=true rollup -c rollup.config.mjs", "build:cjs": "tsc --module commonjs --outDir lib -p tsconfig.build.json && pnpm build:sync-assets lib/", "build:esm": "tsc --module ESNext --outDir esm -p tsconfig.build.json && pnpm build:sync-assets esm/", "build:size-limit": "size-limit", "build:size-limit-json": --- package/lib/utils/export/utils.js (excerpt) --- "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.download = exports.copyToClipboard = exports.copyToClipboardByClipboard = exports.copyToClipboardByExecCommand = void 0; const lodash_1 = require("lodash"); const export_1 = require("../../common/interface/export"); const ssr_1 = require("../ssr"); /** * 同步复制 */ const copyToClipboardByExecCommand = (data) => new Promise((resolve, reject) => { let content; if (Array.isArray(data)) { content = (0, lodash_1.get)(data.filter((item) => item.type === export_1.CopyMIMEType.PLAIN), '[0].content', ''); } else { content = data.content || ''; } const textarea = document.createElement('textarea'); textarea.value = content; document.body.appendChild(textarea); // 开启 preventScroll, 防止页面有滚动条时触发滚动 textarea.focus({ preventScroll: true }); textarea.select(); const success = document.execCommand('copy'); document.body.removeChild(textarea); if (success) { resolve(); } else { reject(); } }); exports.copyToClipboardByExecCommand = copyToClipboardByExecCommand; /** * 异步复制 */ const copyToClipboardByClipboard = (data) => navigator.clipboard .write([ new ClipboardItem((0, lodash_1.concat)(data).reduce((prev, copyable) => { const { type, content } = copyable; // eslint-disable-next-line no-control-regex const contentToCopy = content.replace(/\x00/g, ''); return Object.assign(O --- package/lib/utils/export/copy/table-copy.js (excerpt) --- "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.processSelectedTableByDataCell = exports.asyncProcessSelectedAllTable = exports.processSelectedTableByHeader = void 0; const tslib_1 = require("tslib"); const lodash_1 = require("lodash"); const common_1 = require("../../../common"); const method_1 = require("../method"); const base_data_cell_copy_1 = require("./base-data-cell-copy"); const common_2 = require("./common"); class TableDataCellCopy extends base_data_cell_copy_1.BaseDataCellCopy { constructor(params) { super(params); this.getValueFromMeta = (meta) => { var _a; const [, colNode] = (0, common_2.getHeaderNodeFromMeta)(meta, this.spreadsheet); const field = (0, method_1.getColNodeFieldFromNode)(this.spreadsheet.isPivotMode, colNode); const value = this.isSeriesNumberField(field) ? meta.rowIndex + 1 : (_a = this.displayData[meta.rowIndex]) === null || _a === void 0 ? void 0 : _a[field]; const formatter = this.getFormatter({ field, rowIndex: meta.rowIndex, colIndex: meta.colIndex, }); return formatter(value); }; this.displayData = this.getSelectedDisplayData(); this.columnNodes = this.getSelectedColNodes(); } getHeaderNodeMatrix(node) { // 明细表的表头配置即作为列头, 也作为数值, 所以列头不应该被格式化 return super.getHeaderNodeMatrix(no --- bundled output (OSV-MAL flagged — LLM scope expansion) --- --- lib/utils/data-item-type-checker.js (bundled) --- "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isMultiDataItem = void 0; const lodash_1 = require("lodash"); const isMultiDataItem = (value) => (0, lodash_1.isObject)(value) && 'values' in value; exports.isMultiDataItem = isMultiDataItem; //# sourceMappingURL=data-item-type-checker.js.map --- lib/utils/data-set-operate.js (bundled) --- "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getAggregationAndCalcFuncByQuery = exports.sortByItems = exports.customFlattenDeep = exports.filterOutDetail = exports.getListBySorted = void 0; const lodash_1 = require("lodash"); const field_1 = require("../common/constant/field"); const getListBySorted = (list, sorted, mapValue) => { return list.sort((a, b) => { if (mapValue) { a = mapValue(a); b = mapValue(b); } const ia = sorted.indexOf(a); const ib = sorted.indexOf(b); if (ia === -1 && ib === -1) { return 0; } if (ia === -1) { return 1; } if (ib === -1) { return -1; } return ia - ib; }); }; exports.getListBySorted = getListBySorted; const filterOutDetail = (values = []) => { return values.filter((v) => v !== field_1.TOTAL_VALUE && v !== field_1.EMPTY_EXTRA_FIELD_PLACEHOLDER); }; exports.filterOutDetail = filterOutDetail; const customFlattenDeep = (data) => { if (!(0, lodash_1.isArray)(data)) { return [data]; } return (0, lodash_1.flattenDeep)(data); }; exports.customFlattenDeep = customFlattenDeep; /** * arr1包含arr2,将arr2排到最后 * */ const sortByItems = (arr1, arr2) => { var _a; return (_a = arr1 === null || arr1 === void 0 ? void 0 : arr1.filter((item) => !(arr2 === null || arr2 === void 0 ? void 0 : arr2.includes(item)))) === null || _a === void 0 ? void 0 : _a.concat(arr2); }; exports.sortByItems = sortByItems; function getAggregationAndCalcFuncByQuery(totalsStatus, totalsOptions) { const { isRowGrandTotal, isRowSubTotal, isColGrandTotal, isColSubTotal } = totalsStatus; const { row, col } = totalsOptions || {}; const { calcGrandTotals: rowCalcTotals = {}, calcSubTotals: rowCalcSubTotals = {}, } = row || {}; const { calcGrandTotals: colCalcTotals = {}, calcSubTotals: colCalcSubTotals = {}, } = col || {}; const getCalcTota --- lib/utils/g-mini-charts.js (bundled) --- "use strict"; /** * 基于 g 绘制简单 mini 图工具库 * https://github.com/antvis/g */ Object.defineProperty(exports, "__esModule", { value: true }); exports.renderMiniChart = exports.drawBullet = exports.drawInterval = exports.transformRatioToPercent = exports.getBulletRangeColor = exports.drawBar = exports.drawLine = exports.filterValidChartData = exports.scale = void 0; const lodash_1 = require("lodash"); const constant_1 = require("../common/constant"); const interface_1 = require("../common/interface"); const condition_1 = require("../utils/condition/condition"); const formatter_1 = require("../utils/formatter"); const g_renders_1 = require("../utils/g-renders"); const g_utils_1 = require("../utils/g-utils"); /** * 坐标转换 */ const scale = (chartData, cell) => { var _a; const { data, encode, type } = chartData; const { x, y, height, width } = cell.getMeta(); const dataCellStyle = cell.getStyle(constant_1.CellType.DATA_CELL); const { cell: cellStyle, miniChart } = dataCellStyle; const measures = []; const encodedData = (0, lodash_1.map)(data, (item) => { measures.push(item === null || item === void 0 ? void 0 : item[encode.y]); return { x: item[encode.x], y: item[encode.y], }; }); const maxMeasure = (0, lodash_1.max)(measures) || 0; const minMeasure = (0, lodash_1.min)(measures) || 0; let measureRange = maxMeasure - minMeasure; const { left = 0, right = 0, top = 0, bottom = 0 } = cellStyle.padding; const xStart = x + left; const xEnd = x + width - right; const yStart = y + top; const yEnd = y + height - bottom; const heightRange = yEnd - yStart; const intervalPadding = (_a = miniChart === null || miniChart === void 0 ? void 0 : miniChart.bar) === null || _a === void 0 ? void 0 : _a.intervalPadding; let intervalX; if (type === constant_1.MiniChartType.Bar) { intervalX = (xEnd - xStart - (measures.length - 1) * intervalPadding) / --- lib/utils/g-renders.js (bundled) --- "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createOrUpdateRect = exports.batchSetStyle = exports.renderTreeIcon = exports.renderIcon = exports.updateShapeAttr = exports.renderLine = exports.renderText = exports.renderCircle = exports.renderPolyline = exports.renderPolygon = exports.renderRect = void 0; /** * Utils to render all g supported shape * https://github.com/antvis/g */ const g_1 = require("@antv/g"); const lodash_1 = require("lodash"); const gui_icon_1 = require("../common/icons/gui-icon"); const CustomText_1 = require("../engine/CustomText"); function renderRect(group, style) { return group === null || group === void 0 ? void 0 : group.appendChild(new g_1.Rect({ style, })); } exports.renderRect = renderRect; function renderPolygon(group, style) { return group === null || group === void 0 ? void 0 : group.appendChild(new g_1.Polygon({ style })); } exports.renderPolygon = renderPolygon; function renderPolyline(group, style) { return group === null || group === void 0 ? void 0 : group.appendChild(new g_1.Polyline({ style, })); } exports.renderPolyline = renderPolyline; function renderCircle(group, style) { return group === null || group === void 0 ? void 0 : group.appendChild(new g_1.Circle({ style, })); } exports.renderCircle = renderCircle; /** * @description 如果在单元格内绘制, 是使用 cell.renderTextShape(options) */ function renderText(options) { const { group, textShape, style, appendInfo } = options; if (textShape && group) { if (group.contains(textShape)) { group.removeChild(textShape); } } return group === null || group === void 0 ? void 0 : group.appendChild(new CustomText_1.CustomText({ style: Object.assign({ /** * 补充 g5.0 内部 measureText 时的必要参数(variant|fontStyle|lineWidth) * 否则创建完 Text 后,实例 getBBox 返回为全 0 * @see https://github.com/antvis/GUI/blob/302ae68d93dbb567 --- lib/utils/get-all-children-node-height.js (bundled) --- "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getAllChildrenNodeHeight = void 0; /** * 获取当前node所有children的总高度 * @param node */ const getAllChildrenNodeHeight = (node) => { let nodeAllCellHeight = 0; const nodes = node.children; nodes === null || nodes === void 0 ? void 0 : nodes.forEach((item) => { nodeAllCellHeight += item.height || 0; }); return nodeAllCellHeight; }; exports.getAllChildrenNodeHeight = getAllChildrenNodeHeight; //# sourceMappingURL=get-all-children-node-height.js.map --- lib/utils/get-classnames.js (bundled) --- "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getClassNameWithPrefix = void 0; const classnames_1 = require("../common/constant/classnames"); const getClassNameWithPrefix = (...classNames) => `${classnames_1.S2_PREFIX_CLS}-${classNames.join('-')}`; exports.getClassNameWithPrefix = getClassNameWithPrefix; //# sourceMappingURL=get-classnames.js.map --- lib/utils/hide-columns.js (bundled) --- "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getHiddenColumnContinuousSiblingNodes = exports.isEqualDisplaySiblingNodeId = exports.getValidDisplaySiblingNodeId = exports.getValidDisplaySiblingNode = exports.isLastColumnAfterHidden = exports.hideColumnsByThunkGroup = exports.getColumns = exports.hideColumns = exports.getSameHiddenGroupIndex = expor
