@apitap/core 1.4.0 → 1.4.1
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.
- package/README.md +2 -2
- package/dist/auth/crypto.d.ts +10 -0
- package/dist/auth/crypto.js +30 -6
- package/dist/auth/crypto.js.map +1 -1
- package/dist/auth/handoff.js +20 -1
- package/dist/auth/handoff.js.map +1 -1
- package/dist/auth/manager.d.ts +1 -0
- package/dist/auth/manager.js +35 -9
- package/dist/auth/manager.js.map +1 -1
- package/dist/capture/monitor.js +4 -0
- package/dist/capture/monitor.js.map +1 -1
- package/dist/capture/scrubber.js +10 -0
- package/dist/capture/scrubber.js.map +1 -1
- package/dist/capture/session.js +7 -17
- package/dist/capture/session.js.map +1 -1
- package/dist/cli.js +74 -17
- package/dist/cli.js.map +1 -1
- package/dist/discovery/fetch.js +3 -3
- package/dist/discovery/fetch.js.map +1 -1
- package/dist/mcp.d.ts +2 -0
- package/dist/mcp.js +59 -33
- package/dist/mcp.js.map +1 -1
- package/dist/native-host.js +2 -2
- package/dist/native-host.js.map +1 -1
- package/dist/orchestration/browse.js +13 -4
- package/dist/orchestration/browse.js.map +1 -1
- package/dist/plugin.d.ts +1 -1
- package/dist/plugin.js +14 -4
- package/dist/plugin.js.map +1 -1
- package/dist/read/decoders/reddit.js +4 -0
- package/dist/read/decoders/reddit.js.map +1 -1
- package/dist/replay/engine.js +60 -17
- package/dist/replay/engine.js.map +1 -1
- package/dist/serve.d.ts +2 -0
- package/dist/serve.js +8 -1
- package/dist/serve.js.map +1 -1
- package/dist/skill/generator.d.ts +5 -0
- package/dist/skill/generator.js +30 -4
- package/dist/skill/generator.js.map +1 -1
- package/dist/skill/search.js +1 -1
- package/dist/skill/search.js.map +1 -1
- package/dist/skill/signing.js +19 -1
- package/dist/skill/signing.js.map +1 -1
- package/dist/skill/ssrf.js +71 -2
- package/dist/skill/ssrf.js.map +1 -1
- package/dist/skill/store.d.ts +2 -0
- package/dist/skill/store.js +23 -10
- package/dist/skill/store.js.map +1 -1
- package/dist/skill/validate.d.ts +10 -0
- package/dist/skill/validate.js +106 -0
- package/dist/skill/validate.js.map +1 -0
- package/package.json +1 -1
- package/src/auth/crypto.ts +14 -6
- package/src/auth/handoff.ts +19 -1
- package/src/auth/manager.ts +22 -5
- package/src/capture/monitor.ts +4 -0
- package/src/capture/scrubber.ts +12 -0
- package/src/capture/session.ts +5 -14
- package/src/cli.ts +71 -11
- package/src/discovery/fetch.ts +2 -2
- package/src/mcp.ts +58 -31
- package/src/orchestration/browse.ts +13 -4
- package/src/plugin.ts +17 -5
- package/src/read/decoders/reddit.ts +3 -3
- package/src/replay/engine.ts +65 -15
- package/src/serve.ts +10 -1
- package/src/skill/generator.ts +32 -4
- package/src/skill/search.ts +1 -1
- package/src/skill/signing.ts +20 -1
- package/src/skill/ssrf.ts +69 -2
- package/src/skill/store.ts +29 -11
- package/src/skill/validate.ts +48 -0
package/dist/skill/ssrf.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssrf.js","sourceRoot":"","sources":["../../src/skill/ssrf.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAW3C,MAAM,kBAAkB,GAAG,CAAC,WAAW,CAAC,CAAC;AACzC,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;AAE/H;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAChD,CAAC;IAED,eAAe;IACf,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1D,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC;IACrE,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAE9B,2BAA2B;IAC3B,IAAI,kBAAkB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,kCAAkC,QAAQ,EAAE,EAAE,CAAC;IAC/E,CAAC;IAED,2BAA2B;IAC3B,KAAK,MAAM,MAAM,IAAI,iBAAiB,EAAE,CAAC;QACvC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,gCAAgC,QAAQ,EAAE,EAAE,CAAC;QAC7E,CAAC;IACH,CAAC;IAED,gBAAgB;IAChB,IAAI,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;QAC/C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAC;IAC9D,CAAC;IAED,gEAAgE;IAChE,MAAM,aAAa,GAAG,QAAQ,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC7F,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,WAAW,CAAC,GAAG,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC7G,CAAC;IAED,8EAA8E;IAC9E,MAAM,gBAAgB,GAAG,QAAQ,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;IAC3F,IAAI,gBAAgB,EAAE,CAAC;QACrB,MAAM,EAAE,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7C,MAAM,EAAE,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;QACjF,OAAO,WAAW,CAAC,GAAG,GAAG,CAAC,QAAQ,KAAK,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjG,CAAC;IAED,8BAA8B;IAC9B,IAAI,wBAAwB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,wCAAwC,QAAQ,EAAE,EAAE,CAAC;IACrF,CAAC;IAED,mDAAmD;IACnD,IAAI,wBAAwB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,0CAA0C,QAAQ,EAAE,EAAE,CAAC;IACvF,CAAC;IAED,
|
|
1
|
+
{"version":3,"file":"ssrf.js","sourceRoot":"","sources":["../../src/skill/ssrf.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAW3C,MAAM,kBAAkB,GAAG,CAAC,WAAW,CAAC,CAAC;AACzC,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;AAE/H;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAChD,CAAC;IAED,eAAe;IACf,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1D,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC;IACrE,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAE9B,2BAA2B;IAC3B,IAAI,kBAAkB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,kCAAkC,QAAQ,EAAE,EAAE,CAAC;IAC/E,CAAC;IAED,2BAA2B;IAC3B,KAAK,MAAM,MAAM,IAAI,iBAAiB,EAAE,CAAC;QACvC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,gCAAgC,QAAQ,EAAE,EAAE,CAAC;QAC7E,CAAC;IACH,CAAC;IAED,gBAAgB;IAChB,IAAI,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;QAC/C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAC;IAC9D,CAAC;IAED,gEAAgE;IAChE,MAAM,aAAa,GAAG,QAAQ,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC7F,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,WAAW,CAAC,GAAG,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC7G,CAAC;IAED,8EAA8E;IAC9E,MAAM,gBAAgB,GAAG,QAAQ,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;IAC3F,IAAI,gBAAgB,EAAE,CAAC;QACrB,MAAM,EAAE,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7C,MAAM,EAAE,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;QACjF,OAAO,WAAW,CAAC,GAAG,GAAG,CAAC,QAAQ,KAAK,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjG,CAAC;IAED,8BAA8B;IAC9B,IAAI,wBAAwB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,wCAAwC,QAAQ,EAAE,EAAE,CAAC;IACrF,CAAC;IAED,mDAAmD;IACnD,IAAI,wBAAwB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,0CAA0C,QAAQ,EAAE,EAAE,CAAC;IACvF,CAAC;IAED,uFAAuF;IACvF,MAAM,YAAY,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,YAAY,IAAI,QAAQ,CAAC;IAE3C,gFAAgF;IAChF,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClF,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAEzB,wBAAwB;QACxB,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,oCAAoC,QAAQ,EAAE,EAAE,CAAC;QACjF,CAAC;QACD,uBAAuB;QACvB,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YAClB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,iCAAiC,QAAQ,EAAE,EAAE,CAAC;QAC9E,CAAC;QACD,qBAAqB;QACrB,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YACjB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,2BAA2B,QAAQ,EAAE,EAAE,CAAC;QACxE,CAAC;QACD,0BAA0B;QAC1B,IAAI,KAAK,KAAK,GAAG,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC;YAClD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,2BAA2B,QAAQ,EAAE,EAAE,CAAC;QACxE,CAAC;QACD,wBAAwB;QACxB,IAAI,KAAK,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACpC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,2BAA2B,QAAQ,EAAE,EAAE,CAAC;QACxE,CAAC;QACD,2BAA2B;QAC3B,IAAI,KAAK,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACpC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,mCAAmC,QAAQ,EAAE,EAAE,CAAC;QAChF,CAAC;QACD,4DAA4D;QAC5D,IAAI,KAAK,KAAK,GAAG,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;YACnD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,8BAA8B,QAAQ,EAAE,EAAE,CAAC;QAC3E,CAAC;QACD,sDAAsD;QACtD,IAAI,KAAK,KAAK,GAAG,IAAI,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YAChE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,sCAAsC,QAAQ,EAAE,EAAE,CAAC;QACnF,CAAC;QACD,0CAA0C;QAC1C,IAAI,KAAK,KAAK,GAAG,IAAI,CAAC,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK,EAAE,CAAC,EAAE,CAAC;YACtD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,qCAAqC,QAAQ,EAAE,EAAE,CAAC;QAClF,CAAC;QACD,oCAAoC;QACpC,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;YACjB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,iCAAiC,QAAQ,EAAE,EAAE,CAAC;QAC9E,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC;AAED;;;;GAIG;AACH,SAAS,aAAa,CAAC,QAAgB;IACrC,kCAAkC;IAClC,IAAI,sCAAsC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1D,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,qDAAqD;IACrD,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACnC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,UAAU,EAAE,CAAC;YAClC,OAAO,GAAG,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,EAAE,CAAC;QAC7F,CAAC;IACH,CAAC;IAED,6DAA6D;IAC7D,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,GAAW,CAAC;YAChB,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChC,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC3B,CAAC;iBAAM,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClC,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC1B,CAAC;iBAAM,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC3B,CAAC;iBAAM,CAAC;gBACN,OAAO,IAAI,CAAC,CAAC,YAAY;YAC3B,CAAC;YACD,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,GAAG;gBAAE,OAAO,IAAI,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,EAAU;IAC7B,gBAAgB;IAChB,IAAI,EAAE,KAAK,KAAK;QAAE,OAAO,eAAe,CAAC;IAEzC,8BAA8B;IAC9B,IAAI,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,iBAAiB,CAAC;IAE7D,mDAAmD;IACnD,IAAI,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,mBAAmB,CAAC;IAE/D,2CAA2C;IAC3C,MAAM,QAAQ,GAAG,EAAE,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEzC,iDAAiD;IACjD,MAAM,WAAW,GAAG,EAAE,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC1E,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,EAAE,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACxC,MAAM,EAAE,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACxC,MAAM,aAAa,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;QAC1F,OAAO,WAAW,CAAC,aAAa,CAAC,CAAC;IACpC,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;IACzE,IAAI,CAAC,KAAK;QAAE,OAAO,wBAAwB,CAAC,CAAC,uCAAuC;IAEpF,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC;IACvB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACxB,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAEzB,IAAI,KAAK,KAAK,GAAG;QAAE,OAAO,UAAU,CAAC;IACrC,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,gBAAgB,CAAC;IAC1C,IAAI,KAAK,KAAK,GAAG,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE;QAAE,OAAO,uBAAuB,CAAC;IAClF,IAAI,KAAK,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,qBAAqB,CAAC;IAClE,IAAI,KAAK,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,YAAY,CAAC;IACzD,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,aAAa,CAAC;IACtC,kCAAkC;IAClC,IAAI,KAAK,KAAK,GAAG,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,IAAI,GAAG;QAAE,OAAO,mBAAmB,CAAC;IAC/E,IAAI,KAAK,KAAK,GAAG,IAAI,CAAC,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK,EAAE,CAAC;QAAE,OAAO,0BAA0B,CAAC;IACzF,IAAI,KAAK,IAAI,GAAG;QAAE,OAAO,kBAAkB,CAAC;IAE5C,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,SAAiB;IAC3D,2CAA2C;IAC3C,MAAM,UAAU,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,CAAC,UAAU,CAAC,IAAI;QAAE,OAAO,UAAU,CAAC;IAExC,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAChD,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAE9B,mEAAmE;IACnE,IAAI,QAAQ,CAAC,KAAK,CAAC,sCAAsC,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACvF,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxB,CAAC;IAED,sCAAsC;IACtC,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,QAAQ,gBAAgB,OAAO,KAAK,aAAa,GAAG,EAAE,CAAC;QACzG,CAAC;QAED,kEAAkE;QAClE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;QACrC,SAAS,CAAC,QAAQ,GAAG,OAAO,CAAC;QAC7B,OAAO;YACL,IAAI,EAAE,IAAI;YACV,WAAW,EAAE,SAAS,CAAC,QAAQ,EAAE;YACjC,UAAU,EAAE,OAAO;YACnB,YAAY,EAAE,QAAQ;SACvB,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;QACjD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,6BAA6B,QAAQ,EAAE,EAAE,CAAC;IAC1E,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,+BAA+B,CAAC,KAAgB;IACpE,MAAM,UAAU,GAAG,MAAM,qBAAqB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9D,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QACrB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;IAClE,CAAC;IAED,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC;QACxC,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,KAAK,CAAC,CAAC;YAClD,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,EAAE,KAAK,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;YACxE,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAgB;IACpD,gBAAgB;IAChB,MAAM,UAAU,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QACrB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;IAClE,CAAC;IAED,8BAA8B;IAC9B,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC;QACxC,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;YAClC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,EAAE,KAAK,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;YACxE,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC"}
|
package/dist/skill/store.d.ts
CHANGED
|
@@ -3,5 +3,7 @@ export declare function writeSkillFile(skill: SkillFile, skillsDir?: string): Pr
|
|
|
3
3
|
export declare function readSkillFile(domain: string, skillsDir?: string, options?: {
|
|
4
4
|
verifySignature?: boolean;
|
|
5
5
|
signingKey?: Buffer;
|
|
6
|
+
/** Allow loading unsigned files without throwing. Tampered signed files still reject. */
|
|
7
|
+
trustUnsigned?: boolean;
|
|
6
8
|
}): Promise<SkillFile | null>;
|
|
7
9
|
export declare function listSkillFiles(skillsDir?: string): Promise<SkillSummary[]>;
|
package/dist/skill/store.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { readFile, writeFile, mkdir, readdir, access } from 'node:fs/promises';
|
|
3
3
|
import { join, dirname } from 'node:path';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
|
+
import { validateSkillFile } from './validate.js';
|
|
5
6
|
const DEFAULT_SKILLS_DIR = join(homedir(), '.apitap', 'skills');
|
|
6
7
|
const BASE_GITIGNORE = `# ApiTap — prevent accidental credential commits
|
|
7
8
|
auth.enc
|
|
@@ -27,7 +28,7 @@ async function ensureGitignore(skillsDir) {
|
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
export async function writeSkillFile(skill, skillsDir = DEFAULT_SKILLS_DIR) {
|
|
30
|
-
await mkdir(skillsDir, { recursive: true });
|
|
31
|
+
await mkdir(skillsDir, { recursive: true, mode: 0o700 });
|
|
31
32
|
await ensureGitignore(skillsDir);
|
|
32
33
|
const filePath = skillPath(skill.domain, skillsDir);
|
|
33
34
|
await writeFile(filePath, JSON.stringify(skill, null, 2) + '\n', { mode: 0o600 });
|
|
@@ -38,20 +39,32 @@ export async function readSkillFile(domain, skillsDir = DEFAULT_SKILLS_DIR, opti
|
|
|
38
39
|
const path = skillPath(domain, skillsDir);
|
|
39
40
|
try {
|
|
40
41
|
const content = await readFile(path, 'utf-8');
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const raw = JSON.parse(content);
|
|
43
|
+
const skill = validateSkillFile(raw);
|
|
44
|
+
// Signature verification is ON by default (H1 fix)
|
|
45
|
+
const shouldVerify = options?.verifySignature !== false;
|
|
46
|
+
if (shouldVerify) {
|
|
47
|
+
// Auto-derive signing key if not provided
|
|
48
|
+
let signingKey = options?.signingKey;
|
|
49
|
+
if (!signingKey) {
|
|
50
|
+
const { deriveSigningKey } = await import('../auth/crypto.js');
|
|
51
|
+
const { getMachineId } = await import('../auth/manager.js');
|
|
52
|
+
const machineId = await getMachineId();
|
|
53
|
+
signingKey = deriveSigningKey(machineId);
|
|
54
|
+
}
|
|
44
55
|
if (skill.provenance === 'imported') {
|
|
45
|
-
// Imported files had foreign signature stripped — can't verify
|
|
46
|
-
// Future: re-sign on import with local key
|
|
56
|
+
// Imported files had foreign signature stripped — can't verify
|
|
47
57
|
}
|
|
48
58
|
else if (!skill.signature) {
|
|
49
|
-
//
|
|
50
|
-
|
|
59
|
+
// Unsigned files are rejected unless trustUnsigned is set
|
|
60
|
+
if (!options?.trustUnsigned) {
|
|
61
|
+
throw new Error(`Skill file for ${domain} is unsigned and cannot be verified. ` +
|
|
62
|
+
`Re-capture or re-import the skill file, or use --trust-unsigned to load it.`);
|
|
63
|
+
}
|
|
51
64
|
}
|
|
52
65
|
else {
|
|
53
66
|
const { verifySignature } = await import('./signing.js');
|
|
54
|
-
if (!verifySignature(skill,
|
|
67
|
+
if (!verifySignature(skill, signingKey)) {
|
|
55
68
|
throw new Error(`Skill file signature verification failed for ${domain} — file may be tampered`);
|
|
56
69
|
}
|
|
57
70
|
}
|
|
@@ -80,7 +93,7 @@ export async function listSkillFiles(skillsDir = DEFAULT_SKILLS_DIR) {
|
|
|
80
93
|
const domain = file.replace(/\.json$/, '');
|
|
81
94
|
if (!DOMAIN_RE.test(domain))
|
|
82
95
|
continue; // skip non-conforming filenames
|
|
83
|
-
const skill = await readSkillFile(domain, skillsDir);
|
|
96
|
+
const skill = await readSkillFile(domain, skillsDir, { trustUnsigned: true });
|
|
84
97
|
if (skill) {
|
|
85
98
|
summaries.push({
|
|
86
99
|
domain: skill.domain,
|
package/dist/skill/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/skill/store.ts"],"names":[],"mappings":"AAAA,qBAAqB;AACrB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/skill/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SkillFile } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validate a parsed JSON object as a SkillFile.
|
|
4
|
+
* Throws on invalid input — fail fast, fail loud.
|
|
5
|
+
* SSRF checks are optional here (default: off) because the replay engine
|
|
6
|
+
* already enforces SSRF at request time with DNS resolution.
|
|
7
|
+
*/
|
|
8
|
+
export declare function validateSkillFile(raw: unknown, options?: {
|
|
9
|
+
checkSsrf?: boolean;
|
|
10
|
+
}): SkillFile;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { validateUrl } from './ssrf.js';
|
|
2
|
+
const ALLOWED_METHODS = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']);
|
|
3
|
+
/**
|
|
4
|
+
* Validate a parsed JSON object as a SkillFile.
|
|
5
|
+
* Throws on invalid input — fail fast, fail loud.
|
|
6
|
+
* SSRF checks are optional here (default: off) because the replay engine
|
|
7
|
+
* already enforces SSRF at request time with DNS resolution.
|
|
8
|
+
*/
|
|
9
|
+
export function validateSkillFile(raw, options) {
|
|
10
|
+
if (!raw || typeof raw !== 'object') {
|
|
11
|
+
throw new Error('Skill file must be an object');
|
|
12
|
+
}
|
|
13
|
+
const obj = raw;
|
|
14
|
+
// domain
|
|
15
|
+
if (typeof obj.domain !== 'string' || obj.domain.length === 0 || obj.domain.length > 253) {
|
|
16
|
+
throw new Error('Invalid domain: must be a string of 1-253 characters');
|
|
17
|
+
}
|
|
18
|
+
// baseUrl — must be a valid URL; SSRF checked at replay time
|
|
19
|
+
if (typeof obj.baseUrl !== 'string') {
|
|
20
|
+
throw new Error('Missing baseUrl');
|
|
21
|
+
}
|
|
22
|
+
let baseUrlHostname;
|
|
23
|
+
try {
|
|
24
|
+
const url = new URL(obj.baseUrl);
|
|
25
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
26
|
+
throw new Error('non-HTTP scheme');
|
|
27
|
+
}
|
|
28
|
+
baseUrlHostname = url.hostname;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
throw new Error(`Invalid baseUrl: must be a valid HTTP(S) URL`);
|
|
32
|
+
}
|
|
33
|
+
// Domain-lock: baseUrl hostname must match or be a subdomain of domain (C1 fix)
|
|
34
|
+
const domainStr = obj.domain;
|
|
35
|
+
if (baseUrlHostname !== domainStr && !baseUrlHostname.endsWith('.' + domainStr)) {
|
|
36
|
+
throw new Error(`baseUrl hostname "${baseUrlHostname}" does not match domain "${domainStr}". ` +
|
|
37
|
+
`Skill files cannot redirect requests to unrelated hosts.`);
|
|
38
|
+
}
|
|
39
|
+
if (options?.checkSsrf) {
|
|
40
|
+
const ssrf = validateUrl(obj.baseUrl);
|
|
41
|
+
if (!ssrf.safe) {
|
|
42
|
+
throw new Error(`Unsafe baseUrl: ${ssrf.reason}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// endpoints
|
|
46
|
+
if (!Array.isArray(obj.endpoints)) {
|
|
47
|
+
throw new Error('Missing or invalid endpoints array');
|
|
48
|
+
}
|
|
49
|
+
if (obj.endpoints.length > 500) {
|
|
50
|
+
throw new Error('Too many endpoints (max 500)');
|
|
51
|
+
}
|
|
52
|
+
for (let i = 0; i < obj.endpoints.length; i++) {
|
|
53
|
+
const ep = obj.endpoints[i];
|
|
54
|
+
if (!ep || typeof ep !== 'object') {
|
|
55
|
+
throw new Error(`Endpoint ${i}: must be an object`);
|
|
56
|
+
}
|
|
57
|
+
const e = ep;
|
|
58
|
+
if (typeof e.id !== 'string' || e.id.length === 0 || e.id.length > 200) {
|
|
59
|
+
throw new Error(`Endpoint ${i}: id must be a string of 1-200 characters`);
|
|
60
|
+
}
|
|
61
|
+
if (typeof e.method !== 'string' || !ALLOWED_METHODS.has(e.method)) {
|
|
62
|
+
throw new Error(`Endpoint ${i}: method must be one of ${[...ALLOWED_METHODS].join(', ')}`);
|
|
63
|
+
}
|
|
64
|
+
if (typeof e.path !== 'string' || !e.path.startsWith('/')) {
|
|
65
|
+
throw new Error(`Endpoint ${i}: path must start with /`);
|
|
66
|
+
}
|
|
67
|
+
if (e.path.length > 2000) {
|
|
68
|
+
throw new Error(`Endpoint ${i}: path exceeds 2000 characters`);
|
|
69
|
+
}
|
|
70
|
+
// M11: Deep type validation on nested structures
|
|
71
|
+
if ('headers' in e && e.headers !== undefined) {
|
|
72
|
+
if (typeof e.headers !== 'object' || e.headers === null || Array.isArray(e.headers)) {
|
|
73
|
+
throw new Error(`Endpoint ${i}: headers must be an object`);
|
|
74
|
+
}
|
|
75
|
+
for (const [hk, hv] of Object.entries(e.headers)) {
|
|
76
|
+
if (typeof hv !== 'string') {
|
|
77
|
+
throw new Error(`Endpoint ${i}: header "${hk}" value must be a string`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if ('queryParams' in e && e.queryParams !== undefined) {
|
|
82
|
+
if (typeof e.queryParams !== 'object' || e.queryParams === null || Array.isArray(e.queryParams)) {
|
|
83
|
+
throw new Error(`Endpoint ${i}: queryParams must be an object`);
|
|
84
|
+
}
|
|
85
|
+
for (const [qk, qv] of Object.entries(e.queryParams)) {
|
|
86
|
+
if (typeof qv !== 'object' || qv === null || typeof qv.example !== 'string') {
|
|
87
|
+
throw new Error(`Endpoint ${i}: queryParam "${qk}" must have a string example`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if ('requestBody' in e && e.requestBody !== undefined) {
|
|
92
|
+
const rb = e.requestBody;
|
|
93
|
+
if (typeof rb !== 'object' || rb === null) {
|
|
94
|
+
throw new Error(`Endpoint ${i}: requestBody must be an object`);
|
|
95
|
+
}
|
|
96
|
+
if (typeof rb.contentType !== 'string') {
|
|
97
|
+
throw new Error(`Endpoint ${i}: requestBody.contentType must be a string`);
|
|
98
|
+
}
|
|
99
|
+
if (rb.variables !== undefined && !Array.isArray(rb.variables)) {
|
|
100
|
+
throw new Error(`Endpoint ${i}: requestBody.variables must be an array`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return raw;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/skill/validate.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apitap/core",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Intercept web API traffic during browsing. Generate portable skill files so AI agents can call APIs directly instead of scraping.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
package/src/auth/crypto.ts
CHANGED
|
@@ -24,9 +24,14 @@ function getInstallSalt(saltFile?: string): string {
|
|
|
24
24
|
// Generate and save new salt
|
|
25
25
|
const salt = randomBytes(32).toString('hex');
|
|
26
26
|
try {
|
|
27
|
-
mkdirSync(saltPath.replace(/\/[^/]+$/, ''), { recursive: true });
|
|
27
|
+
mkdirSync(saltPath.replace(/\/[^/]+$/, ''), { recursive: true, mode: 0o700 });
|
|
28
28
|
writeFileSync(saltPath, salt, { mode: 0o600 });
|
|
29
|
-
} catch {
|
|
29
|
+
} catch (err: any) {
|
|
30
|
+
// M2 fix: only swallow EEXIST (dir already exists), throw on permission errors etc.
|
|
31
|
+
if (err?.code !== 'EEXIST') {
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
30
35
|
return salt;
|
|
31
36
|
}
|
|
32
37
|
|
|
@@ -44,12 +49,15 @@ export interface EncryptedData {
|
|
|
44
49
|
* being stretched through 100K iterations.
|
|
45
50
|
*/
|
|
46
51
|
export function deriveKey(machineId: string, saltFile?: string): Buffer {
|
|
47
|
-
// Try per-install salt first, fallback to old constant
|
|
52
|
+
// Try per-install salt first, fallback to old constant only on ENOENT (M2 fix)
|
|
48
53
|
try {
|
|
49
54
|
return pbkdf2Sync(machineId, getInstallSalt(saltFile), PBKDF2_ITERATIONS, KEY_LENGTH, 'sha512');
|
|
50
|
-
} catch {
|
|
51
|
-
//
|
|
52
|
-
|
|
55
|
+
} catch (err: any) {
|
|
56
|
+
// Only fall back to legacy salt on file-not-found (ENOENT) — not on permission errors, corrupt files, etc.
|
|
57
|
+
if (err?.code === 'ENOENT') {
|
|
58
|
+
return pbkdf2Sync(machineId, 'apitap-v0.2-key-derivation', PBKDF2_ITERATIONS, KEY_LENGTH, 'sha512');
|
|
59
|
+
}
|
|
60
|
+
throw err;
|
|
53
61
|
}
|
|
54
62
|
}
|
|
55
63
|
|
package/src/auth/handoff.ts
CHANGED
|
@@ -198,8 +198,20 @@ async function doHandoff(
|
|
|
198
198
|
let detectedOAuth: OAuthTokenDetection | undefined;
|
|
199
199
|
let latestCookies: Array<{ name: string; value: string; domain: string; path: string; expires: number; httpOnly: boolean; secure: boolean; sameSite: 'Strict' | 'Lax' | 'None' }> = [];
|
|
200
200
|
|
|
201
|
+
// M13 fix: Only capture auth from requests targeting the handoff domain
|
|
202
|
+
const targetDomain = domain;
|
|
203
|
+
|
|
201
204
|
// Watch network responses for auth signals (bearer tokens, API keys, OAuth tokens)
|
|
202
205
|
page.on('response', async (response) => {
|
|
206
|
+
// M13: Filter by request origin — only capture auth from target domain
|
|
207
|
+
let reqHost: string;
|
|
208
|
+
try {
|
|
209
|
+
reqHost = new URL(response.request().url()).hostname;
|
|
210
|
+
} catch { return; }
|
|
211
|
+
if (reqHost !== targetDomain && !reqHost.endsWith('.' + targetDomain)) {
|
|
212
|
+
return; // Skip cross-origin requests
|
|
213
|
+
}
|
|
214
|
+
|
|
203
215
|
const reqHeaders = response.request().headers();
|
|
204
216
|
|
|
205
217
|
// Detect auth from request headers
|
|
@@ -279,7 +291,13 @@ async function doHandoff(
|
|
|
279
291
|
// Browser disconnected — use last snapshot from interval
|
|
280
292
|
}
|
|
281
293
|
|
|
282
|
-
|
|
294
|
+
// M4 fix: Filter cookies to only the target domain and its subdomains
|
|
295
|
+
const cookies = latestCookies.filter(c => {
|
|
296
|
+
const cookieDomain = (c.domain || '').replace(/^\./, ''); // Remove leading dot
|
|
297
|
+
return cookieDomain === domain ||
|
|
298
|
+
cookieDomain.endsWith('.' + domain) ||
|
|
299
|
+
domain.endsWith('.' + cookieDomain);
|
|
300
|
+
});
|
|
283
301
|
|
|
284
302
|
if (cookies.length === 0 && !authDetected) {
|
|
285
303
|
return {
|
package/src/auth/manager.ts
CHANGED
|
@@ -157,7 +157,7 @@ export class AuthManager {
|
|
|
157
157
|
|
|
158
158
|
private async saveAll(data: Record<string, StoredAuth>): Promise<void> {
|
|
159
159
|
const dir = join(this.authPath, '..');
|
|
160
|
-
await mkdir(dir, { recursive: true });
|
|
160
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
161
161
|
|
|
162
162
|
const plaintext = JSON.stringify(data);
|
|
163
163
|
const encrypted = encrypt(plaintext, this.key);
|
|
@@ -201,13 +201,30 @@ export function getParentDomains(domain: string): string[] {
|
|
|
201
201
|
* Fallback: hostname + homedir (less secure but portable)
|
|
202
202
|
*/
|
|
203
203
|
export async function getMachineId(): Promise<string> {
|
|
204
|
+
// Allow override for testing
|
|
205
|
+
if (process.env.APITAP_MACHINE_ID) {
|
|
206
|
+
return process.env.APITAP_MACHINE_ID;
|
|
207
|
+
}
|
|
208
|
+
|
|
204
209
|
try {
|
|
205
210
|
const id = await readFile('/etc/machine-id', 'utf-8');
|
|
206
211
|
return id.trim();
|
|
207
212
|
} catch {
|
|
208
|
-
//
|
|
209
|
-
const {
|
|
210
|
-
const
|
|
211
|
-
|
|
213
|
+
// M1 fix: generate a random per-install ID instead of hostname+homedir
|
|
214
|
+
const { homedir: hd } = await import('node:os');
|
|
215
|
+
const installIdPath = join(hd(), '.apitap', 'install-id');
|
|
216
|
+
try {
|
|
217
|
+
const existing = await readFile(installIdPath, 'utf-8');
|
|
218
|
+
return existing.trim();
|
|
219
|
+
} catch {
|
|
220
|
+
// Generate and save new random ID
|
|
221
|
+
const { randomBytes } = await import('node:crypto');
|
|
222
|
+
const newId = randomBytes(32).toString('hex');
|
|
223
|
+
try {
|
|
224
|
+
await mkdir(join(hd(), '.apitap'), { recursive: true, mode: 0o700 });
|
|
225
|
+
await writeFile(installIdPath, newId, { mode: 0o600 });
|
|
226
|
+
} catch { /* best effort save — use the generated ID anyway */ }
|
|
227
|
+
return newId;
|
|
228
|
+
}
|
|
212
229
|
}
|
|
213
230
|
}
|
package/src/capture/monitor.ts
CHANGED
|
@@ -34,6 +34,10 @@ export interface CaptureResult {
|
|
|
34
34
|
domBytes?: number; // v1.0: measured DOM size for browser cost comparison
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
// SECURITY NOTE (M9): CDP connections use unauthenticated HTTP on localhost.
|
|
38
|
+
// Any local process can connect to the same port. This is a known limitation of
|
|
39
|
+
// Chrome DevTools Protocol. Prefer Playwright's built-in launch over connecting
|
|
40
|
+
// to an existing browser when possible. Never expose CDP ports on non-loopback interfaces.
|
|
37
41
|
const DEFAULT_CDP_PORTS = [18792, 18800, 9222];
|
|
38
42
|
const APITAP_DIR = process.env.APITAP_DIR || join(homedir(), '.apitap');
|
|
39
43
|
|
package/src/capture/scrubber.ts
CHANGED
|
@@ -22,6 +22,12 @@ const SSN_RE = /\b\d{3}-\d{2}-\d{4}\b/g;
|
|
|
22
22
|
const BEARER_TOKEN_RE = /\bBearer\s+[A-Za-z0-9._~+/-]+=*\b/g;
|
|
23
23
|
const JWT_RE = /\beyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\b/g;
|
|
24
24
|
|
|
25
|
+
// M12: Cloud provider keys and private key material
|
|
26
|
+
const AWS_ACCESS_KEY_RE = /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g;
|
|
27
|
+
const GCP_API_KEY_RE = /\bAIza[A-Za-z0-9_-]{35}\b/g;
|
|
28
|
+
const PRIVATE_KEY_RE = /-----BEGIN[\s]+(?:RSA\s+|EC\s+|DSA\s+)?PRIVATE\s+KEY-----[\s\S]*?-----END[\s]+(?:RSA\s+|EC\s+|DSA\s+)?PRIVATE\s+KEY-----/g;
|
|
29
|
+
const BASIC_AUTH_RE = /\bBasic\s+[A-Za-z0-9+/]{8,}={0,2}\b/g;
|
|
30
|
+
|
|
25
31
|
/**
|
|
26
32
|
* Scrub PII from a string. Returns the string with PII replaced by placeholders.
|
|
27
33
|
* Order matters: SSN before phone (SSN is more specific).
|
|
@@ -53,5 +59,11 @@ export function scrubPII(input: string): string {
|
|
|
53
59
|
result = result.replace(BEARER_TOKEN_RE, '[token]');
|
|
54
60
|
result = result.replace(JWT_RE, '[token]');
|
|
55
61
|
|
|
62
|
+
// M12: Cloud provider keys and private key material
|
|
63
|
+
result = result.replace(PRIVATE_KEY_RE, '[private-key]');
|
|
64
|
+
result = result.replace(AWS_ACCESS_KEY_RE, '[aws-key]');
|
|
65
|
+
result = result.replace(GCP_API_KEY_RE, '[gcp-key]');
|
|
66
|
+
result = result.replace(BASIC_AUTH_RE, '[token]');
|
|
67
|
+
|
|
56
68
|
return result;
|
|
57
69
|
}
|
package/src/capture/session.ts
CHANGED
|
@@ -133,20 +133,11 @@ export class CaptureSession {
|
|
|
133
133
|
case 'navigate': {
|
|
134
134
|
if (!action.url) return { success: false, error: 'url required for navigate', snapshot: await this.takeSnapshot() };
|
|
135
135
|
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// Block non-HTTP schemes (file://, ftp://, etc.)
|
|
143
|
-
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
144
|
-
return { success: false, error: `Blocked scheme: ${parsed.protocol}`, snapshot: await this.takeSnapshot() };
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Block cloud metadata endpoint specifically (high-value target)
|
|
148
|
-
if (parsed.hostname === '169.254.169.254') {
|
|
149
|
-
return { success: false, error: 'Navigation blocked: cloud metadata endpoint', snapshot: await this.takeSnapshot() };
|
|
136
|
+
// M7 fix: Full SSRF validation on navigate URLs (same checks as ssrf.ts)
|
|
137
|
+
const { validateUrl: validateNavUrl } = await import('../skill/ssrf.js');
|
|
138
|
+
const navResult = validateNavUrl(action.url);
|
|
139
|
+
if (!navResult.safe) {
|
|
140
|
+
return { success: false, error: `Navigation blocked: ${navResult.reason}`, snapshot: await this.takeSnapshot() };
|
|
150
141
|
}
|
|
151
142
|
|
|
152
143
|
await this.page.goto(action.url, { waitUntil: 'domcontentloaded' });
|
package/src/cli.ts
CHANGED
|
@@ -158,6 +158,15 @@ async function handleCapture(positional: string[], flags: Record<string, string
|
|
|
158
158
|
const skipVerify = flags['no-verify'] === true;
|
|
159
159
|
const verifyPosts = flags['verify-posts'] === true;
|
|
160
160
|
|
|
161
|
+
// SSRF validation for CLI (H6 fix)
|
|
162
|
+
if (flags['danger-disable-ssrf'] !== true) {
|
|
163
|
+
const ssrfCheck = await resolveAndValidateUrl(fullUrl);
|
|
164
|
+
if (!ssrfCheck.safe) {
|
|
165
|
+
console.error(`Error: URL blocked (SSRF): ${ssrfCheck.reason}`);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
161
170
|
if (!json) {
|
|
162
171
|
const domainOnly = flags['all-domains'] !== true;
|
|
163
172
|
console.log(`\n 🔍 Capturing ${url}...${duration ? ` (${duration}s)` : ' (Ctrl+C to stop)'}${domainOnly ? ' [domain-only]' : ' [all domains]'}\n`);
|
|
@@ -341,7 +350,7 @@ async function handleShow(positional: string[], flags: Record<string, string | b
|
|
|
341
350
|
process.exit(1);
|
|
342
351
|
}
|
|
343
352
|
|
|
344
|
-
const skill = await readSkillFile(domain, SKILLS_DIR);
|
|
353
|
+
const skill = await readSkillFile(domain, SKILLS_DIR, { trustUnsigned: true });
|
|
345
354
|
if (!skill) {
|
|
346
355
|
console.error(`Error: No skill file found for "${domain}". Run \`apitap capture\` first.`);
|
|
347
356
|
process.exit(1);
|
|
@@ -380,7 +389,8 @@ async function handleReplay(positional: string[], flags: Record<string, string |
|
|
|
380
389
|
|
|
381
390
|
const machineId = await getMachineId();
|
|
382
391
|
const signingKey = deriveSigningKey(machineId);
|
|
383
|
-
const
|
|
392
|
+
const trustUnsigned = flags['trust-unsigned'] === true;
|
|
393
|
+
const skill = await readSkillFile(domain, SKILLS_DIR, { verifySignature: true, signingKey, trustUnsigned });
|
|
384
394
|
if (!skill) {
|
|
385
395
|
console.error(`Error: No skill file found for "${domain}".`);
|
|
386
396
|
process.exit(1);
|
|
@@ -417,6 +427,10 @@ async function handleReplay(positional: string[], flags: Record<string, string |
|
|
|
417
427
|
const fresh = flags.fresh === true;
|
|
418
428
|
const json = flags.json === true;
|
|
419
429
|
const maxBytes = typeof flags['max-bytes'] === 'string' ? parseInt(flags['max-bytes'], 10) : undefined;
|
|
430
|
+
const dangerDisableSsrf = flags['danger-disable-ssrf'] === true;
|
|
431
|
+
if (dangerDisableSsrf) {
|
|
432
|
+
console.error('[apitap] WARNING: SSRF protection is disabled via --danger-disable-ssrf');
|
|
433
|
+
}
|
|
420
434
|
|
|
421
435
|
const result = await replayEndpoint(skill, endpointId, {
|
|
422
436
|
params: Object.keys(params).length > 0 ? params : undefined,
|
|
@@ -424,7 +438,7 @@ async function handleReplay(positional: string[], flags: Record<string, string |
|
|
|
424
438
|
domain,
|
|
425
439
|
fresh,
|
|
426
440
|
maxBytes,
|
|
427
|
-
_skipSsrfCheck:
|
|
441
|
+
_skipSsrfCheck: dangerDisableSsrf,
|
|
428
442
|
});
|
|
429
443
|
|
|
430
444
|
if (json) {
|
|
@@ -493,7 +507,8 @@ async function handleRefresh(positional: string[], flags: Record<string, string
|
|
|
493
507
|
process.exit(1);
|
|
494
508
|
}
|
|
495
509
|
|
|
496
|
-
const
|
|
510
|
+
const trustUnsigned = flags['trust-unsigned'] === true;
|
|
511
|
+
const skill = await readSkillFile(domain, SKILLS_DIR, { trustUnsigned });
|
|
497
512
|
if (!skill) {
|
|
498
513
|
console.error(`Error: No skill file found for "${domain}".`);
|
|
499
514
|
process.exit(1);
|
|
@@ -597,8 +612,8 @@ async function handleAuth(positional: string[], flags: Record<string, string | b
|
|
|
597
612
|
}
|
|
598
613
|
}
|
|
599
614
|
|
|
600
|
-
// Read skill file for OAuth config (non-secret)
|
|
601
|
-
const skill = await readSkillFile(domain, SKILLS_DIR);
|
|
615
|
+
// Read skill file for OAuth config (non-secret) — trustUnsigned for display only
|
|
616
|
+
const skill = await readSkillFile(domain, SKILLS_DIR, { trustUnsigned: true });
|
|
602
617
|
const oauthConfig = skill?.auth?.oauthConfig;
|
|
603
618
|
|
|
604
619
|
const status = {
|
|
@@ -660,7 +675,7 @@ async function handleServe(positional: string[], flags: Record<string, string |
|
|
|
660
675
|
});
|
|
661
676
|
|
|
662
677
|
// Print tool list to stderr (stdout is the MCP transport)
|
|
663
|
-
const skill = await readSkillFile(domain, SKILLS_DIR);
|
|
678
|
+
const skill = await readSkillFile(domain, SKILLS_DIR, { trustUnsigned: true });
|
|
664
679
|
const tools = buildServeTools(skill!);
|
|
665
680
|
|
|
666
681
|
if (json) {
|
|
@@ -682,10 +697,13 @@ async function handleServe(positional: string[], flags: Record<string, string |
|
|
|
682
697
|
}
|
|
683
698
|
}
|
|
684
699
|
|
|
685
|
-
async function handleMcp(): Promise<void> {
|
|
700
|
+
async function handleMcp(flags: Record<string, string | boolean>): Promise<void> {
|
|
701
|
+
if (flags['danger-disable-ssrf'] === true) {
|
|
702
|
+
console.error('[apitap] WARNING: SSRF protection is disabled via --danger-disable-ssrf');
|
|
703
|
+
}
|
|
686
704
|
const server = createMcpServer({
|
|
687
705
|
skillsDir: SKILLS_DIR,
|
|
688
|
-
_skipSsrfCheck:
|
|
706
|
+
_skipSsrfCheck: flags['danger-disable-ssrf'] === true,
|
|
689
707
|
});
|
|
690
708
|
const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
|
|
691
709
|
const transport = new StdioServerTransport();
|
|
@@ -710,6 +728,16 @@ async function handleInspect(positional: string[], flags: Record<string, string
|
|
|
710
728
|
process.exit(1);
|
|
711
729
|
}
|
|
712
730
|
|
|
731
|
+
// SSRF validation for CLI (H6 fix)
|
|
732
|
+
const fullInspectUrl = url.startsWith('http') ? url : `https://${url}`;
|
|
733
|
+
if (flags['danger-disable-ssrf'] !== true) {
|
|
734
|
+
const ssrfCheck = await resolveAndValidateUrl(fullInspectUrl);
|
|
735
|
+
if (!ssrfCheck.safe) {
|
|
736
|
+
console.error(`Error: URL blocked (SSRF): ${ssrfCheck.reason}`);
|
|
737
|
+
process.exit(1);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
713
741
|
const json = flags.json === true;
|
|
714
742
|
const duration = typeof flags.duration === 'string' ? parseInt(flags.duration, 10) : 30;
|
|
715
743
|
|
|
@@ -799,6 +827,20 @@ async function handleDiscover(positional: string[], flags: Record<string, string
|
|
|
799
827
|
const json = flags.json === true;
|
|
800
828
|
const save = flags.save === true;
|
|
801
829
|
|
|
830
|
+
// SSRF validation for CLI (H6 fix)
|
|
831
|
+
const fullDiscoverUrl = url.startsWith('http') ? url : `https://${url}`;
|
|
832
|
+
if (flags['danger-disable-ssrf'] !== true) {
|
|
833
|
+
const ssrfCheck = await resolveAndValidateUrl(fullDiscoverUrl);
|
|
834
|
+
if (!ssrfCheck.safe) {
|
|
835
|
+
if (json) {
|
|
836
|
+
console.log(JSON.stringify({ error: `URL blocked (SSRF): ${ssrfCheck.reason}` }));
|
|
837
|
+
} else {
|
|
838
|
+
console.error(`Error: URL blocked (SSRF): ${ssrfCheck.reason}`);
|
|
839
|
+
}
|
|
840
|
+
process.exit(1);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
802
844
|
if (!json) {
|
|
803
845
|
console.log(`\n Discovering APIs for ${url}...\n`);
|
|
804
846
|
}
|
|
@@ -902,7 +944,7 @@ async function handleBrowse(positional: string[], flags: Record<string, string |
|
|
|
902
944
|
skillsDir: SKILLS_DIR,
|
|
903
945
|
cache: new SessionCache(),
|
|
904
946
|
maxBytes,
|
|
905
|
-
_skipSsrfCheck:
|
|
947
|
+
_skipSsrfCheck: flags['danger-disable-ssrf'] === true,
|
|
906
948
|
});
|
|
907
949
|
|
|
908
950
|
if (json) {
|
|
@@ -933,6 +975,15 @@ async function handlePeek(positional: string[], flags: Record<string, string | b
|
|
|
933
975
|
const json = flags.json === true;
|
|
934
976
|
const fullUrl = url.startsWith('http') ? url : `https://${url}`;
|
|
935
977
|
|
|
978
|
+
// SSRF validation for CLI (H6 fix)
|
|
979
|
+
if (flags['danger-disable-ssrf'] !== true) {
|
|
980
|
+
const ssrfCheck = await resolveAndValidateUrl(fullUrl);
|
|
981
|
+
if (!ssrfCheck.safe) {
|
|
982
|
+
console.error(`Error: URL blocked (SSRF): ${ssrfCheck.reason}`);
|
|
983
|
+
process.exit(1);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
936
987
|
if (!json) {
|
|
937
988
|
console.log(`\n Peeking at ${url}...\n`);
|
|
938
989
|
}
|
|
@@ -964,6 +1015,15 @@ async function handleRead(positional: string[], flags: Record<string, string | b
|
|
|
964
1015
|
const fullUrl = url.startsWith('http') ? url : `https://${url}`;
|
|
965
1016
|
const maxBytes = typeof flags['max-bytes'] === 'string' ? parseInt(flags['max-bytes'], 10) : undefined;
|
|
966
1017
|
|
|
1018
|
+
// SSRF validation for CLI (H6 fix)
|
|
1019
|
+
if (flags['danger-disable-ssrf'] !== true) {
|
|
1020
|
+
const ssrfCheck = await resolveAndValidateUrl(fullUrl);
|
|
1021
|
+
if (!ssrfCheck.safe) {
|
|
1022
|
+
console.error(`Error: URL blocked (SSRF): ${ssrfCheck.reason}`);
|
|
1023
|
+
process.exit(1);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
967
1027
|
if (!json) {
|
|
968
1028
|
console.log(`\n Reading ${url}...\n`);
|
|
969
1029
|
}
|
|
@@ -1073,7 +1133,7 @@ async function main(): Promise<void> {
|
|
|
1073
1133
|
await handleServe(positional, flags);
|
|
1074
1134
|
break;
|
|
1075
1135
|
case 'mcp':
|
|
1076
|
-
await handleMcp();
|
|
1136
|
+
await handleMcp(flags);
|
|
1077
1137
|
break;
|
|
1078
1138
|
case 'inspect':
|
|
1079
1139
|
await handleInspect(positional, flags);
|