-
Notifications
You must be signed in to change notification settings - Fork 3.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature request: Support for asynchronous operations #79
Comments
#81 closed, work to continue on |
Replying to your comments from #81
Completely agree.
In #84 3a90244 I added a polyfill for promises (so we could use PhantomJS: More generally it is probably worth having a larger discussion around:
Given the size and scope of using some of the above tools I think it is worth making a new issue (or using #2 or #5) and discussing it there.
I'm fairly certain bake canceling is doable on the recipe level (i.e. canceling the baking process after an operation has finished) but I'm not sure how one would go about canceling during an operation. One such solution would be to move the baking process into a web worker and then using I agree that we should have a new branch (after this is merged). |
While looking into bugs associated with Conditional Jump + async ops, I noticed that the description for the third argument is "Maximum jumps (if jumping backwards)" but there is no corresponding backwards jump checks in the code. Should line 182 of
be
or is this done elsewhere? If there is no backwards jump check then the following test fails, which seems unexpected.
|
Each jump, whether forward or backward, increments There is a bug causing your test to fail, however it's actually the line which increments |
Thanks for the clarification and the additional bug spot. Is there still need for a check (using the correct variable I have a few tests for "Jump" and "Conditional Jump" so when you merge the test runner and roll out the fix into the feature-async-opsbranch I'll make a PR with those tests. While writing those tests I made the silly mistake (an embarrassing number of times) of using the following operation config:
I indended to run the operation before Fork which is which will cause an infinite loop (it just re-runs the Jump or Conditional Jump operation). I cannot think of a case where the ability to do this is desirable. Is it worth having the following code in Jump and Conditional Jump:
i.e. we default to what the user probably means, alternatively we could throw an error "Do not use -1 for Jump or Conditional Jump". |
I think there is some confusion here on one of our parts. The argument "Maximum jumps (if jumping backwards)" ( For example, you could jump backwards 5 operations, 10 times by setting I don't believe you can enter an infinite loop by just setting I have merged master back into feature-async-ops so the test runner is available on that branch now. |
Please ignore the infinite loop remarks (it was a bug on my end ignoring I think the your solution of "massaging" the negative values such that:
In #86 I have added some tests for Jump and Conditional Jump. Also your test "Fork, Conditional Jump, Encodings" fails for me reproducibly Input
Output
|
I wasn't quite sure whether the test was supposed to be 🔥 or ✔️️ 😄 , thanks for clarifying Just made PR #88 and I wanted to elaborate (in this thread) on my words:
Although changing the check to I think the mental model we should have about As such, the |
I agree with your logic here, but in the interests of "defensive programming", I think it's best to use |
Quick update on the progress for merging this. I'm working on a fairly comprehensive bit of work sorting out the lib dependencies. This involves importing as many libraries as possible through npm and then bundling them all using webpack instead of grunt. This work should also encompass the introduction of a proper structure for dealing with polyfills, which will allow us to use Promises and several other newer JavaScript features in a well defined way. It would still be nice to include a way of cancelling operations while they are running, however that should be handled in a separate branch. I've looked into using web workers in the past but they rely on referencing an external file, which doesn't fit with the compiled model CyberChef uses. The only solution seems to be to create a Blob and load that dynamically into a new worker, which seems a little convoluted and messy. If anyone has any better ideas, I'd love to hear them. Once the new module and packing structure has been introduced we'll look to get this branch integrated. Feel free to add any more tests that you think would be relevant and if there are operations that rely on promises that you want to work on, they can be submitted to this branch as well. |
You are right about the blob method of creating web workers, however I think it can be tidier than you expect. Instead of the current process of concatenating all JS files into one, we concatenate all main window JS into one file and all worker JS into one worker JS file. The worker JS file is has So we could have something like this (relies heavily on this MDN entry):
All of this is quite unnecessary in the short-term though, we can just implement a simple "Stop baking" button that stops baking after the current operation is finished. However I believe this solution will only work for asynchronous tasks. The new dependency bundling system looks to be a great improvement, thanks for taking the time to work on it! Perhaps it is worth creating a branch (and issue) for it to outsource any grunt work (no pun intended) you find yourself too busy to tackle. |
You're right, that doesn't look quite as ugly as I feared. I still wish that there was proper JS support for threads based on objects instead of script files - a rather outdated concept these days. However if this is what's available, it's what we'll have to use. The newly modified structure I'm building should further decouple the core and operations from the HTML view and the browser environment in general. The aim is that the actual Chef part (including all the operations) should be able to run in Node if you so wish, giving us the option to run it on a server and open up a RESTful API to it. We could then allow the user of the web app to specify whether they want their recipe to be run locally or remotely, in case it's process intensive or requires access to a module that cannot be (or is difficult to) run in a browser (shellcode disassembly for example). It may then also be possible to support operations written in different languages like Python or C/C++ etc. There are various hurdles to overcome before we'll be in a position to actually offer this, and it may never be something that we can offer ourselves, but it would be nice to at least have the capability to allow people to set up their own. Once I've got the restructuring to a stage that actually compiles, I'll push it to GitHub and open it up for review. I won't give any timescales yet as this is still something I'm working on in my spare time, but it shouldn't be too far off. |
Being able to have pluggable front-ends and back-end extensions sounds very appealing: Big datasetsFor files above ~100MB CyberChef doesn't seem to tackle very well; a "path/to/file" input with a remote back-end (+ an actual Pluggable import/exportI was also mulling around the idea of being able to get data from more sources than just text/files (e.g. SQL databases, S3/Blob storage, etc), however due to browser limitations it seems this is currently impossible. However with the system you describe this seems easy to envision as possible. Especially with modules ( Don't break "linkability"An often taken for granted value I get from CyberChef is due to the "linkability" of recipes. I can just email/slack a link with the confidence that the recipient will get the same results. I think it would be unwise to take any steps that prevents the continuation of this "feature". However I don't think "linkability" and the new structure are mutually exclusive: I can easily imagine parallel run-times (e.g. CyberChef.html, cyberchefd + browser, and even a CyberChef.app [OSX]). Jupyter notebooks seem to be in a similar vein (though without a browser run-time IIRC). |
Yes, the ability to work with large files is one of the main reasons I want to create an API. Browsers really aren't the best place to work on large datasets, however much we might like it. Building connectors to other services is also an area that I want us to move into. Getting the core and operations set up in a really robust, packaged format is key to this. "Linkability" is one of the most important features of CyberChef. It's very important that we preserve this as much as possible and I'm really glad you've identified it. Perhaps some of the new features won't allow for the input to be included in these links, but the recipe shouldn't be a problem, perhaps also with a parameter to specify where it be run. Don't forget that the recipe can be copied out of the "Save recipe" box as a raw JSON string. This should work across any platform that CyberChef is running on, whether that's Node, a browser, an API, a CLI, an app... |
myOperation: function(input, args) {
return new Promise(function(resolve, reject) {
doAsyncThing()
.then(function(data) {
resolve(data); // Equivalent to `return data`
})
.catch(function(error) {
reject(error); // Equivalent to `throw error`
})
});
}, is really bad Promise manipulation, it can be re-factored to: myOperation: function(input, args) {
return doAsyncThing()
}, new Promise should only be used to convert callback based code to promises: function makeRequest(url) {
function executor(resolve, reject) {
function cb(err, result) {
err ? reject(err) : resolve(result);
}
makeRequestCb(url, cb);
}
return new Promise(executor);
}
...
await makeRequest('http://google.com'); |
also to rebuild all the forking and conditions in an Asyncronous way, it would be a shame not to reuse RxJS Observables: soon to be built into JS |
additionally, now we're using babel we can use async/await: async function myOperation(input, args) {
const v = await someProcess(input);
const w = await process(v);
return v + w;
} and even better, async generators: https://github.com/tc39/proposal-async-iteration |
I agree with you that in a perfect world the above async code is not the most desirable. However, for the (many) cases where CyberChef is just calling existing library code I think it is sometimes the most pragmatic option. In the case where there are either no possible errors to be dealt with or the error messages provided by the library are acceptable in "un-massaged" form then we can just write the following:
Thanks for pointing out the async/await functionality. I think a lot of both the |
It's never pragmatic to convert a promise into another promise with |
I haven't had the most experience working with promises with may explain my ignorance but refactoring code similar to the following does leave me scratching my head:
|
Like this: function processError({ type, message }) {
switch(type) {
case 1: throw 'This is a much more friendly error message for error type 1, you can resolve it by doing X.';
case 2: throw 'This is a much more friendly error message for error type 2, you can resolve it by doing Y.';
default: throw `Unknown error${message}`;
}
}
myOperation: function(input, args) {
return fnThatNeedsMassaging(input)
.then(result => result.thingWeNeed)
.catch(processError);
} |
I can see how the above is a big rookie error, thanks. |
Closed by #117 |
Overview
Right now (as far as I know) operations are limited to synchronous functions, for example
However it would be useful to be able to support promises too, for example
On the surface it looks a lot uglier than the former, but it would allow us to support more operations.
Example
I was working on implementing #6 using OpenPGP.js but the
encrypt
anddecrypt
operations (among others) all use promises, which currently make it impossible to integrate in the current system (AFAIK).Thoughts
I think it can be done without forcing any changes on how operations are configured. I think the main bulk of the changes would happen in the code that runs the recipe, and in how flow control operations currently work.
The text was updated successfully, but these errors were encountered: