"This operation is insecure" when using class properties as key - dictionary

I have a custom FileArgument class that I'm using to store information about an uploaded file:
export class FileArgument {
name: string;
path: string;
file: File;
}
My upload works fine and the server then returns the path where the file was uploaded. I then want to store this path in a dictionary using a previously set fileArgument.name as key. Below is a simplified overview of my component. onSubmit() is where the action is happening:
export class InputModuleComponent {
private vsmodule: InputModule;
private moduleArguments = {};
private fileArgument: FileArgument = new FileArgument();
#Input()
module: InputModule;
constructor(private inputModuleService: InputModuleService) {}
onSubmit(): void {
this.inputModuleService.postFile(this.fileArgument.file).subscribe(
response => {
this.moduleArguments[this.fileArgument.name] = response.filename;
console.log(this.moduleArguments);
},
error => {}
);
}
onFileChange(event): void {
this.fileArgument.file = event.originalTarget.files[0];
this.fileArgument.name = event.originalTarget.id;
}
}
Line 14 above (this.moduleArguments[this.fileArgument.name] = response.filename;) causes the following error in Firefox:
EXCEPTION: Uncaught (in promise): SecurityError: The operation is insecure.
and in Chrome:
core.umd.js:5995 EXCEPTION: Uncaught (in promise): InvalidStateError: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.
If I replace that line with, for example:
this.moduleArguments['hello'] = response.filename;
I don't get any errors. The error clearly comes from using fileArgument.name as a dict key, but I have no idea why.
EDIT: The postFile() method from my service is below:
postFile (file: File): Observable<any> {
console.log('input-module.service - postFile()');
var url = this.uploadURL;
return Observable.create(observer => {
var formData: FormData = new FormData()
var xhr: XMLHttpRequest = new XMLHttpRequest();
formData.append("upload", file, file.name);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
observer.next(JSON.parse(xhr.response));
observer.complete();
} else {
observer.error(xhr.response);
}
}
};
xhr.open('POST', url, true);
xhr.send(formData);
});
}
The component HTML:
<a (click)="modal.open()">
{{vsmodule.displayName}}
</a>
<modal #modal>
<form (ngSubmit)="onSubmit()">
<modal-header [show-close]="true">
<h4 class="modal-title">Input Module - {{vsmodule.displayName}}</h4>
</modal-header>
<modal-body>
<p>{{vsmodule.description}}</p>
<hr>
<ul>
<li *ngFor="let arg of vsmodule.args; let i = index">
<fieldset *ngIf="arg.type == 'file'">
<label>{{ arg.displayName }}</label>
<input
name="{{arg.name}}"
id="{{ arg.name }}"
type="file"
[(ngModel)]="moduleArguments[arg.name]"
(change)="onFileChange($event)"
>
<p>{{ arg.description }}<p>
</fieldset>
</li>
</ul>
</modal-body>
<modal-footer>
<button type="button" class="btn btn-default" data-dismiss="modal" (click)="modal.dismiss()">Dismiss</button>
<button type="submit" class="btn btn-primary">Run</button>
</modal-footer>
</form>
</modal>

In onChange, fileArgument.name is set to the value of event.originalTarget.id - the id of an actual HTML element in the page
And chrome error is saying:
Failed to set the 'value' property on 'HTMLInputElement'
Edit since you added the html - you have bound the 'moduleArguements' property to the file input element's ngmodel - as a result, changing that value will cause angular to try and modify the value property on the file input which is not permitted.
What is the purpose of updating that value? Is it just to feedback to the user?
If you remove the ngModel binding from the input element it should work - you are using the onFileChange event to capture the new filename anyway (although in the controller it is just onChange?)

