Compare commits

..

6 Commits

Author SHA1 Message Date
318e2319ee ODS parse hyperlink tooltips 2024-11-10 21:27:04 -05:00
6c0f950f83 feat: Add Sheet Protection for XLS (BIFF8) (#3202)
Fixes #3201

Reviewed-on: sheetjs/sheetjs#3202
Co-authored-by: Lucas Picchi <lucas.picchi@exeo.com.ar>
Co-committed-by: Lucas Picchi <lucas.picchi@exeo.com.ar>
2024-10-26 20:37:20 +00:00
235ed7ccfb update CONTRIBUTING.md (fixes #3233 h/t @Akxe) 2024-10-21 09:43:40 -04:00
2d6c821261 Parse DIF-esque CSV (fixes #3230 h/t @lowkeyfish) 2024-10-02 01:04:56 -04:00
36debb0eaa RollupJS workaround (fixes #3219 h/t @lvzhenbo) 2024-09-19 19:45:18 -04:00
df48a059c3 Ignore negative sign for a symmetric rounding. 2024-09-18 17:31:02 -07:00
10 changed files with 127 additions and 36 deletions

@ -7,6 +7,7 @@ changes may not be included if they are not expected to break existing code.
* Sheet Visibility for ODS / FODS (h/t @edemaine)
* HTML DOM ingress support formulae (`data-f`)
* Proper handling of XLSX encoded entities (h/t @inreoh)
* Proper handling of invalid DIF sheets that match heuristics (h/t @lowkeyfish)
## v0.20.3

@ -1,69 +1,74 @@
# Contributing
The SheetJS Libraries should be free and clear to use in your projects. In
order to maintain that, every contributor must be vigilant.
SheetJS CE should be free and clear to use in your projects. To ensure that
remains true, each contributor must be vigilant and each contribution must be
carefully scrutinized from a technical and legal perspective.
There have been many projects in the past that have been very lax regarding
licensing. We are of the opinion that those are ticking timebombs and that no
commercial product should depend on them.
Many commercial products and open source projects have been very lax regarding
licensing. They are ticking timebombs that no commercial product should use.
# Required Reading
## Required Reading
These are pretty short reads and emphasize the importance of proper licensing:
- https://github.com/jazzband/tablib/issues/114 (discussion of other tools)
- https://web.archive.org/web/20200916173942/https://github.com/jazzband/tablib/issues/114
- https://web.archive.org/web/20120615223756/http://www.codinghorror.com/blog/2007/04/pick-a-license-any-license.html
- https://web.archive.org/web/20240909210554/https://github.com/stephen-hardy/xlsx.js/issues/8
# Raising Issues
## Raising Issues
Issues should generally be accompanied by test files. Since github does not
support attachments, the best method is to send files to <sheetjs@gmail.com>
(subject line should contain issue number or message) or to share using some
storage service. Unless expressly permitted, any attachments will not be
shared or included in a test suite (although I will ask :)
Issues should generally be accompanied by test files. It is strongly recommended
to use the [issue tracker](https://git.sheetjs.com/sheetjs/sheetjs/issues).
If sending email to a gmail account is problematic, the <dev@sheetjs.com> email
inbox is self-hosted.
If they cannot be shared publicly, please send files to <oss@sheetjs.com>
(subject line should contain issue number or message) or share links to files
hosted on a storage service. Unless expressly permitted, attachments will not be
shared outside of SheetJS LLC or included in a test suite.
# Opening Pull Requests
If a NDA is required, please send an email to <oss@sheetjs.com> with subject
line "Non-Disclosure Agreemeant Request".
[Squash commits](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History)
before opening a pull request, If the pull request addresses documentation or
demos, add `[ci skip]` in the body or title of the commit message to skip tests.
# Pre-Contribution Checklist
## Opening Pull Requests
Please raise an issue before opening pull requests. It is easy to solve a
specific problem without considering the full context or implications.
## Pre-Contribution Checklist
Before thinking about contributing, make sure that:
- You are not, nor have ever been, an employee of Microsoft Corporation.
- You have not signed any NDAs or Shared Source Agreements with Microsoft
Corporation or a subsidiary
Corporation or a subsidiary.
- You have not consulted any existing relevant codebase (if you have, please
take note of which codebases were consulted).
If you cannot attest to each of these items, the best approach is to raise an
issue. If it is a particularly high-priority issue, please drop an email to
<sheetjs@gmail.com> and it will be prioritized.
issue. If it is a particularly high-priority issue, please drop an email to
<support@sheetjs.com> and it will be prioritized.
# Intra-Contribution
## Intra-Contribution
Keep these in mind as you work:
- Your contributions are your original work. Take note of any resources you
- Your contributions are your original work. Take note of any resources you
consult in the process. Be extra careful not to use unlicensed code on the
Internet or code generated by a large language model or other AI tool.
- You are working on your own time. Unless they explicitly grant permission,
- You are working on your own time. Unless they explicitly grant permission,
your employer may be the ultimate owner of your IP
# Post-Contribution
Before contributions are merged, you will receive an email (at the address
associated with the git commit) and will be asked to confirm the aforementioned
items. Ensure that the email addresses associated with the commits are valid.
## Post-Contribution
Before certain contributions are merged, you will receive an email (at the
address associated with the git commit) and will be asked to confirm the
aforementioned items. Ensure that the email addresses associated with the
commits are valid.

@ -421,7 +421,11 @@ function hashq(str/*:string*/)/*:string*/ {
}
return o;
}
function rnd(val/*:number*/, d/*:number*/)/*:string*/ { var dd = Math.pow(10,d); return ""+(Math.round(val * dd)/dd); }
function rnd(val/*:number*/, d/*:number*/)/*:string*/ {
var sgn = val < 0 ? -1 : 1;
var dd = Math.pow(10,d);
return ""+sgn*(Math.round(sgn * val * dd)/dd);
}
function dec(val/*:number*/, d/*:number*/)/*:number*/ {
var _frac = val - Math.floor(val), dd = Math.pow(10,d);
if (d < ('' + Math.round(_frac * dd)).length) return 0;

@ -153,7 +153,13 @@ function utf8readb(data) {
function utf8readc(data) { return Buffer_from(data, 'binary').toString('utf8'); }
var utf8corpus = "foo bar baz\u00e2\u0098\u0083\u00f0\u009f\u008d\u00a3";
var utf8read = has_buf && (/*#__PURE__*/utf8readc(utf8corpus) == /*#__PURE__*/utf8reada(utf8corpus) && utf8readc || /*#__PURE__*/utf8readb(utf8corpus) == /*#__PURE__*/utf8reada(utf8corpus) && utf8readb) || utf8reada;
var utf8read = /*#__PURE__*/(function() {
if(has_buf) {
if(utf8readc(utf8corpus) == utf8reada(utf8corpus)) return utf8readc;
if(utf8readb(utf8corpus) == utf8reada(utf8corpus)) return utf8readb;
}
return utf8reada;
})();
var utf8write/*:StringConv*/ = has_buf ? function(data) { return Buffer_from(data, 'utf8').toString("binary"); } : function(orig/*:string*/)/*:string*/ {
var out/*:Array<string>*/ = [], i = 0, c = 0, d = 0;

@ -1138,3 +1138,17 @@ function read_wb_ID(d, opts) {
}
}
function read_wb_TABL(d, opts) {
var o = opts || {}, OLD_WTF = !!o.WTF; o.WTF = true;
try {
var out = DIF.to_workbook(d, o);
if(!out || !out.Sheets) throw "DIF bad workbook";
var ws = out.Sheets[out.SheetNames[0]];
if(!ws || !ws["!ref"]) throw "DIF empty worksheet";
o.WTF = OLD_WTF;
return out;
} catch(e) {
o.WTF = OLD_WTF;
return PRN.to_workbook(d, opts);
}
}

@ -431,8 +431,54 @@ function write_FMTS_biff8(ba, NF/*:?SSFTable*/, opts) {
});
}
function write_ws_protect_biff8(sp) {
/* SheetProtection */
var flags = 0x0000;
[
["objects", false, 0x0001], // fObjects - Bit 0 (Edit objects)
["scenarios", false, 0x0002], // fScenarios - Bit 1 (Edit scenarios)
["formatCells", true, 0x0004], // fFormatCells - Bit 2 (Change cell formatting)
["formatColumns", true, 0x0008], // fFormatColumns - Bit 3 (Change column formatting)
["formatRows", true, 0x0010], // fFormatRows - Bit 4 (Change row formatting)
["insertColumns", true, 0x0020], // fInsertColumns - Bit 5 (Insert columns)
["insertRows", true, 0x0040], // fInsertRows - Bit 6 (Insert rows)
["insertHyperlinks", true, 0x0080], // fInsertHyperlinks - Bit Bit 7 (Insert hyperlinks)
["deleteColumns", true, 0x0100], // fDeleteColumns - Bit 8 (Delete columns)
["deleteRows", true, 0x0200], // fDeleteRows - Bit 9 (Delete rows)
["selectLockedCells", false, 0x0400], // fSelLockedCells - Bit 10 (Select locked cells)
["sort", true, 0x0800], // fSort - Bit 11 (Sort a cell range)
["autoFilter", true, 0x1000], // fAutoFilter - Bit 12 (Edit auto filters)
["pivotTables", true, 0x2000], // fPivotTables - Bit 13 (Edit PivotTables)
["selectUnlockedCells", false, 0x4000] // fSelUnlockedCells - Bit 14 (Select unlocked cells)
].forEach(function(n) {
if(n[1]) flags |= sp[n[0]] != null && !sp[n[0]] ? n[2] : 0x0000;
else flags |= sp[n[0]] != null && sp[n[0]] ? 0x0000 : n[2];
});
/* [MS-XLS] 2.4.112 */
var featHdr = new_buf(23);
/* [MS-XLS] 2.5.135 */
featHdr.write_shift(2, 0x0867);
featHdr.write_shift(2, 0x0000);
featHdr.write_shift(4, 0x00000000);
featHdr.write_shift(4, 0x00000000);
/* [MS-XLS] 2.5.237 */
featHdr.write_shift(2, 0x0002); // SharedFeatureType ISFPROTECTION
/* Reserved byte */
featHdr.write_shift(1, 0x01);
/* cbHdrData */
featHdr.write_shift(4, 0xffffffff);
/* [MS-XLS] 2.5.104 */
featHdr.write_shift(4, flags);
return featHdr;
}
function write_FEAT(ba, ws) {
/* [MS-XLS] 2.4.112 */
/* ISFPROTECTION */
if(ws['!protect']) write_biff_rec(ba, 0x0867 /* FeatHdr */, write_ws_protect_biff8(ws['!protect']));
/* ISFFEC2 */
var o = new_buf(19);
o.write_shift(4, 0x867); o.write_shift(4, 0); o.write_shift(4, 0);
o.write_shift(2, 3); o.write_shift(1, 1); o.write_shift(4, 0);
@ -537,6 +583,14 @@ function write_ws_biff8(idx/*:number*/, opts, wb/*:Workbook*/) {
/* Footer (string) */
write_biff_rec(ba, 0x0083 /* HCenter */, writebool(false));
write_biff_rec(ba, 0x0084 /* VCenter */, writebool(false));
/* PROTECTION */
if(ws['!protect']){
var sp = ws['!protect'];
/* [MS-XLS] 2.4.207 */
write_biff_rec(ba, 0x0012 /* Protect */, writeuint16(1));
/* [MS-XLS] 2.4.191 */
if(sp.password) write_biff_rec(ba, 0x0013 /* Password */, writeuint16(crypto_CreatePasswordVerifier_Method1(sp.password)));
}
/* ... */
if(b8) write_ws_cols_biff8(ba, ws["!cols"]);
/* ... */

@ -764,6 +764,10 @@ function parse_content_xml(d/*:string*/, _opts, _nfm)/*:Workbook*/ {
_Ref = ods_to_csf_3D(atag.Target.slice(1));
atag.Target = "#" + _Ref[0] + "!" + _Ref[1];
} else if(atag.Target.match(/^\.\.[\\\/]/)) atag.Target = atag.Target.slice(3);
/* Appendix D.2 Hyperlink Titles */
if(atag.title) {
atag.Tooltip = unescapexml(atag.title); delete atag.title;
}
}
break;

@ -99,7 +99,7 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
if(n[1] === 0x49 && n[2] === 0x2a && n[3] === 0x00) throw new Error("TIFF Image File is not a spreadsheet");
if(n[1] === 0x44) return read_wb_ID(d, o);
break;
case 0x54: if(n[1] === 0x41 && n[2] === 0x42 && n[3] === 0x4C) return DIF.to_workbook(d, o); break;
case 0x54: if(n[1] === 0x41 && n[2] === 0x42 && n[3] === 0x4C) return read_wb_TABL(d, o); break;
case 0x50: return (n[1] === 0x4B && n[2] < 0x09 && n[3] < 0x09) ? read_zip(d, o) : read_prn(data, d, o, str);
case 0xEF: return n[3] === 0x3C ? parse_xlml(d, o) : read_prn(data, d, o, str);
case 0xFF:

@ -111,7 +111,7 @@ function write_binary_type(out, opts/*:WriteOpts*/)/*:any*/ {
function writeSyncXLSX(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
reset_cp();
check_wb(wb);
if(!opts || !opts.unsafe) check_wb(wb);
var o = dup(opts||{});
if(o.cellStyles) { o.cellNF = true; o.sheetStubs = true; }
if(o.type == "array") { o.type = "binary"; var out/*:string*/ = (writeSyncXLSX(wb, o)/*:any*/); o.type = "array"; return s2ab(out); }
@ -120,7 +120,7 @@ function writeSyncXLSX(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
reset_cp();
check_wb(wb);
if(!opts || !opts.unsafe) check_wb(wb);
var o = dup(opts||{});
if(o.cellStyles) { o.cellNF = true; o.sheetStubs = true; }
if(o.type == "array") { o.type = "binary"; var out/*:string*/ = (writeSync(wb, o)/*:any*/); o.type = "array"; return s2ab(out); }

3
types/index.d.ts vendored

@ -317,6 +317,9 @@ export interface WritingOptions extends CommonOptions {
/** Record Separator ("row separator") for CSV / Text output */
RS?: string;
/** Skip certain validity checks (NOTE: generated files may not open in Excel) */
unsafe?: boolean;
}
/** Workbook Object */