Short Answer: You cannot cannot actually change the value of the this.moduleArguments[this.fileArgument.name]as it would be a security issue.
Explaination: You would be changing the actual value of this.fileArgument.name to something else, which would allow other people with ill intent to do the same. Essentially, an attacker could change that name to redirect any attempts to use that file to another file. So, Java (or Flash, or any other programming language) could not programmatically change that due to security reasons.
With your work-around, you are not actually setting a File data member, therefore JS does not see this as a security hazard.
Remember, [almost] anything you can do involving website/server code (or code interacting with either), an attacker can do as well. That's why JS, in this case, blocks people from changing the content of this specific standard File object. Hope this helps!

have you tried this:
var fileName = this.fileArgument.name;
this.moduleArguments[fileName] = response.filename;
and also if you are somewhere in JS changing the 'value' of your
tag, you will get that error, please refer to:
https://stackoverflow.com/a/29721020/2071008

Related

Angular Reactive form asynchronous operation

I am getting this error when I try to build a reactive form for creating a new password form.I have mentioned the source code below and when I remove the source code part then there is no error but without that my operation is not working as well. I think I have to add or delete something in my source code to get the desired output
main.ts:12 TypeError: Cannot read property 'value' of null
at FormGroup.passwordShouldMatch [as validator] (password.validators.ts:18)
at FormGroup._runValidator (forms.js:4089)
at FormGroup.updateValueAndValidity (forms.js:4050)
at new FormGroup (forms.js:4927)
at FormBuilder.group (forms.js:8924)
at new ChangePasswordComponent (change-password.component.ts:15)
at createClass (core.js:31987)
at createDirectiveInstance (core.js:31807)
at createViewNodes (core.js:44210)
at callViewAction (core.js:44660)
static passwordShouldMatch(control : AbstractControl) {
let newPassword = control.get('newPassowrd');
let confirmPassword = control.get('confirmPassowrd');
if (newPassword.value !== confirmPassword.value){
return { passwordShouldMatch:true };
return null;
}
}
As you didn't add any code snippet I am considering your form structure is something like this.
this.fb.group({
newPassowrd: [''],
confirmPassowrd: [''],
});
here, you include an custom validation function called passwordShouldMatch and this function looks fine. So, I assume that you did something wrong when you set the validator to that form group.
this.fb.group({
newPassowrd: [''],
confirmPassowrd: [''],
}, { validator: this.passwordShouldMatch});
this is how you should set the validation function for the form group. And in html your form should be something like this.
<form [formGroup]="form" novalidate (ngSubmit)="onSubmit(survey)">
<input type="text" placeholder="Untitled form" formControlName="newPassowrd">
<input type="text" placeholder="Untitled form" formControlName="confirmPassowrd">
<span *ngIf="form.hasError('passwordShouldMatch')">not match</span>
</form>
everything should work this way. Here is the working version of stackblitz

"Uncaught TypeError: Cannot read property 'push' of null" with Polymer 3 and polymerfire3

I'm working in a project with Polymer 3 and polymerfire3.
Right now I have been able to use firebase-auth element successfully. But now that I'm trying to use the firebase-query element I get this on the console Uncaught TypeError: Cannot read property 'push' of null
This is my code
import { PolymerElement, html } from '#polymer/polymer/polymer-element.js';
import './shared-styles.js';
import 'polymerfire3/firebase-auth.js';
import 'polymerfire3/firebase-query.js';
class PerfilView extends PolymerElement {
static get properties() {
return {
user: Object,
uid: String,
data: {
type: Object,
observer: 'dataChanged'
}
};
}
static get template() {
return html`
<firebase-auth
id="auth" user="{{user}}">
</firebase-auth>
<firebase-query
log
id="query"
app-name="morse"
path="/notes/"
data="{{data}}">
</firebase-query>
<div class="card">
<div id="notes">
<ul id="notes-list">
<template is="dom-repeat" items="{{data}}" as="note">
<li>
<p class="content">{{note}}</p>
</li>
</template>
</ul>
<paper-input value="{{inputP::input}}" label="Take a note.."></paper-input>
<div id="notes-controls">
<paper-button id="add" on-tap="add">Add</paper-button>
</div>
</div>
</div>
`;
}
add() {
this.$.query.ref.push({
content: this.inputP
});
}
}
window.customElements.define('perfil-view', PerfilView);
Does it have something to do with the polymerfire3 elements?
You will need to add polymer-document in order to add a record. Additional to your code, something like:
<firebase-document
id="document"
app-name="morse"
path="/notes/[[key]]"
data="{{edata}}">
</firebase-document>
and pushing new data may look like ;
add() {
var key = firebase.database.ref('notes').push().key;
this.set('edata', this.inputP);
this.set('key', key);
// this new Note will be syncronised upon new key and edata properties defined. Here firebase-document will keep sysnronised with edata and path.
}
firebase-query element is basically utilized for -
combining the given properties into query options that generate a
query, a request for a filtered, ordered, immutable set of Firebase
data
Here is further to read up on.
I think, what you're trying to do, can simply be achieved as follows -
add(noteData) {
let newNoteRef = firebase.app().database().ref('notes').push();
return newNoteRef.set(noteData).then(() => {
console.log(`a new note successfully added.`);
});
}
Upon successful data upsert firebase-query will automatically update data bound-variable to reflect the new list.

vue.js reference div id on v-on:click

Using v-on:click I'd like to set a variable with the id of the div in Vue.JS - how do I reference this?
<div id="foo" v-on:click="select">...</div>
<script>
new Vue({
el: '#app',
data: {
},
methods: {
select: function(){
divID = this.id // ??
alert(divID)
}
}
})
</script>
You can extend your event handler with the event object $event. That should fit your needs:
<div id="foo" v-on:click="select($event)">...</div>
The event is passed on in javascript:
export default {
methods: {
select: function(event) {
targetId = event.currentTarget.id;
console.log(targetId); // returns 'foo'
}
}
}
As mentioned in the comments, `$event` is not strictly necessary, when using it as the only parameter. It's a nice reminder that this property is passed on, when writing it explicitly.
However, nobody will stop you from writing the short notation:
<div id="foo" #click="select">...</div>
Beware that the method will not receive the `$event` object when you add another parameter. You need to explicitly add it at the position you will handle it in the listener. Any parameter order will work:
<div id="foo" #click="select(bar, $event)">...</div>
To find more options of the v-on directive, you can look through the corresponding entry in the vue documentation:
Vue API Documentation - v-on
Inspired by #nirazul's answer, to retrieve data attributes:
HTML:
<ul>
<li
v-for="style in styles"
:key="style.id"
#click="doFilter"
data-filter-section="data_1"
:data-filter-size="style.size"
>
{{style.name}}
</li>
</ul>
JS:
export default {
methods: {
doFilter(e) {
let curTarget = e.currentTarget;
let curTargetData = curTarget.dataset;
if (curTargetData) {
console.log("Section: " + curTargetData.filterSection);
console.log("Size: " + curTargetData.filterSize);
}
}
}
}
Just to highlight another option than the selected answer for the same question, I have a delete button on a record and want to perform an action with the record's unique id (a number). I could do the selected answer as before:
<button :id="record.id" #click="del">×</button>
This leaves the unfortunate reality that my del function needs to pull the id attribute out of the javascript event, which is more about the API (the DOM) than my domain (my app). Also using a number as an element id isn't ideal and could cause a conflict if I do it more than once in a view. So here's something that's just as clear and avoids any future confusion:
<button #click="()=>del(record.id)">×</button>
methods: {
del(id) {
fetch(`/api/item/${id}`, {method:"DELETE"})
}
}
You see, now my del function takes the record id instead of an event, simplifying things.
Note that if you do this wrong, you will invoke your delete function immediately, which is not what you want. Don't do this:~~
<button #click="del(record.id)">×</button>
If you end up doing that, Vue will call the del function every time this html fragment is rendered. Using the anonymous function ()=>del(record.id) will return a function that's ready to be executed when the click event happens.
Actually #nirazul proved this is fine. Not sure what my issue was.

Meteor.js autosubscribe after callback from server

I am trying to use meteor autosubscribe function on the client but sometimes it works and sometimes it doesn't. So here is the case:
Working version: I have dropdown which is populated with channels. When user clicks on the channel I set session variable and start loading threads:
Template.channelDropdown.events({
"click #channelLink": function() {
Session.set("currentChannel", this);
}
});
html
<ul class="dropdown-menu">
{{#each channels}}
<li>
<a id="channelLink" href="#">{{name}}</a>
</li>
{{/each}}
</ul>
and
Tracker.autorun(function() {
Meteor.subscribe("threadsByChannel", Session.get("currentChannel"));
});
Meteor.publish("threadsByChannel", function (channel) {
return threads.find({channel: channel});
});
and loading threads:
"channelThreads": function() {
return threads.find({channel: Session.get("currentChannel")}).fetch();
},
Now this works. However I have other method to open channel which doesn't work. It is possible to enter channel name and if it doesn't exist it is created, otherwise existing one is returned.
Template.channelSearchBar.events({
"submit #joinChannelForm": function() {
event.preventDefault();
var channelName = $("#channelNameField").val();
Meteor.call("getChannelByName", channelName, function(error, result) {
if (error) {
// TODO error handling
} else {
Session.set("currentChannel", result);
}
});
$("#channelNameField").val("");
}
});
server:
'getChannelByName': function (channelName) {
var channel = channels.findOne({name: channelName});
if (channel) {
return channel;
} else {
var newChannel = {
name: channelName
}
return channels.insert(newChannel);
}
}
html
<template name="channelSearchBar">
<form id="joinChannelForm" class="navbar-form navbar-left" role="search">
<div class="form-group">
<input id="channelNameField" type="text" class="form-control" placeholder="Enter channel name">
</div>
<button type="submit" class="btn btn-default">Join</button>
</form>
</template>
Now the only difference is that session variable is set in callback. I'm pretty sure this is the problem as it is asynchronious call to the server and somehow threads are not populated in client when requested. When I set breakpoint in loading threads function (threads.find() on client), I see that session variable is correctly set, but it just does not return anything. Also sometimes it is called two times (for example in working first case first call returns nothing and then second call returns real results for some reason. Is this is how it suppose to work?). I am just beginning to learn meteor and trying to understand how it all works. Would be glad if someone could explain or direct me to the right way.
EDIT: Its very strange. I have put breakpoint in publish function and it seems it works fine - exactly like it should. However on the not working case it simply returns nothing right from the server side even though both working and not working situations provides (seemingly) exactly the same channel object. It seems that the problem is related with mongodb query.
Why don't remove the Meteor.call, and do everything on the client side?, the subscription on the Autorun seems to be fine, lets try with this code, just make sure you have the allow/deny permissions in order.
Template.channelSearchBar.events({
"submit #joinChannelForm": function() {
event.preventDefault();
var channel = channels.findOne({name: channelName}),
channelName = $("#channelNameField").val();
if (channel) {
return channel;
} else {
var newChannel = {
name: channelName
}
var chanelCreated = channels.insert(newChannel);
Session.set("currentChannel", chanelCreated);
$("#channelNameField").val("");
}
}
});
OK it seems the real problem was not that of meteor publish/subscribe mistake but because of mongodb query which was not recognizing channel object. Problem was solved by changing this:
threads.find({channel: channel})
to this:
threads.find({"channel.name": channel.name})
I have found that mongo queries cares about order of object parameters, but channel had only one parameter (name) at the moment, so I'm still not sure why they were not considered equal. One channel was returned from findOne query and another from find. One from find was recognized.

grails controller/action/id automagically turning into controller/index

My problem is that the backend server (written in grails) is automatically converting my request URL to be a different URL. Specifically, it is changing it from /UXChallengeAwards/processSelectedNotifications to /UXChallengeAwards/index.
--
In a template gsp file, I have defined a button that makes a jQuery ajax call when clicked on:
<button class="blue-link"
onclick="jQuery.ajax({type:'POST',
data:jQuery(this).parents('.multiSelectForm').serialize(),
url: '/ici/UXChallengeAwards/processSelectedNotifications/${challenge.id}',
success:function(data,textStatus){},
error:function(xhr,textStatus,errorThrown){}
})" >
The method UXChallengeAwardsController.processSelectedNotifications exists. It performs some work and then redirects to another action in the controller. In fact, this used to work. But somehow in the process of adding a second button I made a change which seems to have broken things.
When the button is now clicked, the request URL gets switched to /ici/UXChallengeAwards/index and a 404 is returned because index does not exist as an action in this controller.
I've googled, and the most common answer for when this happens is that a controller must return some results for the view. But I've seen plenty of examples of redirects in controllers, and I do not see what I am doing wrong. (I did try variants of rendering results, but with no success.)
Here is what my controller action looks like:
def processSelectedNotifications = {
def challenge
def checkboxes = params.list('selectCheckbox');
for (checkbox in checkboxes) {
// the checkbox contains the id of a ChallangeAward that should be published
ChallengeAwards challengeAwards = ChallengeAwards.get(checkbox.toInteger())
if (challengeAwards) {
// grab a challenge for use in the redirect, they are all the same
challenge=challengeAwards.challenge
publish(challengeAwards)
}
}
if (challenge) {
redirect action: 'challengeAwardsRemote', id: challenge.id
return
}
// render a failure message if we got here
render messageNS(code:"UX.ChallengeAwards.Publish.failure")
}
I would really appreciate any insights into what might be wrong, or how to go about tackling this issue. I've checked my UrlMappings, and this is the rule that should handle this controller/method request:
"/$controller/$action?/$id?"{ constraints {} }
Thank you very much!
I'm going to go ahead and answer my own question, in case it is helpful for other newbies.
It turns out that I was not getting an automatic redirect. Rather, I had an error in the button setup code, so that grails was using its default link behavior. (Which is to go to the controller that matches the view, and if no action is specified, use the index method.)
The code above was originally created using a remoteSubmit tag, but I found that the generated code did not support handling multiple forms on a single page very well. So, I copied that generated code and then tweaked it to handle the multiple forms. However, I wanted the styling to match up with what was already in place on the page, so I switched it to be a button. That's when things went awry.
Eventually, I ended up specifying an onClick function for the button, and then writing the ajax submit code in javascript. Which turned out to be much simpler.
Here is what the button specification ended up looking like:
<button type="submit" id="notifications" class="blue-link" >
<i class="fa fa-envelope-o"></i>
<g:messageNS
code="UX.DiscussionBoard.ChallengeAward.Button.notify" />
</button>
And the associated JavaScript:
jQuery(document).ready(function() {
var clkBtn = "";
jQuery('button[type="submit"]').click(function(evt) {
clkBtn = evt.target.id;
});
jQuery('.multiSelectForm').submit(function() {
var url = '/ici/UXChallengeAwards/processSelectedNotifications';
if (clkBtn == 'deletes') {
url ='/ici/UXChallengeAwards/processSelectedDeletes';
}
var errorTarget = jQuery(this).parents().find('.recipientMessage').val();
var requestData = jQuery(this).parents('.multiSelectForm').serialize();
var options = {
data : requestData,
type : 'POST',
url : url,
target : '#awardsTab',
error : function(data) {
jQuery('#' + errorTarget).html(data.responseText).show();
},
success : function(data) {
console.log("in success");
}
};
jQuery(this).ajaxSubmit(options);
return false;
});

Resources